From 883538af343b75913e108410dc4a0862ccbc001e Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 23 Apr 2018 15:44:50 +0200 Subject: [PATCH] Add EDE mentor compensation form --- common/urls.py | 6 +- stages/admin.py | 6 +- stages/pdf.py | 204 +++++++++++++++++++++++++++++------------------- stages/tests.py | 15 +++- stages/views.py | 23 +++++- 5 files changed, 164 insertions(+), 90 deletions(-) diff --git a/common/urls.py b/common/urls.py index 381661f..506ada4 100644 --- a/common/urls.py +++ b/common/urls.py @@ -39,8 +39,10 @@ urlpatterns = [ name='student-ede-convocation'), path('student_ede//pdf_to_expert', views.print_pdf_to_expert_ede, name='print-pdf-to-expert-ede'), - path('student//examination_compensation', views.print_examination_compensation_form, - name='examination-compensation'), + path('student_ede//examination/expert', views.print_expert_ede_compensation_form, + name='print-expert-compens-ede'), + path('student_ede//examination/mentor', views.print_mentor_ede_compensation_form, + name='print-mentor-compens-ede'), path('imputations/export/', views.imputations_export, name='imputations_export'), path('print/update_form/', views.print_update_form, name='print_update_form'), diff --git a/stages/admin.py b/stages/admin.py index 5f51737..a8d9813 100644 --- a/stages/admin.py +++ b/stages/admin.py @@ -151,10 +151,12 @@ class StudentAdmin(admin.ModelAdmin): return format_html( 'Courrier pour l’expert ' 'Mail convocation soutenance ' - 'Indemnité aux experts', + 'Indemnité aux experts ' + 'Indemnité au mentor', reverse('print-pdf-to-expert-ede', args=[obj.pk]), reverse('student-ede-convocation', args=[obj.pk]), - reverse('examination-compensation', args=[obj.pk]) + reverse('print-expert-compens-ede', args=[obj.pk]), + reverse('print-mentor-compens-ede', args=[obj.pk]), ) else: return '' diff --git a/stages/pdf.py b/stages/pdf.py index bcd74a2..a33b6cc 100644 --- a/stages/pdf.py +++ b/stages/pdf.py @@ -18,19 +18,22 @@ from reportlab.platypus import ( ) style_normal = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment=TA_LEFT) +style_normal_center = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment=TA_CENTER) style_bold = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=10, alignment=TA_LEFT) style_title = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=12, alignment=TA_LEFT, spaceBefore=1*cm) style_adress = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment=TA_LEFT, leftIndent=280) style_normal_right = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment=TA_RIGHT) style_bold_center = PS(name="CORPS", fontName="Helvetica-Bold", fontSize=9, alignment=TA_CENTER) style_footer = PS(name='CORPS', fontName='Helvetica', fontSize=7, alignment=TA_CENTER) -style_bold_center_12 = PS(name="CORPS", fontName="Helvetica-Bold", fontSize=12, alignment=TA_CENTER) +style_bold_title = PS(name="CORPS", fontName="Helvetica-Bold", fontSize=12, alignment=TA_LEFT) LOGO_EPC = find('img/logo_EPC.png') LOGO_ESNE = find('img/logo_ESNE.png') class CifomBaseISO(SimpleDocTemplate): + points = '.' * 93 + def __init__(self, filename): super().__init__( filename, pagesize=A4, _pageBreakQuick=0, @@ -69,6 +72,78 @@ class CifomBaseISO(SimpleDocTemplate): first_page = PageTemplate(id='FirstPage', frames=[first_page_table_frame], onPage=self.header) self.addPageTemplates([first_page]) + def private_data(self, person): + self.story.append(Spacer(0, 0.5 * cm)) + self.story.append(Paragraph('DONNÉES PRIVÉES', style_bold)) + self.story.append(Spacer(0, 0.2 * cm)) + data = [ + [self.formating('Nom : '), person.last_name or self.points], + [self.formating('Prénom :'), person.first_name or self.points], + [ + self.formating('Date de naissance :'), + django_format(person.birth_date, 'j F Y') if person.birth_date else self.points + ], + [self.formating('N° de téléphone :'), person.tel or self.points], + [self.formating('Adresse complète :'), person.street or self.points], + ['', person.pcode_city if person.pcode else self.points], + ['', self.points], + [self.formating('Employeur :'), person.corporation.name or self.points], + [Spacer(0, 0.2 * cm)], + ] + + t = Table(data, colWidths=[4 * cm, 12 * cm], hAlign=TA_LEFT) + t.setStyle(TableStyle([('ALIGN', (1, 0), (-1, -1), 'LEFT'), + ('BOX', (0, 0), (-1, -1), 0.25, colors.black), + ])) + self.story.append(t) + self.story.append(Spacer(0, 0.5 * cm)) + + def account_data(self, person): + self.story.append(Paragraph('COORDONNÉES DE PAIEMENT', style_bold)) + self.story.append(Spacer(0, 0.2 * cm)) + data = [ + [self.formating('N° de ccp ou compte bancaire :'), person.ccp or self.points], + [self.formating('Si banque, nom et adresse de celle-ci :'), person.bank or self.points], + [self.formating('ainsi que N° IBAN :'), person.iban or self.points], + ] + + t = Table(data, colWidths=[4 * cm, 12 * cm], hAlign=TA_LEFT) + t.setStyle(TableStyle([('ALIGN', (1, 0), (-1, -1), 'LEFT'), + ('BOX', (0, 0), (-1, -1), 0.25, colors.black), + ])) + self.story.append(t) + self.story.append(Spacer(0, 0.5 * cm)) + + def formating(self, text): + return Preformatted(text, style_normal, maxLineLength=25) + + def stamp_account(self): + data = [['Visa chef de service:', "Donneur d'ordre et visa:", "Total en Fr.:"]] + t = Table(data, colWidths=[4 * cm, 4 * cm, 4 * cm], rowHeights=(1.2 * cm,), hAlign=TA_CENTER) + t.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ('FONTSIZE', (0, 0), (-1, -1), 7), + ('BOX', (0, 0), (-1, -1), 0.25, colors.black), + ('GRID', (0, 0), (-1, -1), 0.25, colors.black), + ])) + self.story.append(t) + + data = [ + ['No écriture', "Compte à débiter", "CC / OTP", " Montants"], + ["Pièces annexées", '', '', 'Fr.'], + ["Ordre", '', '', 'Fr.'], + ["No fournisseur", '', '', 'Fr.'], + ["Date scannage et visa", '', '', 'Fr.'], + ] + t = Table(data, colWidths=[3 * cm, 3 * cm, 3 * cm, 3 * cm], hAlign=TA_CENTER) + t.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ('FONTSIZE', (0, 0), (-1, -1), 7), + ('BOX', (0, 0), (-1, -1), 0.25, colors.black), + ('GRID', (0, 0), (-1, -1), 0.25, colors.black), + ])) + self.story.append(t) + class EpcBaseDocTemplate(SimpleDocTemplate): filiere = 'Formation EDE' @@ -392,7 +467,7 @@ class ExpertEDEPDF(EpcBaseLetterTemplate): self.build(self.story) -class ExaminationCompensationPdfForm(CifomBaseISO): +class ExpertCompensationPdfForm(CifomBaseISO): def __init__(self, student): self.student = student @@ -403,62 +478,23 @@ class ExaminationCompensationPdfForm(CifomBaseISO): super().__init__(path) self.set_normal_template_page() - def formating(self, text): - return Preformatted(text, style_normal, maxLineLength=20) - def produce(self): - self.story.append(Spacer(0, 0.7 * cm)) - self.story.append(Paragraph('Ecole Santé-social Pierre-Coullery', style_bold_center_12)) + self.story.append(Paragraph('Ecole Santé-social Pierre-Coullery', style_bold_title)) self.story.append(Spacer(0, 0.7 * cm)) - self.story.append(Paragraph('DONNÉES PRIVÉES', style_bold)) - expert = self.student.expert - data = [ - [self.formating('NOM : '), expert.last_name], - [self.formating('Prénom :'), expert.first_name], - [ - self.formating('Date de naissance :'), - django_format(expert.birth_date, 'j F Y') if expert.birth_date else '?' - ], - [self.formating('N° de téléphone :'), expert.tel], - [self.formating('Adresse complète :'), expert.street], - ['', expert.pcode_city], - ['', ''], - [self.formating('Employeur :'), expert.corporation.name], - ] + self.private_data(self.student.expert) + self.account_data(self.student.expert) - t = Table(data, colWidths=[4 * cm, 12 * cm]) - t.hAlign = TA_LEFT - t.setStyle(TableStyle([('ALIGN', (1, 0), (-1, -1), 'LEFT'), - ('BOX', (0, 0), (-1, -1), 0.25, colors.black), - ])) - self.story.append(t) - self.story.append(Spacer(0, 0.5 * cm)) - - self.story.append(Paragraph('COORDONNÉES DE PAIEMENT', style_bold)) - data = [] - data.append([self.formating('N° de ccp ou compte bancaire :'), expert.ccp]) - data.append([self.formating('Si banque, nom et adresse de celle-ci :'), expert.bank]) - data.append([self.formating('ainsi que N° IBAN :'), expert.iban]) - data.append(['', '']) - data.append([ - self.formating('Mandat :'), - 'Soutenance de {0} {1}, classe {2}'.format( + self.story.append(Paragraph( + "Soutenance de {0} {1}, classe {2}".format( self.student.civility, self.student.full_name, self.student.klass - ) - ]) - data.append([ - self.formating('Date des examens :'), - django_format(self.student.date_exam, 'l j F Y') - ]) + ), style_normal + )) + self.story.append(Paragraph( + "Date de l'examen : {}".format(django_format(self.student.date_exam, 'l j F Y')), style_normal + )) - t = Table(data, colWidths=[4 * cm, 12 * cm]) - t.hAlign = TA_LEFT - t.setStyle(TableStyle([('ALIGN', (1, 0), (-1, -1), 'LEFT'), - ('BOX', (0, 0), (-1, -4), 0.25, colors.black), - ])) - self.story.append(t) - self.story.append(Spacer(0, 1.5 * cm)) + self.story.append(Spacer(0, 2 * cm)) data = [ ['Indemnités', 'Fr.'], @@ -466,40 +502,46 @@ class ExaminationCompensationPdfForm(CifomBaseISO): ['Repas', 'Fr.'], ['TOTAL', 'Fr.'], ] - t = Table(data, colWidths=[4.5 * cm, 3 * cm]) - t.hAlign = TA_CENTER + t = Table(data, colWidths=[4.5 * cm, 3 * cm], hAlign=TA_CENTER) t.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), ('LINEBELOW', (1, 2), (2, 2), 0.5, colors.black), ('LINEBELOW', (1, 3), (2, 3), 0.5, colors.black), ])) self.story.append(t) - self.story.append(Spacer(0, 1.5 * cm)) - data = [['Visa chef de service:', "Donneur d'ordre et visa:", "Total en Fr.:"]] - t = Table(data, colWidths=[4 * cm, 4 * cm, 4 * cm], rowHeights=(1.2 * cm, )) - t.hAlign = TA_CENTER - t.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('FONTSIZE', (0, 0), (-1, -1), 7), - ('BOX', (0, 0), (-1, -1), 0.25, colors.black), - ('GRID', (0, 0), (-1, -1), 0.25, colors.black), - ])) - self.story.append(t) + self.story.append(Spacer(0, 1 * cm)) - data = [ - ['No écriture', "Compte à débiter", "CC / OTP", " Montants"], - ["Pièces annexées",'','', 'Fr.'], - ["Ordre", '', '', 'Fr.'], - ["No fournisseur", '', '', 'Fr.'], - ["Date scannage et visa", '', '', 'Fr.'], - ] - t = Table(data, colWidths=[3 * cm, 3 * cm, 3 * cm, 3 * cm]) - t.hAlign = TA_CENTER - t.setStyle(TableStyle([('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('FONTSIZE', (0, 0), (-1, -1), 7), - ('BOX', (0, 0), (-1, -1), 0.25, colors.black), - ('GRID', (0, 0), (-1, -1), 0.25, colors.black), - ])) - self.story.append(t) + self.stamp_account() + + self.build(self.story) + + +class MentorCompensationPdfForm(CifomBaseISO): + def __init__(self, student): + self.student = student + filename = slugify( + '{0}_{1}'.format(self.student.last_name, self.student.first_name) + ) + '_Indemn_mentor.pdf' + path = os.path.join(tempfile.gettempdir(), filename) + super().__init__(path) + self.set_normal_template_page() + + def produce(self): + self.story.append(Paragraph('Ecole Santé-social Pierre-Coullery', style_bold_title)) + self.story.append(Spacer(0, 0.7 * cm)) + self.private_data(self.student.mentor) + self.account_data(self.student.mentor) + self.story.append(Spacer(0, 4 * cm)) + + self.story.append(Paragraph( + "Mandat : Mentoring de {0} {1}, classe {2}".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(Spacer(0, 1 * cm)) + + self.stamp_account() self.build(self.story) diff --git a/stages/tests.py b/stages/tests.py index 45f1c29..17e784c 100644 --- a/stages/tests.py +++ b/stages/tests.py @@ -223,9 +223,9 @@ tél. 032 886 33 00 self.assertEqual(response['Content-Type'], 'application/pdf') self.assertGreater(len(response.content), 200) - def test_print_ede_expert_compensation(self): + def test_print_ede_compensation_forms(self): st = Student.objects.get(first_name="Albin") - url = reverse('examination-compensation', args=[st.pk]) + url = reverse('print-expert-compens-ede', args=[st.pk]) self.client.login(username='me', password='mepassword') response = self.client.post(url, follow=True) self.assertContains(response, "Toutes les informations ne sont pas disponibles") @@ -244,6 +244,17 @@ tél. 032 886 33 00 self.assertEqual(response['Content-Type'], 'application/pdf') self.assertGreater(len(response.content), 200) + # Mentor form + st.mentor = CorpContact.objects.get(last_name="Horner") + st.save() + response = self.client.post(reverse('print-mentor-compens-ede', args=[st.pk]), follow=True) + self.assertEqual( + response['Content-Disposition'], + 'attachment; filename="dupond_albin_Indemn_mentor.pdf"' + ) + self.assertEqual(response['Content-Type'], 'application/pdf') + self.assertGreater(len(response.content), 200) + class PeriodTest(TestCase): def setUp(self): diff --git a/stages/views.py b/stages/views.py index 9e13ef6..f397c6e 100644 --- a/stages/views.py +++ b/stages/views.py @@ -34,7 +34,7 @@ from .models import ( Klass, Section, Option, Student, Teacher, Corporation, CorpContact, Course, Period, Training, Availability, ) -from .pdf import ExaminationCompensationPdfForm, ExpertEDEPDF, UpdateDataFormPDF +from .pdf import ExpertCompensationPdfForm, ExpertEDEPDF, UpdateDataFormPDF, MentorCompensationPdfForm from .utils import is_int @@ -930,7 +930,7 @@ def print_pdf_to_expert_ede(request, pk): return response -def print_examination_compensation_form(request, pk): +def print_expert_ede_compensation_form(request, pk): """ Imprime le PDF à envoyer à l'expert EDE en accompagnement du travail de diplôme @@ -939,7 +939,24 @@ def print_examination_compensation_form(request, pk): if not student.is_examination_valid: messages.error(request, "Toutes les informations ne sont pas disponibles pour la lettre à l’expert!") return redirect(reverse("admin:stages_student_change", args=(student.pk,))) - pdf = ExaminationCompensationPdfForm(student) + pdf = ExpertCompensationPdfForm(student) + pdf.produce() + + with open(pdf.filename, mode='rb') as fh: + response = HttpResponse(fh.read(), content_type='application/pdf') + response['Content-Disposition'] = 'attachment; filename="{0}"'.format(os.path.basename(pdf.filename)) + return response + + +def print_mentor_ede_compensation_form(request, pk): + """ + Imprime le PDF à envoyer au mentor EDE pour le mentoring + """ + student = Student.objects.get(pk=pk) + if not student.mentor: + messages.error(request, "Aucun mentor n'est attribué à cet étudiant") + return redirect(reverse("admin:stages_student_change", args=(student.pk,))) + pdf = MentorCompensationPdfForm(student) pdf.produce() with open(pdf.filename, mode='rb') as fh: