diff --git a/common/urls.py b/common/urls.py index b82e3b6..2119e83 100644 --- a/common/urls.py +++ b/common/urls.py @@ -42,19 +42,22 @@ urlpatterns = [ name='candidate-validation'), path('candidate//summary/', candidats_views.inscription_summary, name='candidate-summary'), + path('student//examination/mentor/', views.PrintCompensationForm.as_view(), {'typ': 'mentor'}, + name='print-mentor-compens-form'), + path('exam//indemn//', views.PrintCompensationForm.as_view(), + name='print-compens-form'), + # Qualification EDE path('student_ede//send_convocation/', views.StudentConvocationExaminationView.as_view(), name='student-ede-convocation'), path('student_ede//examination/expert/', views.PrintExpertEDECompensationForm.as_view(), - name='print-expert-compens-ede'), - path('student_ede//examination/mentor/', views.PrintMentorEDECompensationForm.as_view(), - name='print-mentor-compens-ede'), + name='print-expert-letter-ede'), # Qualification EDS path('student_eds//send_convocation/', views.StudentConvocationEDSView.as_view(), name='student-eds-convocation'), path('student_eds//examination/expert/', views.PrintExpertEDSCompensationForm.as_view(), - name='print-expert-compens-eds'), + name='print-expert-letter-eds'), path('student/export_qualif//', views.export.export_qualification, name='export-qualif'), diff --git a/stages/admin.py b/stages/admin.py index f728754..b5457b5 100644 --- a/stages/admin.py +++ b/stages/admin.py @@ -130,10 +130,12 @@ class ExaminationInline(admin.StackedInline): return format_html( 'Courrier pour l’expert ' 'Mail convocation soutenance ' - 'Indemnité au mentor', - reverse('print-expert-compens-ede', args=[obj.pk]), + 'Indemnité EP ' + 'Indemnité soutenance', + reverse('print-expert-letter-ede', args=[obj.pk]), reverse('student-ede-convocation', args=[obj.pk]), - reverse('print-mentor-compens-ede', args=[obj.student.pk]), + reverse('print-compens-form', args=[obj.pk, 'ep']), + reverse('print-compens-form', args=[obj.pk, 'sout']), ) elif obj and obj.student.is_eds_3(): if obj.missing_examination_data(): @@ -142,10 +144,12 @@ class ExaminationInline(admin.StackedInline): return format_html( 'Courrier pour l’expert ' 'Mail convocation soutenance ' - 'Indemnité au mentor', - reverse('print-expert-compens-eds', args=[obj.pk]), + 'Indemnité EP ' + 'Indemnité soutenance', + reverse('print-expert-letter-eds', args=[obj.pk]), reverse('student-eds-convocation', args=[obj.pk]), - reverse('print-mentor-compens-ede', args=[obj.student.pk]), + reverse('print-compens-form', args=[obj.pk, 'ep']), + reverse('print-compens-form', args=[obj.pk, 'sout']), ) else: return missing_message @@ -158,7 +162,7 @@ class StudentAdmin(admin.ModelAdmin): list_filter = (('archived', ArchivedListFilter), ('klass', KlassRelatedListFilter)) search_fields = ('last_name', 'first_name', 'pcode', 'city', 'klass__name') autocomplete_fields = ('corporation', 'instructor', 'supervisor', 'mentor') - readonly_fields = ('report_sem1_sent', 'report_sem2_sent') + readonly_fields = ('report_sem1_sent', 'report_sem2_sent', 'mentor_indemn') fieldsets = [ (None, { 'fields': ( @@ -176,13 +180,23 @@ class StudentAdmin(admin.ModelAdmin): 'fields': ( ('supervisor', 'supervision_attest_received'), ('subject', 'title'), - ('training_referent', 'referent', 'mentor'), + ('training_referent', 'referent'), + ('mentor', 'mentor_indemn'), ) }), ] actions = ['archive'] inlines = [ExaminationInline, SupervisionBillInline] + def mentor_indemn(self, obj): + if obj is None or not obj.mentor: + return '-' + return format_html( + 'Indemnité au mentor', + reverse('print-mentor-compens-form', args=[obj.pk]), + ) + mentor_indemn.short_description = 'Indemnité' + def get_inlines(self, request, obj=None): if obj is None: return [] diff --git a/stages/pdf.py b/stages/pdf.py index 6cb9242..9f3b697 100644 --- a/stages/pdf.py +++ b/stages/pdf.py @@ -286,6 +286,7 @@ class UpdateDataFormPDF(EpcBaseDocTemplate): class CompensationForm: """Mixin class to host paiement formdata.""" + AMOUNT = '' EXPERT_MANDAT = 'EXPERT' MENTOR_MANDAT = 'MENTOR' EXPERT_ACCOUNT = MENTOR_ACCOUNT = "3'130'0003" @@ -336,12 +337,12 @@ class CompensationForm: self.story.append(Spacer(0, 0.5 * cm)) def add_accounting_stamp(self, student, mandat=None): - account = otp = total = '' + account = otp = '' + total = self.AMOUNT if mandat == self.EXPERT_MANDAT: account = self.EXPERT_ACCOUNT elif mandat == self.MENTOR_MANDAT: account = self.MENTOR_ACCOUNT - total = '500.-' if student.klass.is_Ede_pe(): otp = self.OTP_EDE_PE_OTP @@ -535,32 +536,59 @@ class ExpertEdsLetterPdf(ExpertEdeLetterPdf): """ -class MentorCompensationPdfForm(CompensationForm, EpcBaseDocTemplate): - def __init__(self, out, student): - self.student = student - super().__init__(out) +class CompensationPDFForm(CompensationForm, EpcBaseDocTemplate): + def __init__(self, out, *args, **kwargs): + super().__init__(out, *args, **kwargs) self.addPageTemplates([ PageTemplate(id='FirstPage', frames=[self.page_frame], onPage=self.header_iso) ]) def produce(self): - self.add_private_data(self.student.mentor) + self.add_private_data(self.expert) self.story.append(Paragraph( - "Mandat : Mentoring de {0} {1}, classe {2}".format( + self.mandat_template.format( self.student.civility, self.student.full_name, self.student.klass ), style_normal_center )) - self.story.append(Paragraph( - "Montant forfaitaire de Fr 500.- payable à la fin de la session d'examen", style_normal_center - )) + self.story.append(Paragraph(self.montant_template, style_normal_center)) self.story.append(Spacer(0, 3 * cm)) - self.add_accounting_stamp(self.student, self.MENTOR_MANDAT) + self.add_accounting_stamp(self.student, self.mandat_type) self.build(self.story) +class MentorCompensationPdfForm(CompensationPDFForm): + mandat_type = CompensationPDFForm.MENTOR_MANDAT + mandat_template = "Mandat : Mentoring de {0} {1}, classe {2}" + montant_template = "Montant forfaitaire de Fr 500.- payable à la fin de la session d’examen" + AMOUNT = '500.-' + + def __init__(self, out, student): + self.student = student + self.expert = student.mentor + super().__init__(out) + + +class EntretienProfCompensationPdfForm(CompensationPDFForm): + mandat_type = CompensationPDFForm.EXPERT_MANDAT + mandat_template = "Mandat : Entretien professionnel pour {0} {1}, classe {2}" + montant_template = "Montant forfaitaire de Fr 200.- payable à la fin de la session d’examen" + AMOUNT = '200.-' + + def __init__(self, out, exam): + self.student = exam.student + self.expert = exam.external_expert + super().__init__(out) + + +class SoutenanceCompensationPdfForm(EntretienProfCompensationPdfForm): + mandat_template = "Mandat : Soutenance pour {0} {1}, classe {2}" + montant_template = "Montant forfaitaire de Fr 200.- payable à la fin de la session d’examen" + AMOUNT = '200.-' + + class KlassListPDF(EpcBaseDocTemplate): """ Génération des rôles de classes en pdf. diff --git a/stages/tests.py b/stages/tests.py index 333a8d9..0e47d27 100644 --- a/stages/tests.py +++ b/stages/tests.py @@ -334,7 +334,7 @@ tél. 032 886 33 00 def test_print_ede_compensation_forms(self): st = Student.objects.get(first_name="Albin") exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1')) - url = reverse('print-expert-compens-ede', args=[exam.pk]) + url = reverse('print-expert-letter-ede', args=[exam.pk]) self.client.login(username='me', password='mepassword') response = self.client.get(url, follow=True) self.assertContains(response, "Toutes les informations ne sont pas disponibles") @@ -361,7 +361,7 @@ tél. 032 886 33 00 # Mentor form st.mentor = CorpContact.objects.get(last_name="Horner") st.save() - response = self.client.get(reverse('print-mentor-compens-ede', args=[st.pk]), follow=True) + response = self.client.get(reverse('print-mentor-compens-form', args=[st.pk]), follow=True) self.assertEqual( response['Content-Disposition'], 'attachment; filename="dupond_albin_Indemn_mentor.pdf"' @@ -378,7 +378,7 @@ tél. 032 886 33 00 pcode="2000", city="Neuchâtel", klass=klass ) exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1')) - url = reverse('print-expert-compens-eds', args=[exam.pk]) + url = reverse('print-expert-letter-eds', args=[exam.pk]) self.client.login(username='me', password='mepassword') response = self.client.get(url, follow=True) self.assertContains(response, "Toutes les informations ne sont pas disponibles") diff --git a/stages/views/__init__.py b/stages/views/__init__.py index 307c964..aaf3da0 100644 --- a/stages/views/__init__.py +++ b/stages/views/__init__.py @@ -26,10 +26,7 @@ from ..models import ( Klass, Section, Student, Teacher, Corporation, CorpContact, Period, Training, Availability, Examination, ) -from ..pdf import ( - ChargeSheetPDF, ExpertEdeLetterPdf, ExpertEdsLetterPdf, UpdateDataFormPDF, - MentorCompensationPdfForm, KlassListPDF, -) +from .. import pdf from ..utils import school_year_start @@ -501,8 +498,8 @@ class PrintUpdateForm(ZippedFilesBaseView): for klass in Klass.objects.filter(level__gte=2 ).exclude(section__name='MP_ASSC').exclude(section__name='MP_ASE'): buff = io.BytesIO() - pdf = UpdateDataFormPDF(buff, self.return_date) - pdf.produce(klass) + pdf_doc = pdf.UpdateDataFormPDF(buff, self.return_date) + pdf_doc.produce(klass) yield ('{0}.pdf'.format(klass.name), buff.getvalue()) @@ -511,7 +508,7 @@ class PrintExpertEDECompensationForm(PDFBaseView): Imprime le PDF à envoyer à l'expert EDE en accompagnement du travail de diplôme """ - pdf_class = ExpertEdeLetterPdf + pdf_class = pdf.ExpertEdeLetterPdf def filename(self, exam): return slugify('{0}_{1}'.format(exam.student.last_name, exam.student.first_name)) + '_Expert.pdf' @@ -536,25 +533,40 @@ class PrintExpertEDECompensationForm(PDFBaseView): return super().get(request, *args, **kwargs) -class PrintMentorEDECompensationForm(PDFBaseView): +class PrintCompensationForm(PDFBaseView): """ Imprime le PDF à envoyer au mentor EDE pour le mentoring """ - pdf_class = MentorCompensationPdfForm + @property + def pdf_class(self): + return { + 'mentor': pdf.MentorCompensationPdfForm, + 'ep': pdf.EntretienProfCompensationPdfForm, + 'sout': pdf.SoutenanceCompensationPdfForm, + }.get(self.typ) - def filename(self, student): + def filename(self, obj): + student = obj if self.typ == 'mentor' else obj.student return slugify( '{0}_{1}'.format(student.last_name, student.first_name) - ) + '_Indemn_mentor.pdf' + ) + f'_Indemn_{self.typ}.pdf' def get_object(self): - return Student.objects.get(pk=self.kwargs['pk']) + model = Student if self.typ == 'mentor' else Examination + return model.objects.get(pk=self.kwargs['pk']) def get(self, request, *args, **kwargs): - student = self.get_object() - if not student.mentor: - messages.error(request, "Aucun mentor n'est attribué à cet étudiant") - return redirect(reverse("admin:stages_student_change", args=(student.pk,))) + self.typ = self.kwargs['typ'] + if self.typ == 'mentor': + student = self.get_object() + if not student.mentor: + messages.error(request, "Aucun mentor n’est attribué à cet étudiant") + return redirect(reverse("admin:stages_student_change", args=(student.pk,))) + else: + exam = self.get_object() + if not exam.external_expert: + messages.error(request, "Aucun expert n’est attribué à cet examen") + return redirect(reverse("admin:stages_student_change", args=(exam.student.pk,))) return super().get(request, *args, **kwargs) @@ -563,7 +575,7 @@ class PrintExpertEDSCompensationForm(PrintExpertEDECompensationForm): Imprime le PDF à envoyer à l'expert EDS en accompagnement du travail final. """ - pdf_class = ExpertEdsLetterPdf + pdf_class = pdf.ExpertEdsLetterPdf def check_object(self, exam): missing = exam.missing_examination_data() @@ -581,8 +593,8 @@ class PrintKlassList(ZippedFilesBaseView): def generate_files(self): for klass in Klass.active.order_by('section', 'name'): buff = io.BytesIO() - pdf = KlassListPDF(buff, klass) - pdf.produce(klass) + pdf_doc = pdf.KlassListPDF(buff, klass) + pdf_doc.produce(klass) filename = slugify(klass.name + '.pdf') yield (filename, buff.getvalue()) @@ -599,7 +611,7 @@ class PrintChargeSheet(ZippedFilesBaseView): for teacher in queryset: activities = teacher.calc_activity() buff = io.BytesIO() - pdf = ChargeSheetPDF(buff, teacher) - pdf.produce(activities) + pdf_doc = pdf.ChargeSheetPDF(buff, teacher) + pdf_doc.produce(activities) filename = slugify('{0}_{1}'.format(teacher.last_name, teacher.first_name)) + '.pdf' yield (filename, buff.getvalue())