From 5d064a313c4ad87b5787d3a49841c5c83bd20896 Mon Sep 17 00:00:00 2001 From: Truptimayee Dash Date: Mon, 11 Aug 2025 13:15:18 +0530 Subject: [PATCH] added transcript report and gradesheet and some requirement changes --- __manifest__.py | 8 +- models/__init__.py | 2 + models/__pycache__/__init__.cpython-310.pyc | Bin 985 -> 1056 bytes models/__pycache__/enrollment.cpython-310.pyc | Bin 1923 -> 1923 bytes models/__pycache__/gradesheet.cpython-310.pyc | Bin 0 -> 6166 bytes .../gradesheet_line.cpython-310.pyc | Bin 0 -> 2739 bytes models/__pycache__/schedule.cpython-310.pyc | Bin 1735 -> 1735 bytes .../__pycache__/school_class.cpython-310.pyc | Bin 2123 -> 2123 bytes .../school_course_schedule.cpython-310.pyc | Bin 414 -> 158 bytes .../school_misc_menus.cpython-310.pyc | Bin 2956 -> 6978 bytes .../school_student.cpython-310.pyc | Bin 2234 -> 2234 bytes .../school_subject.cpython-310.pyc | Bin 2889 -> 2889 bytes ...chool_subject_teacher_info.cpython-310.pyc | Bin 1105 -> 1105 bytes .../teacher_attendance.cpython-310.pyc | Bin 1297 -> 1297 bytes models/gradesheet.py | 242 +++++++++++ models/gradesheet_line.py | 103 +++++ models/school_course_schedule.py | 6 +- models/school_misc_menus.py | 141 ++++++- models/school_student.py | 2 +- security/ir.model.access.csv | 6 +- views/gradesheet.xml | 394 ++++++++++++++++++ views/menu.xml | 10 +- views/school_reporting_views.xml | 102 ++++- 23 files changed, 985 insertions(+), 31 deletions(-) create mode 100644 models/__pycache__/gradesheet.cpython-310.pyc create mode 100644 models/__pycache__/gradesheet_line.cpython-310.pyc create mode 100644 models/gradesheet.py create mode 100644 models/gradesheet_line.py create mode 100644 views/gradesheet.xml diff --git a/__manifest__.py b/__manifest__.py index 26e1899..0871508 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'School_Management', 'version': '1.0', - 'depends': ['base', 'web', 'mail','product','hr'], + 'depends': ['base', 'web', 'mail', 'product', 'hr', 'hr_attendance'], 'license': 'LGPL-3', 'data': [ 'security/security.xml', @@ -23,8 +23,10 @@ 'views/school_reporting_views.xml', 'views/school_student_views.xml', 'views/fee_structure_views.xml', - 'views/fee_element_views.xml', - 'views/menu.xml', + 'views/fee_element_views.xml', + 'views/gradesheet.xml', + 'views/menu.xml', + ], 'installable': True, 'application': True, diff --git a/models/__init__.py b/models/__init__.py index f65f0e8..2ac231a 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -19,6 +19,8 @@ from . import school_student from . import fee_structure from . import fee_element from . import school_fee_structure_line +from . import gradesheet +from . import gradesheet_line diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc index eecf7aaeb22ac588306e74c2bd71b4c6dbf80d6e..3ac9522fd685ea28e124d17996a95ead3d213cd2 100644 GIT binary patch delta 139 zcmcb~zJP-_pO=@50SHv%r(}Gc$Scbzu~3^)mLWwfhap!iN{o>qg&{>8$QF+h2eKuC z88js~8!+x=RMBL-#g$%^n37tYk(ycpB~U;Ss6mrilj#;~N>O53Nz~;1EcT45oBy-iWE9{5$`v69-pMc63bhb?FpC$c Z<`#!dZhlH>PO2RvkXOtCBt#g*m;e?{AGH7g diff --git a/models/__pycache__/gradesheet.cpython-310.pyc b/models/__pycache__/gradesheet.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae59369997493ab6c483206cec528a80d73fc1ba GIT binary patch literal 6166 zcma)AOLH8@k?!|2=7k4AfDb)dlqg|DVg!P3nUeT2xLR85`s zk3@6~4mKI#lTHrr#dHL)d(vOnAL2t@eAvSVH+=NL!8K)nS=~JV2+FY-barOeJ1gtU ztd456vW7qBPrqFc-_f-HCSm`d1>riL_(Kq;F+I=*dP^S|En{G|Or3N_U=8e+J#bo% zuHDg?$*d^BTK`Ex($Ge7A^ z;SYs~gq-+U%!Oh*)N=oy1K~QJ_}_I+<628+TFYR1%Vb8&VrI)`7PEUs%i*OBmpQD& zTsHBI*_wb{W@T1k)o+YeMM*VQS5j3;lWYpLr%|^Cx!%**%zxDcuxw)9g4q@r~Y^$=jSno7d3K>u7TXx>@!Hdo!1hD%oRip@q?!%Uhg6i+T1o z>b&!f)tZO)7(310W$&Su**XsS1Y1C918q*K9^PkXlx((MgZw%>%N8Ni%->LQb1c89 zK>`|Jk0iT4?H3;TUrPOWv;qbRr|e8%H-&XaUHR+?sSPj!4P z88RLw-d;8DbG{jgBz+_1aSV4j>vaM@j@zL>;NYX7hW8wlmi2HiJ$0jlSq%CeFIRaByxD7>^_G{GFD!ywY>;cliRgzt=?ebk zurD~1);bS1)A~;yBz`~S%=^q2599wSe6eAO3GlMSc-J2WNm{uVC4P|A>SeXQxSrPU za?!yW{T}xgPB-L{b|)Ha4inzq92YdqysNxO+Hn#I-Y7MU=f;gQY4ye#Z{h0F;<=@z z(+wOK{YF~8;VoQQTD)?V6wi&DXVS_|RJy#hcv-2WelsoK^cF5HEndnMx6<-0Z{gz7 z;>BEXJ1yV#7A`C;UdRG+@xXFX)YwrSvlD66yF^|yyW_&I!-J*^3&XKfSNXsOhFQoG%T_~grBwZ||izHnt zq)Q}SE~LvOT`8n1Bwa0}tB{OyOR1X=zog`IW0~e-oLe5t%hFag^X(M`kZCRV-OT@_ zb4&SQTF=$-@WBS}ByhQmX_)BLX2S0#(!NbE$P+yta^WYOwI=QqEa|)b5a^eZex+c= zBAGmM3sEDhx{2^R58fYQ+>zQNc{l#)_?80$&e%mp&KTapfGjL9dis>Xd#{mV^@b*rGoGyA$ zbH}vf^z}okrm|35RY{K>E~e3kaR=dG{AYusKyeFC{BIy5?Y8!dGuwKijr0xuvA%6& z+zvtqGoEO>q%%jx$ULTPTX^ejb7X>ABMa0X8O(fWiT9y1P9nU`YN5_-lsI1+OrsFD zdSC)H83WH6o|t@Sq-`L6J~U9(SQW>iY3PJGG<9(Lqi;2F724+)`G@x-7De|5Tk4hV zc6;E5fRF)@F@Bg`Q zS>6jThxTvmKsPZ6?VmV7&O#XuNx##6Z*lVI1mYIfoaV4nQiEf2#{}?3aupXWLQOS@**pZ||+@ z=cpLZfjEX?%;+<^^_}%Mqh#oY;?mQHE%l0C4%mhc*EAk=xc(K6GSYUmT}{^#9d)MxhAiHcH zD?BnXNQ2qTvSZ27jk%rqUA6v=W2k@pDQ1Wq`UE@4UQ3RTjK@0G^!o2jl`(oxP4?DP z6L@hdJ+s1-&bpVMGf#zA&kvbb#1wB24AN_N1MbJ%!zm3sA5W3cKtoCQheaB6)>A%r zR`ryHwB|TX!(oqtfbd4pK@*=6A?K5&JT*gjx?GfyzlPnu7;u)JUX8r0HK4zM2Co|p zG2d<^vO&Fc0>i5?>ZRqg#gsg$$$FG0T);e}5ekW9B}1A_bB(4^-pik)t(LFylx$8} zA|mocq$>EYvX>yWpz(}u$4~tMU&FjD2$t(pbulqS=Ns?1AnU>MoGWh z@2E>r+PE{$n#X$%;|@C=B)Q#T5Ny5Wttbnp<+071*s83?3i8|1096q}q?K%Katb~D z&ciKiqK^qn6A7vW=n8)jH%`bh596WW02GcpS`Y||Dr);PZy;#Qf`Ky(k$pUrCJ(uXxuoX7qf;V>HxK&a9(WW#hrnMEW-KrJ)o;el; z;Jqj#Ph@-3?$YTyxQH%l#`i#6W7cr>vRO8(dKo`Mcm8UXPU#kD4a=|)lIWRIrBs7{ zj$Z!r)Rbi!2wV8QAV97g3Y1?sFVZuIK>7P;(xF&i{JCICcoa1M7Z?gy0Rf}2qZ8cj z8ry(cz@j;_kjL1w+O|Eine)&VCs}FRVeWP*JO|ALH1Du-u33Pl0?i_;=9)L5sX=pz z)pN}pG?UPL!lrUf4Vr0a{)x@x8jBqPH@jV;@kjQ^8I?x3pg%Ok7r0u_KXni^=dyR> zSo)i?m;c@2N#WKx`gKJ#@U{RT9)Hw_FNeLX-~;~1A9(RrobbW8`Mvdi?BU<{aI`vn zJqj2X-lFF-e>3s?4rLzHLlR|O!rDVzDIU_1xKsT-hgeNwLt`d(X$qD6cbfu z_v|ill$tzSA?^rqR8Pzjr(ohJaSNoQ;G991LM`h~6tQ%Am2(z*pAI%f^p#5c8qP~l zB*^{tAP7l=BM=j!0nQNK{V-{`S)T9}Rs1DL%Pp`~{E&2nX#2W3=>8%=>3`yhDgSZn zRrqmLcTMtUSNZh!QSy2NzMcKx?avLzd7i#~$g2;7!+}JDa!~Sa<<0m$HBK}TV09$Z zrl=(bL)iQke0!v|b!H)4+Ub08kMfdrzArQ?Y)((-@v%VSmntDjia2SCC5(3u@W}BN z|3XB8-j9hpXsA7$$ggkNRYiUF_yek|{GXCkN>MHSADr&P?GM?5CZX&>zEdP#WRD#q zORjcIm82p~&C`p_CdWVLF%FReLNzmTc6CKncg+<+E_|%Ou^4i#9eaeU z9Gt*R=kMZk&VdDs?9}odx*(?mYOuZB5is_s+GA!`hKjgQ~T~>&8*LMUa)6f zoL1#H%8<%&*0Bx=ck0So-Y8rDD~&s!+h&v2u+N?Tm&{8b@8YiG>N7^V@O@@|x#?=- ztaRJzLt48n%k4IBZ5VLy)pq+!z#YYFNVcj1B|hABM1PaA6X~>pkX)dHez?vNhO;Pa z-CFmB$_=Ew5=4IT5=6;~H6Gy02)YnoVAcw)q;-!!N`y+AR6^-~=7(F$NHwzDLZwo4 z;H3MLkT2unyQNYM5mOKMiTr{}OP_`b?;-4eLaOUTZV(}~67=PoKFv|05v%K>bc=h9 z_>5|NO@ts$JSOsl$ghd)9d5;``zFOy>M{;NT+M=OTd@C>X<6jr&UXjj(7Y(ymh1e* zDyt*#on<;Nsy2>8ZL3pT)pDl5cluiDq!_`K`!lrU^IB9=I-`Z94SFH(O3^EV+m|!-hCbQaxDk@LCpb1{mjKcezN8Bg%{ws&zE|#Kn_gEzt)|C#03}t3yO#by13NE7UCidLK$%d`%4lJ!EhH3 zO8pTSg|XL~QutbwECDDjP({LK+IfL_#A|BgR;Ox&T*l-SY zbms(d@DY5xslj=ek=!_PC*cCluR5pL#E+A35vo6FP=iaal$WYAh1_(Pn}y4gJB{3# zE;k2t$(@By(A*6%w(4(1QSg9!z~U{&k}sPfOUGl$);D`}Js^H$HR%8g6I<%rq$^5s z#<`)_V(IXIMmpBPI!ZP11is1;3W*Q8FFzFaaysvcO1o|AaKJd zEk4GEz0zr|GoF^aTZ%jD+ssdJy{u7FL7U#%w|8VT%&9^Z-o zz|G5R|3mV?HoQ)dq{Syu;su%3W~TG?-L(9OaUbXOHkn7stYQgp$yP*9?TQOwwFUyrdIz8x=+VF>8^XL^+tontz7DE=0_afD0~)G-rZui)*^_awbpa_%3=n08=~VVCf2gU zZ0#VOTuP^U`#B%O39jQ4)7e_kEOksRXvW`Zb@iCMtRLh5L*^h;^vv-K0%;Df&m7KS zbG*>@&=@;8J`Dz$1ac_#U1CjSFGBtZTR`>-3>;xCWIu=E5w?Wv7ch8)EhD=Mm3|h6 zBpODEYr_b>jUya6YBX~czknb1)-bGT@ zLvrm9y4F++c>kX?HSCvq#_#F*z#5kTlaO?GoRtfCsWs! zJ$1AnB~@l>D%D*2gR-d4h$YwGV*XBMK@a<4{N2Fhz|>Gn_-Owa@6&U~Tc&U1O3fAp zSC%E$6_)E_7Ip%L?UL*6cD#V<1frZ&K@uyV&zo)fxe%s{3C=jDY!ZcsUikbR%6+BEMQk0^6JacEd7S5Id@-vnvZSC+RzgUB@StuG%q<6zm-Ly4LQD*-VtMpw zSNt=?e4a?-344a+h-U{Tr++Vj)uhcrh~=R|bv27bew8{cKMJwpVl|36{h;+lt~5V| zFE>%pRPj?31;tS59@2&|bm=auhOViaYJMK<{ literal 0 HcmV?d00001 diff --git a/models/__pycache__/schedule.cpython-310.pyc b/models/__pycache__/schedule.cpython-310.pyc index 26e5b4c8423862b8a38aff958356eb09e606bc0e..96c10f031a3153e97df8cea68013dc8db686ddad 100644 GIT binary patch delta 59 zcmX@kdz_a$pO=@50SG3BOxegS$ijGQvnta9V&npO=@50SNfsOx?)szySa?z69t1 delta 20 acmX>ta9V&npO=@50SIDkEbzySa_E(J0G diff --git a/models/__pycache__/school_course_schedule.cpython-310.pyc b/models/__pycache__/school_course_schedule.cpython-310.pyc index 524770f1c4aeb1c7b67ef005d6e497888acda7e2..0c2f4283ad90668f8cb09a163abfec530850554b 100644 GIT binary patch delta 76 zcmbQoJdaT@pO=@50SKPHoSHEcNI#yKCn?DSWTY?zGiWmUtz;--1oEK7#P4pRw>WHa S^HWN5Qtdzz#Y{kgg#iGzI1teQ literal 414 zcmYjN!Ab)`49(1J%UUe-Abx_dmwteVR6KaHm)@3zVcA4?WoAcrTcKX{2gI-Nm*ymR z68r*B&CC{TAjx}4NFH(1=>*t*zuxN)&fkF?TEb+(-k)(mKw-d&Tv(Y)0XKjW)f=E9 z@t|H+o?xeAke{z6#|-6HFHD{fq-aE)a5wB3CGRjq)d}s&>v? zyG_>Yzx9i%(a}sY* z>XvNlp2b||J+_#~?S21|+bx4rVm>&&;RN86SpZI8I2CXztO`!maH`NO_z*^w645z^sH?8*4|6-k1+m-$xWjx8GzZ3I>WzvmCaod&)A4f^dqC8HA z9}1C*J`JD$`5e|feI?V36%;|c(wC~dvS?NosTLu*{-%u4^It*C(iQTQD zRbv5Kb&P1x=FD~)U%K5UcniDO$RBLl0N!5w((AU^5*B|();IfmX_{=LdEDp4nM_fg zp&~omX(U*2Mh^+~nD7H)%FWB-PCp%tqTzw`^~8M}8=w5BP2~>C9|Kemt$l0X-n5?D8&4hS^5JK)Y$m|R zy^JS2Pc5;Gkw-WAyZ2L;ruPR2>Y+E&8$`osmk;Y^l(?(HOLJKZ45#|+=h zy1}Mt5jW>ie~$}rw@j<3ejI1SucSy*yQ7n5)jx4oJW0P-iQs0?V^KyXiFT zD*l#He6RXvHRi;@m|B82@Z!mXafpKfmDh)Y4oa|JQOBgpe5iI=E^68RgpX2@-yIJI z2gQosK}RSaH+Hux%^BY_x0QWvokFr0Nn? zAyqJGmcm?IK}&ih8gZ4Hr95TPLAObW_jV^U&Y?v*EEHtd(K>^Yk*U;>Rh<=E5dQ>B zB;Z>p*=wl24klO&4}bwAz~jr7hG(7gOmDVWGU)7pWgseQ)F0{rVijMI{_R0J9_AbE z>fZr?=+G;r^(2jQ=|}3#Q@2bqCWSw?<)$r&e`3p|QuNFI2d3iWmX*nLl$nG_AkBdJ z^I`tV-~}*=FrE8VWhOOU1MQQ^L_e8KZAP0gmzpLM>cey{8hSy|OYDrHTc8)&GCOPN zCDp&e&KdfQqR+DnhF%68uou{ihCU0r%3fkGvy1E!JPet8ue+k&3fW~d>Ky0>yTZN! z`YKj=UiH7izG?a|DEd|QnxS7%beml>^oxpqom~fA!uu~NdX;qy{W9o_>|5*&_9i%2 zv4Tr##2WjyqTOx?`Z9Zq-C%FS9bEzS4fYQE&X;!gDu3nho9w$!Z1yhu-ec>L-F;PY zZi4e3dtY&0Q=IREbBoGudcl#z6)BGH+h!996oil@g}={G&xYwXq3c#6$4

d;Zh!d(`rgtL7;7l`FzO5T@-m}ip9t?JG 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 diff --git a/models/__pycache__/school_student.cpython-310.pyc b/models/__pycache__/school_student.cpython-310.pyc index c8c2892f4c8f5cd98fe43e5cb670b2c6b4bc5645..96a11488b4dfbf1be8b4d5a939741b640213561c 100644 GIT binary patch delta 42 wcmdlbxJ!^bpO=@50SLNlXKv(9W@X9AEH2qx#LC9R7&W<)y^`_Ppc2bNxpO=@50SM&ZOx?(B#|;2Bzy$&T delta 20 acmX>pc2bNxpO=@50SInfp16_QjvD|wB?X=U diff --git a/models/__pycache__/school_subject_teacher_info.cpython-310.pyc b/models/__pycache__/school_subject_teacher_info.cpython-310.pyc index 38749b9f3d3fe6e2de9b7be37ff449fe76ea84dc..9dd99bdb8ce09483692c0c251927657841a72df2 100644 GIT binary patch delta 20 acmcb}agl>NpO=@50SJ`dOx?)s#sUB~76keL delta 20 acmcb}agl>NpO=@50SF2&Ox(!r#sUC0g9Rx7 diff --git a/models/__pycache__/teacher_attendance.cpython-310.pyc b/models/__pycache__/teacher_attendance.cpython-310.pyc index 7f5b1fa87739d131afc2c2b543b992f281a9236b..0950e540eaf967f5e28f758c252d8203a492aaeb 100644 GIT binary patch delta 156 zcmbQpHIa)upO=@50SIDRXKv&^z{q%O@)^d1oWeknUyQazLX)R41#p9SMWP@=X!1uU zB}S>qBFz7Vz@nT$(Oaw~`6-E&KuJ{=Cs7%YIw@2_dU6d*rVhFwNGpd;ZhlH>PO2Rv PkXOtGBt#f^7{!*E(#m0zo1apelWNBZ P 0 else 0.0 + + @api.onchange('class_name') + def _onchange_class_name(self): + if self.class_name: + return { + 'domain': { + 'course_id': [('class_id.name', '=', f'Class {self.class_name}')], + 'subject_id': [('class_name', '=', f'Class {self.class_name}')], #Add this + } + } + return { + 'domain': { + 'course_id': [], + 'subject_id': [], + } + } + + def action_fetch_student_marks(self): + """Fetch student marks and generate gradesheet""" + self.ensure_one() + + if not all([self.session, self.class_name, self.course_id]): + raise UserError("Please fill all required fields: Session, Class, and Course.") + + # Clear existing lines + self.student_grade_ids.unlink() + + # Get enrolled students for the course + enrollments = self.env['school.enrollment'].search([ + ('course_id', '=', self.course_id.id), + ('class_name', '=', self.class_name), + ('session', '=', self.session), + ('status', '=', 'confirmed') + ]) + + if not enrollments: + raise UserError(f"No enrolled students found for Class {self.class_name}, Course {self.course_id.name}, Session {self.session}") + + grade_lines = [] + for enrollment in enrollments: + # Get student marks (you may need to adapt this based on your marks/exam model) + student_marks = self._get_student_marks(enrollment) + + grade_lines.append({ + 'gradesheet_id': self.id, + 'student_id': enrollment.student_id.id, + 'enrollment_id': enrollment.id, + 'student_name': enrollment.student_id.name, + 'roll_number': enrollment.application_id.roll_no if enrollment.application_id else '', + 'obtained_marks': student_marks.get('obtained_marks', 0), + 'total_marks': student_marks.get('total_marks', 100), + 'percentage': student_marks.get('percentage', 0), + 'final_grade': student_marks.get('grade', 'F'), + 'status': student_marks.get('status', 'fail'), + 'remarks': student_marks.get('remarks', ''), + 'subject_id': self.subject_id.id, + }) + + # Create grade lines + self.env['school.gradesheet.line'].create(grade_lines) + + # Update gradesheet status + self.write({ + 'state': 'generated', + 'generated_date': fields.Datetime.now(), + 'generated_by': self.env.user.id, + }) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'message': f'Gradesheet generated successfully! Found {len(grade_lines)} students.', + 'type': 'success', + 'sticky': False, + } + } + + def _get_student_marks(self, enrollment): + """ + Get student marks from your existing marks/exam system + This is a placeholder - adapt according to your actual marks model + """ + # Example: If you have a student.marks model or exam.result model + # marks_record = self.env['student.marks'].search([ + # ('student_id', '=', enrollment.student_id.id), + # ('course_id', '=', self.course_id.id), + # ('session', '=', self.session) + # ], limit=1) + + # For demo purposes, generating sample marks + # Replace this with actual marks fetching logic + import random + obtained = random.randint(30, 95) + total = 100 + percentage = (obtained / total) * 100 + + # Grade calculation + if percentage >= 90: + grade = 'A+' + status = 'pass' + elif percentage >= 80: + grade = 'A' + status = 'pass' + elif percentage >= 70: + grade = 'B+' + status = 'pass' + elif percentage >= 60: + grade = 'B' + status = 'pass' + elif percentage >= 50: + grade = 'C' + status = 'pass' + elif percentage >= 40: + grade = 'D' + status = 'pass' + else: + grade = 'F' + status = 'fail' + + return { + 'obtained_marks': obtained, + 'total_marks': total, + 'percentage': percentage, + 'grade': grade, + 'status': status, + 'remarks': 'Good' if status == 'pass' else 'Needs Improvement' + } + + def action_finalize_gradesheet(self): + """Finalize the gradesheet""" + self.ensure_one() + if self.state != 'generated': + raise UserError("Please generate the gradesheet first.") + + self.state = 'finalized' + return True + + def action_reset_to_draft(self): + """Reset gradesheet to draft""" + self.ensure_one() + self.student_grade_ids.unlink() + self.write({ + 'state': 'draft', + 'generated_date': False, + 'generated_by': False, + }) + return True + + def action_print_gradesheet(self): + """Print gradesheet report""" + self.ensure_one() + if not self.student_grade_ids: + raise UserError("No student data found. Please fetch student marks first.") + + return self.env.ref('school_management.report_school_gradesheet').report_action(self) + + diff --git a/models/gradesheet_line.py b/models/gradesheet_line.py new file mode 100644 index 0000000..10eb472 --- /dev/null +++ b/models/gradesheet_line.py @@ -0,0 +1,103 @@ +from odoo import models, fields, api +from odoo.exceptions import ValidationError + +class SchoolGradesheetLine(models.Model): + _name = 'school.gradesheet.line' + _description = 'Student Grade Line' + _order = 'student_name' + + gradesheet_id = fields.Many2one('school.gradesheet', string="Gradesheet", ondelete="cascade") + application_id = fields.Many2one('school.application', string="Application", required=True) + student_id = fields.Many2one('school.application', string="Student", required=True) + student_name = fields.Char(related='student_id.name', string="Student Name", store=True) + roll_number = fields.Char(related='student_id.roll_no', string="Roll Number", store=True) + subject_id = fields.Many2one('school.subject', string="Subject") + + + enrollment_id = fields.Many2one( + 'school.enrollment', + string="Enrollment" + ) + + obtained_marks = fields.Float( + string="Obtained Marks", + default=0.0 + ) + total_marks = fields.Float( + string="Total Marks", + default=100.0 + ) + percentage = fields.Float( + string="Percentage (%)", + compute="_compute_percentage", + store=True + ) + + # Grade Info + final_grade = fields.Selection([ + ('A+', 'A+ (90-100%)'), + ('A', 'A (80-89%)'), + ('B+', 'B+ (70-79%)'), + ('B', 'B (60-69%)'), + ('C', 'C (50-59%)'), + ('D', 'D (40-49%)'), + ('F', 'F (Below 40%)') + ], string="Grade", default='F') + + status = fields.Selection([ + ('pass', 'Pass'), + ('fail', 'Fail') + ], string="Status", default='fail') + + remarks = fields.Text(string="Remarks") + + # Compute percentage + @api.depends('obtained_marks', 'total_marks') + def _compute_percentage(self): + for record in self: + if record.total_marks > 0: + record.percentage = (record.obtained_marks / record.total_marks) * 100 + else: + record.percentage = 0.0 + + # Auto-update grade and status on marks change + @api.onchange('obtained_marks', 'total_marks') + def _onchange_marks(self): + for record in self: + if record.total_marks > 0: + percentage = (record.obtained_marks / record.total_marks) * 100 + + if percentage >= 90: + record.final_grade = 'A+' + record.status = 'pass' + elif percentage >= 80: + record.final_grade = 'A' + record.status = 'pass' + elif percentage >= 70: + record.final_grade = 'B+' + record.status = 'pass' + elif percentage >= 60: + record.final_grade = 'B' + record.status = 'pass' + elif percentage >= 50: + record.final_grade = 'C' + record.status = 'pass' + elif percentage >= 40: + record.final_grade = 'D' + record.status = 'pass' + else: + record.final_grade = 'F' + record.status = 'fail' + else: + record.percentage = 0.0 + record.final_grade = 'F' + record.status = 'fail' + + # Validation to prevent invalid marks + @api.constrains('obtained_marks', 'total_marks') + def _check_marks(self): + for record in self: + if record.obtained_marks < 0 or record.total_marks < 0: + raise ValidationError("Marks cannot be negative.") + if record.obtained_marks > record.total_marks: + raise ValidationError("Obtained marks cannot exceed total marks.") diff --git a/models/school_course_schedule.py b/models/school_course_schedule.py index 2f3154e..e8fa7e3 100644 --- a/models/school_course_schedule.py +++ b/models/school_course_schedule.py @@ -1,6 +1,6 @@ -from odoo import models, fields +# from odoo import models, fields -class SchoolCourseSchedule(models.Model): - _inherit = 'hr.leave' # not 'school.course.schedule' +# class SchoolCourseSchedule(models.Model): +# _inherit = 'hr.leave' # not 'school.course.schedule' \ No newline at end of file diff --git a/models/school_misc_menus.py b/models/school_misc_menus.py index bbcce01..4dfde6c 100644 --- a/models/school_misc_menus.py +++ b/models/school_misc_menus.py @@ -1,4 +1,5 @@ -from odoo import models, fields +from odoo import models, fields, api +from odoo.exceptions import ValidationError class SchoolNotice(models.Model): _name = 'school.notice.board' @@ -22,8 +23,6 @@ class SchoolReportDummy(models.Model): name = fields.Char("Report Name") date_generated = fields.Date("Generated On", default=fields.Date.today) - - class SchoolFeesReport(models.Model): _name = 'school.fees.report' _description = 'Fees Report' @@ -31,12 +30,140 @@ class SchoolFeesReport(models.Model): name = fields.Char("Report Name") amount = fields.Float("Amount") -class SchoolTranscriptReport(models.Model): +class TranscriptReport(models.Model): _name = 'school.transcript.report' _description = 'Transcript Report' - student_id = fields.Many2one('school.application', string="Student", ondelete='set null') - grade = fields.Char("Grade") + name = fields.Char(string="Report Name", default="Transcript Report", readonly=True) + session = fields.Selection([ + ('2023-24', '2023-24'), + ('2024-25', '2024-25'), + ('2025-26', '2025-26'), + ('2026-27', '2026-27'), + ], string="Session", required=True) + + student_id = fields.Many2one('school.application', string="Student", required=True) + + # Student Information Fields (computed from student_id) + student_name = fields.Char(related='student_id.name', string="Student Name", readonly=True) + student_address = fields.Text(related='student_id.address', string="Address", readonly=True) + student_phone = fields.Char(related='student_id.phone_no', string="Phone", readonly=True) + student_email = fields.Char(related='student_id.email', string="Email", readonly=True) + student_dob = fields.Date(related='student_id.date_of_birth', string="Date of Birth", readonly=True) + student_guardian = fields.Char(related='student_id.father_name', string="Guardian", readonly=True) + student_academic_year = fields.Char(related='student_id.academic_year', string="Academic Year", readonly=True) + student_class = fields.Selection(related='student_id.class_name', string="Class", readonly=True) + + # School Information Fields + school_id = fields.Many2one('res.company', string="School", default=lambda self: self.env.company, readonly=True) + school_name = fields.Char(related='school_id.name', string="School Name", readonly=True) + school_address = fields.Text(string="School Address", compute='_compute_school_address', readonly=True) + school_state = fields.Char(related='school_id.state_id.name', string="State", readonly=True) + school_phone = fields.Char(related='school_id.phone', string="School Phone", readonly=True) + school_email = fields.Char(related='school_id.email', string="School Email", readonly=True) + school_academic_year = fields.Char(string="School Academic Year", default="2024-25", readonly=True) + school_classes = fields.Char(string="Classes", default="Class 1-12", readonly=True) + + # Subject Information + subject_ids = fields.Many2many('school.subject', string="Subjects", compute='_compute_subjects', readonly=True) + subject_names = fields.Char(string="Subject Names", compute='_compute_subject_names', readonly=True) + + # Report Status + report_generated = fields.Boolean(string="Report Generated", default=False) + generated_date = fields.Datetime(string="Generated Date") + + @api.depends('school_id') + def _compute_school_address(self): + for record in self: + if record.school_id: + address_parts = [] + if record.school_id.street: + address_parts.append(record.school_id.street) + if record.school_id.street2: + address_parts.append(record.school_id.street2) + if record.school_id.city: + address_parts.append(record.school_id.city) + if record.school_id.zip: + address_parts.append(record.school_id.zip) + record.school_address = ', '.join(address_parts) + else: + record.school_address = '' + + @api.depends('student_id') + def _compute_subjects(self): + for record in self: + if record.student_id and record.student_id.class_name: + # Get subjects for the student's class + subjects = self.env['school.subject'].search([ + ('class_name', '=', 'Class ' + record.student_id.class_name) + ]) + record.subject_ids = subjects + else: + record.subject_ids = False + + @api.depends('subject_ids') + def _compute_subject_names(self): + for record in self: + if record.subject_ids: + subject_names = [] + for subject in record.subject_ids: + # Get the display name for the subject + subject_display = dict(subject._fields['name'].selection).get(subject.name, subject.name) + subject_names.append(subject_display) + record.subject_names = ', '.join(subject_names) + else: + record.subject_names = '' + + def action_fetch_report(self): + """Fetch and prepare the report data""" + self.ensure_one() + if not self.student_id: + raise ValidationError("Please select a student first.") + if not self.session: + raise ValidationError("Please select a session first.") + + # Mark report as generated + self.report_generated = True + self.generated_date = fields.Datetime.now() + + # You can add more logic here to fetch additional data + # For example: get grades, attendance, etc. + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'success', + 'message': 'Report data fetched successfully!', + 'next': {'type': 'ir.actions.act_window_close'}, + } + } + + def action_download_report(self): + """Download the transcript report""" + self.ensure_one() + if not self.report_generated: + raise ValidationError("Please fetch the report first before downloading.") + + # Here you would typically call a report action + # For now, we'll show a notification + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'type': 'success', + 'message': 'Report download functionality will be implemented with PDF generation.', + 'sticky': False, + } + } + + @api.model + def create(self, vals): + # Auto-generate report name with session and student info + if 'student_id' in vals and 'session' in vals: + student = self.env['school.application'].browse(vals['student_id']) + vals['name'] = f"Transcript Report - {student.name} - {vals['session']}" + return super().create(vals) class SchoolScholarshipReport(models.Model): _name = 'school.scholarship.report' @@ -59,4 +186,4 @@ class SchoolConfigSettings(models.Model): _description = 'School Configuration Settings' key = fields.Char("Setting Key", required=True) - value = fields.Char("Setting Value") + value = fields.Char("Setting Value") \ No newline at end of file diff --git a/models/school_student.py b/models/school_student.py index 4785084..0d63c11 100644 --- a/models/school_student.py +++ b/models/school_student.py @@ -34,7 +34,7 @@ class SchoolStudent(models.Model): 'type': 'ir.actions.act_window', 'name': 'Fee Slip', 'res_model': 'school.enrollment.fee.summary', - 'view_mode': 'tree,form', + 'view_mode': 'list,form', 'domain': [('student_ref', '=', self.id)], 'target': 'current', } diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 4e2cb33..dfbd2e1 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -4,7 +4,7 @@ access_school_application,School Application,model_school_application,,1,1,1,1 access_school_enrollment,School Enrollment,model_school_enrollment,,1,1,1,1 access_school_enrollment_subject,School Enrollment Subject,model_school_enrollment_subject,,1,1,1,1 access_school_enrollment_fee_summary,School Enrollment Fee Summary,model_school_enrollment_fee_summary,,1,1,1,1 -access_school_class_schedule,School Class Schedule,model_school_class_schedule,,1,1,1,1 +access_school_class_schedule,School Class Schedule,model_school_class_schedule,base.group_user,1,1,1,1 access_school_class,School Class,model_school_class,,1,1,1,1 access_school_subject,School Subject,model_school_subject,,1,1,1,1 access_school_course,School Course,model_school_course,,1,1,1,1 @@ -23,5 +23,7 @@ access_school_report_card,Report Card,model_school_report_card,,1,1,1,1 access_school_student,Student,model_school_student,,1,1,1,1 access_school_fee_structure,School Fee Structure,model_school_fee_structure,,1,1,1,1 access_school_fee_element,School Fee Element,model_school_fee_element,,1,1,1,1 -access_school_fee_component,School Fee Component,model_school_fee_component,,1,0,0,0 +access_school_fee_component,School Fee Component,model_school_fee_component,,1,1,1,1 +access_school_gradesheet,School Gradesheet,model_school_gradesheet,,1,1,1,1 +access_school_gradesheet_line,School Gradesheet Line,model_school_gradesheet_line,,1,1,1,1 diff --git a/views/gradesheet.xml b/views/gradesheet.xml new file mode 100644 index 0000000..3231412 --- /dev/null +++ b/views/gradesheet.xml @@ -0,0 +1,394 @@ + + + + + + school.gradesheet.list + school.gradesheet + + + + + + + + + + + + + + + + + + school.gradesheet.form + school.gradesheet + +

+
+
+ + + + + {"readonly": [["state", "!=", "draft"]]} + + + {"readonly": [["state", "!=", "draft"]]} + + + {"readonly": [["state", "!=", "draft"]]} + + + + + {"invisible": [["state", "==", "draft"]]} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + school.gradesheet.search + school.gradesheet + + + + + + + + + + + + + + + + + + + + + + + + + + + school.gradesheet.line.list + school.gradesheet.line + + + + + + + + + + + + + + + + + school.gradesheet.kanban + school.gradesheet + + + + + + + + + + + + + +
+
+
+
+ + + + +
+
+
+
+ Class +
+
+
+
+ +
+
+
+
+ Students: +
+
+ Passed: +
+
+
+
+
+
+ % +
+
+
+
+
+
+
+
+
+
+
+ + + + school.gradesheet.pivot + school.gradesheet + + + + + + + + + + + + + + + school.gradesheet.graph + school.gradesheet + + + + + + + + + + + + Gradesheets + school.gradesheet + kanban,list,form,pivot,graph + {'search_default_filter_current_session': 1} + +

+ Create your first Gradesheet! +

+

+ Generate gradesheets for students by selecting session, class, and course. + Fetch student marks automatically from your existing data. +

+
+
+ + + + Student Grades + school.gradesheet.line + list + + + + + Gradesheet Report + school.gradesheet + qweb-pdf + school_management.report_gradesheet_template + school_management.report_gradesheet_template + + report + + + + + + + \ No newline at end of file diff --git a/views/menu.xml b/views/menu.xml index 4ccad7b..4630ad3 100644 --- a/views/menu.xml +++ b/views/menu.xml @@ -103,7 +103,7 @@ @@ -119,7 +119,13 @@ parent="menu_school_reporting" action="action_school_report_card" sequence="5"/> - + + + @@ -11,6 +12,7 @@ + school.fees.report.form school.fees.report @@ -26,48 +28,123 @@ - Fees Report school.fees.report list,form - school.transcript.report.list school.transcript.report - + + - + + + + + school.transcript.report.form school.transcript.report
+
+
- - +

Transcript Report Generator

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - + Transcript Report school.transcript.report list,form + +

+ Create a transcript report +

+

+ Select a session and student to generate transcript reports. +

+
- school.scholarship.report.list @@ -80,6 +157,7 @@ + school.scholarship.report.form school.scholarship.report @@ -96,14 +174,12 @@ - Scholarship Report school.scholarship.report list,form - school.report.card.list @@ -116,6 +192,7 @@ + school.report.card.form school.report.card @@ -132,11 +209,10 @@ - Report Card school.report.card list,form -
+ \ No newline at end of file