source: src/lib/pdf-generators.ts@ 4fe4582

Last change on this file since 4fe4582 was 4fe4582, checked in by Stefan-Saveski <stefansaveski19@…>, 7 months ago

Refactor code structure for improved readability and maintainability

  • Property mode set to 100644
File size: 48.6 KB
Line 
1import { jsPDF } from 'jspdf';
2import autoTable from 'jspdf-autotable';
3import { loadCyrillicFonts } from './pdf-fonts';
4
5/* ---------- shared types ---------- */
6
7export type StudentInfo = {
8 firstName: string;
9 middleName: string;
10 lastName: string;
11 index: string;
12 embg: string;
13 dateOfBirth: string;
14 gender: string;
15 nationality: string;
16 citizenship: string;
17 currentPlan: string;
18 registryNumber: string;
19 studyGroup: string;
20 scholarship: string;
21};
22
23export type EnrollmentInfo = {
24 enrollmentYear: string | number;
25 status: string;
26 cycle: string;
27 program: string;
28 quota: string;
29};
30
31export type BirthInfo = {
32 placeOfBirth: string;
33 municipalityOfBirth: string;
34 country: string;
35};
36
37export type ContactInfo = {
38 placeOfResidence: string;
39 municipalityOfResidence: string;
40 address: string;
41 phone: string;
42 mobilePhone: string;
43 email: string;
44};
45
46export type PassedExam = {
47 id: number;
48 code: string;
49 subject: string;
50 credits: number;
51 grade: number;
52 date: string;
53 semester: string;
54 professor: string;
55};
56
57export type PreviousEducation = {
58 type: string;
59 profession: string;
60 average: string | number;
61 language: string;
62 country: string;
63 previousUniversity: string;
64 previousFaculty: string;
65};
66
67/* ---------- helpers ---------- */
68
69const PAGE_WIDTH = 210; // A4 mm
70const MARGIN = 20;
71const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN;
72
73function drawUniversityHeader(doc: jsPDF, y: number): number {
74 doc.setFont('Roboto', 'bold');
75
76 doc.setFontSize(11);
77 doc.text('УНИВЕРЗИТЕТ „СВ. КИРИЛ И МЕТОДИЈ" - СКОПЈЕ', PAGE_WIDTH / 2, y, { align: 'center' });
78 y += 6;
79
80 doc.setFontSize(10);
81 doc.text('ФАКУЛТЕТ ЗА ИНФОРМАТИЧКИ НАУКИ И', PAGE_WIDTH / 2, y, { align: 'center' });
82 y += 5;
83 doc.text('КОМПЈУТЕРСКО ИНЖЕНЕРСТВО', PAGE_WIDTH / 2, y, { align: 'center' });
84 y += 5;
85
86 // Horizontal line under header
87 doc.setDrawColor(0, 114, 209); // primary blue
88 doc.setLineWidth(0.5);
89 doc.line(MARGIN, y, PAGE_WIDTH - MARGIN, y);
90 y += 8;
91
92 return y;
93}
94
95function drawDocumentTitle(doc: jsPDF, title: string, y: number): number {
96 doc.setFont('Roboto', 'bold');
97 doc.setFontSize(14);
98 doc.text(title, PAGE_WIDTH / 2, y, { align: 'center' });
99 y += 10;
100 return y;
101}
102
103function drawInfoRow(doc: jsPDF, label: string, value: string, x: number, y: number, labelWidth = 50): number {
104 doc.setFont('Roboto', 'bold');
105 doc.setFontSize(9);
106 doc.text(`${label}:`, x, y);
107 doc.setFont('Roboto', 'normal');
108 doc.text(String(value || '—'), x + labelWidth, y);
109 return y + 5.5;
110}
111
112function drawFooter(doc: jsPDF, archiveNumber: string, date: string): void {
113 const pageHeight = doc.internal.pageSize.getHeight();
114 let y = pageHeight - 40;
115
116 doc.setDrawColor(0, 114, 209);
117 doc.setLineWidth(0.3);
118 doc.line(MARGIN, y, PAGE_WIDTH - MARGIN, y);
119 y += 8;
120
121 doc.setFont('Roboto', 'normal');
122 doc.setFontSize(9);
123
124 doc.text(`Бр.: ${archiveNumber}`, MARGIN, y);
125 doc.text(`Датум: ${date}`, PAGE_WIDTH / 2, y, { align: 'center' });
126 y += 12;
127
128 // Signature placeholders
129 doc.text('Потпис на студентот:', MARGIN, y);
130 doc.text('М.П.', PAGE_WIDTH / 2, y, { align: 'center' });
131 doc.text('Одговорно лице:', PAGE_WIDTH - MARGIN - 35, y);
132 y += 8;
133
134 doc.setDrawColor(150, 150, 150);
135 doc.setLineWidth(0.2);
136 doc.line(MARGIN, y, MARGIN + 45, y);
137 doc.line(PAGE_WIDTH / 2 - 15, y, PAGE_WIDTH / 2 + 15, y);
138 doc.line(PAGE_WIDTH - MARGIN - 45, y, PAGE_WIDTH - MARGIN, y);
139}
140
141/* ==================================================================
142 PDF 1 — Уверение за положени испити (Certificate of Passed Exams)
143 ================================================================== */
144
145export async function generatePassedExamsCertificate(
146 student: StudentInfo,
147 enrollment: EnrollmentInfo,
148 passedExams: PassedExam[],
149 archiveNumber: string,
150 date: string,
151): Promise<void> {
152 const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
153 await loadCyrillicFonts(doc);
154
155 let y = 20;
156
157 // --- Header ---
158 y = drawUniversityHeader(doc, y);
159
160 // --- Title ---
161 y = drawDocumentTitle(doc, 'УВЕРЕНИЕ ЗА ПОЛОЖЕНИ ИСПИТИ', y);
162
163 // --- Student Info Block ---
164 doc.setFont('Roboto', 'normal');
165 doc.setFontSize(9);
166
167 y = drawInfoRow(doc, 'Студент', `${student.firstName} ${student.middleName} ${student.lastName}`, MARGIN, y);
168 y = drawInfoRow(doc, 'Индекс', student.index, MARGIN, y);
169 y = drawInfoRow(doc, 'Студиска програма', enrollment.program, MARGIN, y);
170 y = drawInfoRow(doc, 'Циклус на студии', enrollment.cycle, MARGIN, y);
171 y = drawInfoRow(doc, 'Статус', enrollment.status, MARGIN, y);
172 y += 4;
173
174 // --- Passed Exams Table ---
175 const tableBody = passedExams.map((exam, idx) => [
176 String(idx + 1),
177 exam.code,
178 exam.subject,
179 String(exam.credits),
180 String(exam.grade),
181 exam.date,
182 exam.semester,
183 exam.professor || '—',
184 ]);
185
186 autoTable(doc, {
187 startY: y,
188 head: [['Р.Б.', 'Шифра', 'Предмет', 'Кредити', 'Оцена', 'Датум', 'Семестар', 'Професор']],
189 body: tableBody,
190 theme: 'grid',
191 styles: {
192 font: 'Roboto',
193 fontSize: 7.5,
194 cellPadding: 2,
195 textColor: [30, 30, 30],
196 lineColor: [200, 200, 200],
197 lineWidth: 0.2,
198 },
199 headStyles: {
200 fillColor: [0, 114, 209],
201 textColor: [255, 255, 255],
202 fontStyle: 'bold',
203 fontSize: 7.5,
204 halign: 'center',
205 },
206 columnStyles: {
207 0: { halign: 'center', cellWidth: 10 },
208 1: { halign: 'center', cellWidth: 16 },
209 2: { cellWidth: 50 },
210 3: { halign: 'center', cellWidth: 14 },
211 4: { halign: 'center', cellWidth: 12 },
212 5: { halign: 'center', cellWidth: 20 },
213 6: { cellWidth: 22 },
214 7: { cellWidth: 26 },
215 },
216 margin: { left: MARGIN, right: MARGIN },
217 didParseCell: (data) => {
218 if (data.section === 'body' && data.row.index % 2 === 0) {
219 data.cell.styles.fillColor = [245, 247, 250];
220 }
221 },
222 });
223
224 // eslint-disable-next-line @typescript-eslint/no-explicit-any
225 y = (doc as any).lastAutoTable.finalY + 8;
226
227 // --- Summary ---
228 const totalCredits = passedExams.reduce((sum, e) => sum + e.credits, 0);
229 const grades = passedExams.map((e) => e.grade).filter((g) => g > 0);
230 const average = grades.length > 0 ? (grades.reduce((s, g) => s + g, 0) / grades.length).toFixed(2) : '0.00';
231
232 doc.setFont('Roboto', 'bold');
233 doc.setFontSize(10);
234 doc.text(`Вкупно положени предмети: ${passedExams.length}`, MARGIN, y);
235 y += 6;
236 doc.text(`Вкупно кредити: ${totalCredits}`, MARGIN, y);
237 y += 6;
238 doc.text(`Просечна оцена: ${average}`, MARGIN, y);
239
240 // --- Footer ---
241 drawFooter(doc, archiveNumber, date);
242
243 doc.save(`Уверение_положени_испити_${student.index}.pdf`);
244}
245
246/* ==================================================================
247 PDF 2 — УППИ образец (UPPI Form / Student Record)
248 ================================================================== */
249
250export async function generateUPPIForm(
251 student: StudentInfo,
252 enrollment: EnrollmentInfo,
253 birth: BirthInfo,
254 contact: ContactInfo,
255 previousEdu: PreviousEducation,
256 passedExams: PassedExam[],
257 archiveNumber: string,
258 date: string,
259): Promise<void> {
260 const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
261 await loadCyrillicFonts(doc);
262
263 let y = 20;
264
265 // --- Header ---
266 y = drawUniversityHeader(doc, y);
267
268 // --- Title ---
269 y = drawDocumentTitle(doc, 'УППИ ОБРАЗЕЦ', y);
270
271 doc.setFont('Roboto', 'bold');
272 doc.setFontSize(9);
273 doc.text('УНИВЕРЗАЛНА ПРИЈАВА ЗА ПРАКТИЧНА ИНДИВИДУАЛНА РАБОТА', PAGE_WIDTH / 2, y, { align: 'center' });
274 y += 10;
275
276 // --- Section: Personal Information ---
277 doc.setFillColor(0, 114, 209);
278 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
279 doc.setFont('Roboto', 'bold');
280 doc.setFontSize(9);
281 doc.setTextColor(255, 255, 255);
282 doc.text('ЛИЧНИ ПОДАТОЦИ', MARGIN + 3, y + 0.5);
283 doc.setTextColor(30, 30, 30);
284 y += 10;
285
286 const col2X = MARGIN + CONTENT_WIDTH / 2;
287
288 y = drawInfoRow(doc, 'Име', student.firstName, MARGIN, y, 35);
289 y -= 5.5;
290 y = drawInfoRow(doc, 'Презиме', student.lastName, col2X, y, 35);
291 y = drawInfoRow(doc, 'Татково име', student.middleName, MARGIN, y, 35);
292 y -= 5.5;
293 y = drawInfoRow(doc, 'ЕМБГ', student.embg, col2X, y, 35);
294 y = drawInfoRow(doc, 'Датум на раѓање', student.dateOfBirth, MARGIN, y, 35);
295 y -= 5.5;
296 y = drawInfoRow(doc, 'Пол', student.gender, col2X, y, 35);
297 y = drawInfoRow(doc, 'Националност', student.nationality, MARGIN, y, 35);
298 y -= 5.5;
299 y = drawInfoRow(doc, 'Државјанство', student.citizenship, col2X, y, 35);
300 y += 3;
301
302 // --- Section: Birth Info ---
303 doc.setFillColor(0, 114, 209);
304 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
305 doc.setFont('Roboto', 'bold');
306 doc.setFontSize(9);
307 doc.setTextColor(255, 255, 255);
308 doc.text('ПОДАТОЦИ ЗА РАЃАЊЕ', MARGIN + 3, y + 0.5);
309 doc.setTextColor(30, 30, 30);
310 y += 10;
311
312 y = drawInfoRow(doc, 'Место на раѓање', birth.placeOfBirth, MARGIN, y, 40);
313 y = drawInfoRow(doc, 'Општина', birth.municipalityOfBirth, MARGIN, y, 40);
314 y = drawInfoRow(doc, 'Држава', birth.country, MARGIN, y, 40);
315 y += 3;
316
317 // --- Section: Contact ---
318 doc.setFillColor(0, 114, 209);
319 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
320 doc.setFont('Roboto', 'bold');
321 doc.setFontSize(9);
322 doc.setTextColor(255, 255, 255);
323 doc.text('КОНТАКТ ИНФОРМАЦИИ', MARGIN + 3, y + 0.5);
324 doc.setTextColor(30, 30, 30);
325 y += 10;
326
327 y = drawInfoRow(doc, 'Место на живеење', contact.placeOfResidence, MARGIN, y, 40);
328 y = drawInfoRow(doc, 'Адреса', contact.address, MARGIN, y, 40);
329 y = drawInfoRow(doc, 'Телефон', contact.phone || contact.mobilePhone, MARGIN, y, 40);
330 y = drawInfoRow(doc, 'Е-пошта', contact.email, MARGIN, y, 40);
331 y += 3;
332
333 // --- Section: Enrollment ---
334 doc.setFillColor(0, 114, 209);
335 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
336 doc.setFont('Roboto', 'bold');
337 doc.setFontSize(9);
338 doc.setTextColor(255, 255, 255);
339 doc.text('СТУДИСКИ ИНФОРМАЦИИ', MARGIN + 3, y + 0.5);
340 doc.setTextColor(30, 30, 30);
341 y += 10;
342
343 y = drawInfoRow(doc, 'Индекс', student.index, MARGIN, y, 40);
344 y -= 5.5;
345 y = drawInfoRow(doc, 'Мат. број', student.registryNumber, col2X, y, 35);
346 y = drawInfoRow(doc, 'Студиска програма', enrollment.program, MARGIN, y, 40);
347 y = drawInfoRow(doc, 'Циклус', enrollment.cycle, MARGIN, y, 40);
348 y -= 5.5;
349 y = drawInfoRow(doc, 'Статус', enrollment.status, col2X, y, 35);
350 y = drawInfoRow(doc, 'Год. на запишување', String(enrollment.enrollmentYear), MARGIN, y, 40);
351 y -= 5.5;
352 y = drawInfoRow(doc, 'Квота', enrollment.quota, col2X, y, 35);
353 y += 3;
354
355 // --- Section: Previous Education ---
356 doc.setFillColor(0, 114, 209);
357 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
358 doc.setFont('Roboto', 'bold');
359 doc.setFontSize(9);
360 doc.setTextColor(255, 255, 255);
361 doc.text('ПРЕТХОДНО ОБРАЗОВАНИЕ', MARGIN + 3, y + 0.5);
362 doc.setTextColor(30, 30, 30);
363 y += 10;
364
365 y = drawInfoRow(doc, 'Вид', previousEdu.type, MARGIN, y, 40);
366 y = drawInfoRow(doc, 'Просек', String(previousEdu.average), MARGIN, y, 40);
367 y = drawInfoRow(doc, 'Јазик', previousEdu.language, MARGIN, y, 40);
368 y += 3;
369
370 // --- Section: Passed Exams mini-table ---
371 doc.setFillColor(0, 114, 209);
372 doc.rect(MARGIN, y - 4, CONTENT_WIDTH, 7, 'F');
373 doc.setFont('Roboto', 'bold');
374 doc.setFontSize(9);
375 doc.setTextColor(255, 255, 255);
376 doc.text('ПОЛОЖЕНИ ИСПИТИ', MARGIN + 3, y + 0.5);
377 doc.setTextColor(30, 30, 30);
378 y += 8;
379
380 const examTableBody = passedExams.map((exam, idx) => [
381 String(idx + 1),
382 exam.code,
383 exam.subject,
384 String(exam.credits),
385 String(exam.grade),
386 exam.date,
387 ]);
388
389 autoTable(doc, {
390 startY: y,
391 head: [['#', 'Шифра', 'Предмет', 'Кредити', 'Оцена', 'Датум']],
392 body: examTableBody,
393 theme: 'grid',
394 styles: {
395 font: 'Roboto',
396 fontSize: 7,
397 cellPadding: 1.5,
398 textColor: [30, 30, 30],
399 lineColor: [200, 200, 200],
400 lineWidth: 0.2,
401 },
402 headStyles: {
403 fillColor: [0, 114, 209],
404 textColor: [255, 255, 255],
405 fontStyle: 'bold',
406 fontSize: 7,
407 halign: 'center',
408 },
409 columnStyles: {
410 0: { halign: 'center', cellWidth: 8 },
411 1: { halign: 'center', cellWidth: 18 },
412 2: { cellWidth: 70 },
413 3: { halign: 'center', cellWidth: 16 },
414 4: { halign: 'center', cellWidth: 14 },
415 5: { halign: 'center', cellWidth: 22 },
416 },
417 margin: { left: MARGIN, right: MARGIN },
418 didParseCell: (data) => {
419 if (data.section === 'body' && data.row.index % 2 === 0) {
420 data.cell.styles.fillColor = [245, 247, 250];
421 }
422 },
423 });
424
425 // eslint-disable-next-line @typescript-eslint/no-explicit-any
426 y = (doc as any).lastAutoTable.finalY + 8;
427
428 // Summary
429 const totalCredits = passedExams.reduce((sum, e) => sum + e.credits, 0);
430 const grades = passedExams.map((e) => e.grade).filter((g) => g > 0);
431 const average = grades.length > 0 ? (grades.reduce((s, g) => s + g, 0) / grades.length).toFixed(2) : '0.00';
432
433 doc.setFont('Roboto', 'bold');
434 doc.setFontSize(9);
435 doc.text(`Вкупно кредити: ${totalCredits} | Просечна оцена: ${average}`, MARGIN, y);
436
437 // Footer
438 drawFooter(doc, archiveNumber, date);
439
440 doc.save(`УППИ_образец_${student.index}.pdf`);
441}
442
443/* ==================================================================
444 PDF 3 — Уверение за редовен студент (Regular Student Certificate)
445 ================================================================== */
446
447export async function generateRegularStudentCertificate(
448 student: StudentInfo,
449 enrollment: EnrollmentInfo,
450 passedExams: PassedExam[],
451 archiveNumber: string,
452 date: string,
453): Promise<void> {
454 const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
455 await loadCyrillicFonts(doc);
456
457 let y = 20;
458
459 // --- Header ---
460 y = drawUniversityHeader(doc, y);
461
462 // --- Title ---
463 y = drawDocumentTitle(doc, 'УВЕРЕНИЕ', y);
464 doc.setFont('Roboto', 'normal');
465 doc.setFontSize(10);
466 doc.text('за редовен студент', PAGE_WIDTH / 2, y, { align: 'center' });
467 y += 12;
468
469 // --- Body text ---
470 doc.setFont('Roboto', 'normal');
471 doc.setFontSize(10);
472
473 const totalCredits = passedExams.reduce((sum, e) => sum + e.credits, 0);
474 const grades = passedExams.map((e) => e.grade).filter((g) => g > 0);
475 const average = grades.length > 0 ? (grades.reduce((s, g) => s + g, 0) / grades.length).toFixed(2) : '0.00';
476
477 const bodyText = [
478 `Со ова се потврдува дека студентот/студентката`,
479 ``,
480 `${student.firstName} ${student.middleName} ${student.lastName}`,
481 ``,
482 `со индекс број ${student.index}, ЕМБГ ${student.embg},`,
483 `е редовен студент на Факултетот за информатички науки и компјутерско`,
484 `инженерство при Универзитетот „Св. Кирил и Методиј" - Скопје.`,
485 ``,
486 `Студиска програма: ${enrollment.program}`,
487 `Циклус на студии: ${enrollment.cycle}`,
488 `Статус: ${enrollment.status}`,
489 `Година на запишување: ${enrollment.enrollmentYear}`,
490 ``,
491 `Студентот/студентката до сега има положено ${passedExams.length} испити,`,
492 `со вкупно ${totalCredits} кредити и просечна оцена ${average}.`,
493 ``,
494 `Ова уверение се издава за потребите на студентот/студентката`,
495 `и може да послужи за секоја законска употреба.`,
496 ];
497
498 for (const line of bodyText) {
499 if (line === '') {
500 y += 4;
501 } else {
502 if (line.startsWith(`${student.firstName}`)) {
503 doc.setFont('Roboto', 'bold');
504 doc.setFontSize(12);
505 doc.text(line, PAGE_WIDTH / 2, y, { align: 'center' });
506 doc.setFont('Roboto', 'normal');
507 doc.setFontSize(10);
508 } else {
509 doc.text(line, MARGIN, y);
510 }
511 y += 6;
512 }
513 }
514
515 // Footer
516 drawFooter(doc, archiveNumber, date);
517
518 doc.save(`Уверение_редовен_студент_${student.index}.pdf`);
519}
520
521/* ==================================================================
522 Generic document — fallback for other document types
523 ================================================================== */
524
525export async function generateGenericDocument(
526 student: StudentInfo,
527 enrollment: EnrollmentInfo,
528 documentTitle: string,
529 archiveNumber: string,
530 date: string,
531): Promise<void> {
532 const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
533 await loadCyrillicFonts(doc);
534
535 let y = 20;
536
537 y = drawUniversityHeader(doc, y);
538 y = drawDocumentTitle(doc, documentTitle.toUpperCase(), y);
539
540 doc.setFont('Roboto', 'normal');
541 doc.setFontSize(10);
542
543 y = drawInfoRow(doc, 'Студент', `${student.firstName} ${student.middleName} ${student.lastName}`, MARGIN, y);
544 y = drawInfoRow(doc, 'Индекс', student.index, MARGIN, y);
545 y = drawInfoRow(doc, 'ЕМБГ', student.embg, MARGIN, y);
546 y = drawInfoRow(doc, 'Студиска програма', enrollment.program, MARGIN, y);
547 y = drawInfoRow(doc, 'Циклус', enrollment.cycle, MARGIN, y);
548 y = drawInfoRow(doc, 'Статус', enrollment.status, MARGIN, y);
549 y += 8;
550
551 doc.setFont('Roboto', 'normal');
552 doc.setFontSize(10);
553 doc.text('Документот е издаден по барање на студентот.', MARGIN, y);
554
555 drawFooter(doc, archiveNumber, date);
556
557 doc.save(`${documentTitle.replace(/\s+/g, '_')}_${student.index}.pdf`);
558}
559
560/* ==================================================================
561 Themed request generators — one for each document type
562 ================================================================== */
563
564async function generateRequestDocument(
565 student: StudentInfo,
566 enrollment: EnrollmentInfo,
567 title: string,
568 bodyLines: string[],
569 archiveNumber: string,
570 date: string,
571 filename: string,
572): Promise<void> {
573 const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
574 await loadCyrillicFonts(doc);
575
576 let y = 20;
577
578 y = drawUniversityHeader(doc, y);
579 y = drawDocumentTitle(doc, title, y);
580
581 // Student info block
582 doc.setFont('Roboto', 'normal');
583 doc.setFontSize(9);
584 y = drawInfoRow(doc, 'Студент', `${student.firstName} ${student.middleName} ${student.lastName}`, MARGIN, y);
585 y = drawInfoRow(doc, 'Индекс', student.index, MARGIN, y);
586 y = drawInfoRow(doc, 'ЕМБГ', student.embg, MARGIN, y);
587 y = drawInfoRow(doc, 'Студиска програма', enrollment.program, MARGIN, y);
588 y = drawInfoRow(doc, 'Циклус', enrollment.cycle, MARGIN, y);
589 y = drawInfoRow(doc, 'Статус', enrollment.status, MARGIN, y);
590 y = drawInfoRow(doc, 'Година на упис', String(enrollment.enrollmentYear), MARGIN, y);
591 y += 8;
592
593 // Separator
594 doc.setDrawColor(200, 200, 200);
595 doc.setLineWidth(0.3);
596 doc.line(MARGIN, y, PAGE_WIDTH - MARGIN, y);
597 y += 8;
598
599 // Body text
600 doc.setFont('Roboto', 'normal');
601 doc.setFontSize(10);
602 for (const line of bodyLines) {
603 if (line === '') {
604 y += 5;
605 } else {
606 const wrapped = doc.splitTextToSize(line, CONTENT_WIDTH);
607 for (const wl of wrapped) {
608 doc.text(wl, MARGIN, y);
609 y += 5.5;
610 }
611 }
612 }
613
614 drawFooter(doc, archiveNumber, date);
615 doc.save(`${filename}_${student.index}.pdf`);
616}
617
618// --- Administrative Regulation ---
619export async function generateAdministrativeRegulation(
620 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
621): Promise<void> {
622 await generateRequestDocument(student, enrollment,
623 'БАРАЊЕ ЗА АДМИНИСТРАТИВНО РЕГУЛИРАЊЕ НА РЕТРОАКТИВЕН СЕМЕСТАР',
624 [
625 `До: Деканат на Факултетот за информатички науки и компјутерско инженерство`,
626 '',
627 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
628 `запишан/а на студиската програма ${enrollment.program}, ${enrollment.cycle} циклус студии,`,
629 `со ова барање побарувам административно регулирање на ретроактивен семестар.`,
630 '',
631 `Молам да ми се одобри административна регулација на семестар кој не е запишан во предвидениот рок,`,
632 `согласно одлуката на Факултетот и важечката регулатива.`,
633 '',
634 `Прилози: Уплатница за административна такса од 1000 ден.`,
635 ],
636 archiveNumber, date, 'Административно_регулирање');
637}
638
639// --- Late Exam Registration ---
640export async function generateExamApplicationSatisfaction(
641 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
642): Promise<void> {
643 await generateRequestDocument(student, enrollment,
644 'БАРАЊЕ ЗА ЗАДОЧНО ПРИЈАВУВАЊЕ НА ИСПИТ',
645 [
646 `До: Деканат на ФИНКИ`,
647 '',
648 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
649 `запишан/а на студиската програма ${enrollment.program},`,
650 `побарувам задочно пријавување на испит кој не е пријавен во предвидениот рок.`,
651 '',
652 `Причина за задоцнување: ___________________________________`,
653 '',
654 `Молам за одобрување на ова барање.`,
655 '',
656 `Прилози: Уплатница за административна такса од 1000 ден.`,
657 ],
658 archiveNumber, date, 'Задочно_пријавување_испит');
659}
660
661// --- Semester Enrollment After Deadline ---
662export async function generateSemesterEnrollmentAfterDeadline(
663 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
664): Promise<void> {
665 await generateRequestDocument(student, enrollment,
666 'БАРАЊЕ ЗА ЗАПИШУВАЊЕ НА СЕМЕСТАР ПО ИСТЕК НА РОК',
667 [
668 `До: Деканат на ФИНКИ`,
669 '',
670 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
671 `побарувам да ми се одобри запишување на нареден семестар по истекот на официјалниот рок.`,
672 '',
673 `Причина за задоцнето запишување: ___________________________________`,
674 '',
675 `Молам за одобрување.`,
676 '',
677 `Прилози: Уплатница за административна такса од 1500 ден.`,
678 ],
679 archiveNumber, date, 'Запишување_семестар_по_рок');
680}
681
682// --- Certificate Issuance ---
683export async function generateCertificateIssuanceVarious(
684 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
685): Promise<void> {
686 await generateRequestDocument(student, enrollment,
687 'БАРАЊЕ ЗА ИЗДАВАЊЕ НА ПОТВРДИ ПО РАЗНИ ОСНОВИ',
688 [
689 `До: Деканат на ФИНКИ`,
690 '',
691 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
692 `побарувам издавање на потврда за следната намена:`,
693 '',
694 `Намена: ___________________________________`,
695 '',
696 `Молам да ми биде издадена бараната потврда.`,
697 '',
698 `Прилози: Уплатница за административна такса од 1500 ден.`,
699 ],
700 archiveNumber, date, 'Издавање_потврди');
701}
702
703// --- Study Suspension ---
704export async function generateStudySuspension(
705 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
706): Promise<void> {
707 await generateRequestDocument(student, enrollment,
708 'БАРАЊЕ ЗА МИРУВАЊЕ НА СТУДИИТЕ',
709 [
710 `До: Деканат на ФИНКИ`,
711 '',
712 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
713 `запишан/а на студиската програма ${enrollment.program},`,
714 `побарувам мирување на студиите за период од ______ семестри.`,
715 '',
716 `Причина за мирување: ___________________________________`,
717 '',
718 `Изјавувам дека сум запознаен/а со правилата за продолжување на студиите`,
719 `по истекот на периодот на мирување.`,
720 '',
721 `Прилози: Уплатница за административна такса од 2000 ден.`,
722 ],
723 archiveNumber, date, 'Мирување_студии');
724}
725
726// --- Diploma Thesis Cancellation ---
727export async function generateDiplomaThesisCancellation(
728 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
729): Promise<void> {
730 await generateRequestDocument(student, enrollment,
731 'БАРАЊЕ ЗА ОТКАЖУВАЊЕ НА ПРИЈАВЕНА ТЕМА ЗА ДИПЛОМСКА РАБОТА',
732 [
733 `До: Деканат на ФИНКИ`,
734 '',
735 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
736 `побарувам откажување на претходно пријавената тема за дипломска работа.`,
737 '',
738 `Назив на темата: ___________________________________`,
739 `Ментор: ___________________________________`,
740 '',
741 `Причина за откажување: ___________________________________`,
742 '',
743 `Прилози: Уплатница за административна такса од 1000 ден.`,
744 ],
745 archiveNumber, date, 'Откажување_дипломска');
746}
747
748// --- Graduation Package ---
749export async function generateDiplomaPackage(
750 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
751): Promise<void> {
752 await generateRequestDocument(student, enrollment,
753 'БАРАЊЕ ЗА ПАКЕТ ЗА ДИПЛОМИРАЊЕ',
754 [
755 `До: Деканат на ФИНКИ`,
756 '',
757 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
758 `запишан/а на студиската програма ${enrollment.program}, ${enrollment.cycle} циклус,`,
759 `побарувам издавање на пакет за дипломирање.`,
760 '',
761 `Со ова потврдувам дека ги имам исполнето сите услови за дипломирање`,
762 `согласно студиската програма и правилникот на Факултетот.`,
763 '',
764 `Пакетот за дипломирање ги вклучува: диплома, додаток на диплома и свечена промоција.`,
765 '',
766 `Прилози: Уплатница за административна такса од 6200 ден.`,
767 ],
768 archiveNumber, date, 'Пакет_дипломирање');
769}
770
771// --- Exam Cancellation ---
772export async function generateExamCancellation(
773 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
774): Promise<void> {
775 await generateRequestDocument(student, enrollment,
776 'БАРАЊЕ ЗА ПОНИШТУВАЊЕ НА ИСПИТ',
777 [
778 `До: Деканат на ФИНКИ`,
779 '',
780 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
781 `побарувам поништување на испит по предметот:`,
782 '',
783 `Предмет: ___________________________________`,
784 `Датум на полагање: ___________________________________`,
785 '',
786 `Причина за поништување: ___________________________________`,
787 '',
788 `Прилози: Уплатница за административна такса од 2000 ден.`,
789 ],
790 archiveNumber, date, 'Поништување_испит');
791}
792
793// --- Study Continuation ---
794export async function generateStudyContinuation(
795 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
796): Promise<void> {
797 await generateRequestDocument(student, enrollment,
798 'БАРАЊЕ ЗА ПРОДОЛЖУВАЊЕ НА СТУДИИ ВО МИРУВАЊЕ',
799 [
800 `До: Деканат на ФИНКИ`,
801 '',
802 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
803 `побарувам продолжување на студиите по периодот на мирување.`,
804 '',
805 `Период на мирување: од ____________ до ____________`,
806 '',
807 `Молам да ми се одобри продолжување на студиите согласно тековната`,
808 `студиска програма и важечкиот правилник.`,
809 '',
810 `Прилози: Уплатница за административна такса од 2000 ден.`,
811 ],
812 archiveNumber, date, 'Продолжување_студии');
813}
814
815// --- Elective Subject Change ---
816export async function generateElectiveSubjectChange(
817 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
818): Promise<void> {
819 await generateRequestDocument(student, enrollment,
820 'БАРАЊЕ ЗА ПРОМЕНА НА ИЗБОРЕН ПРЕДМЕТ',
821 [
822 `До: Деканат на ФИНКИ`,
823 '',
824 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
825 `побарувам промена на изборен предмет.`,
826 '',
827 `Предмет што се менува: ___________________________________`,
828 `Нов изборен предмет: ___________________________________`,
829 '',
830 `Причина за промена: ___________________________________`,
831 '',
832 `Прилози: Уплатница за административна такса од 1500 ден.`,
833 ],
834 archiveNumber, date, 'Промена_изборен_предмет');
835}
836
837// --- Passed Subject Change ---
838export async function generatePassedSubjectChange(
839 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
840): Promise<void> {
841 await generateRequestDocument(student, enrollment,
842 'БАРАЊЕ ЗА ПРОМЕНА НА ПОЛОЖЕН ПРЕДМЕТ',
843 [
844 `До: Деканат на ФИНКИ`,
845 '',
846 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
847 `побарувам промена / корекција на запис за положен предмет.`,
848 '',
849 `Предмет: ___________________________________`,
850 `Датум на полагање: ___________________________________`,
851 `Оцена: ___________________________________`,
852 '',
853 `Детали за промена: ___________________________________`,
854 ],
855 archiveNumber, date, 'Промена_положен_предмет');
856}
857
858// --- Study Program Change (Same Accreditation) ---
859export async function generateProgramChangeSameAccreditation(
860 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
861): Promise<void> {
862 await generateRequestDocument(student, enrollment,
863 'БАРАЊЕ ЗА ПРОМЕНА НА СТУДИСКА ПРОГРАМА ОД ИСТА АКРЕДИТАЦИЈА',
864 [
865 `До: Деканат на ФИНКИ`,
866 '',
867 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
868 `тековно запишан/а на програмата ${enrollment.program},`,
869 `побарувам промена на студиска програма во рамки на истата акредитација.`,
870 '',
871 `Нова студиска програма: ___________________________________`,
872 '',
873 `Причина за промена: ___________________________________`,
874 '',
875 `Прилози: Уплатница за административна такса од 2000 ден.`,
876 ],
877 archiveNumber, date, 'Промена_програма_иста_акредитација');
878}
879
880// --- Study Program Change (New Accreditation) ---
881export async function generateProgramChangeNewAccreditation(
882 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
883): Promise<void> {
884 await generateRequestDocument(student, enrollment,
885 'БАРАЊЕ ЗА ПРОМЕНА НА СТУДИСКА ПРОГРАМА ОД ПОНОВА АКРЕДИТАЦИЈА',
886 [
887 `До: Деканат на ФИНКИ`,
888 '',
889 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
890 `тековно запишан/а на програмата ${enrollment.program},`,
891 `побарувам промена на студиска програма во рамки на нова акредитација.`,
892 '',
893 `Нова студиска програма: ___________________________________`,
894 `Нова акредитација: ___________________________________`,
895 '',
896 `Причина за промена: ___________________________________`,
897 '',
898 `Прилози: Уплатница за административна такса од 3000 ден.`,
899 ],
900 archiveNumber, date, 'Промена_програма_нова_акредитација');
901}
902
903// --- Diploma Supplement ---
904export async function generateDiplomaSupplement(
905 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
906): Promise<void> {
907 await generateRequestDocument(student, enrollment,
908 'ДОДАТОК НА ДИПЛОМА',
909 [
910 `До: Деканат на ФИНКИ`,
911 '',
912 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
913 `запишан/а на студиската програма ${enrollment.program}, ${enrollment.cycle} циклус,`,
914 `побарувам издавање на додаток на диплома.`,
915 '',
916 `Додатокот на дипломата се издава како придружен документ`,
917 `кон дипломата за завршените студии.`,
918 ],
919 archiveNumber, date, 'Додаток_диплома');
920}
921
922// --- Diploma Supplement Second Cycle ---
923export async function generateDiplomaSupplementSecondCycle(
924 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
925): Promise<void> {
926 await generateRequestDocument(student, enrollment,
927 'ДОДАТОК НА ДИПЛОМА (ВТОР ЦИКЛУС)',
928 [
929 `До: Деканат на ФИНКИ`,
930 '',
931 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
932 `запишан/а на студиската програма ${enrollment.program}, втор циклус на студии,`,
933 `побарувам издавање на додаток на диплома за вториот циклус студии.`,
934 '',
935 `Додатокот на дипломата се издава како придружен документ`,
936 `кон магистерската диплома.`,
937 ],
938 archiveNumber, date, 'Додаток_диплома_втор_циклус');
939}
940
941// --- MKSA ---
942export async function generateMKSA(
943 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
944): Promise<void> {
945 await generateRequestDocument(student, enrollment,
946 'МКСА - МАКЕДОНСКА КВАЛИФИКАЦИСКА РАМКА',
947 [
948 `До: Деканат на ФИНКИ`,
949 '',
950 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
951 `побарувам издавање на МКСА (Македонска Квалификациска Рамка) документ.`,
952 '',
953 `Студиска програма: ${enrollment.program}`,
954 `Циклус: ${enrollment.cycle}`,
955 '',
956 `Документот е потребен за целите на: ___________________________________`,
957 '',
958 `Прилози: Уплатница за административна такса од 750 ден.`,
959 ],
960 archiveNumber, date, 'МКСА');
961}
962
963// --- Exam Recognition ---
964export async function generateExamRecognition(
965 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
966): Promise<void> {
967 await generateRequestDocument(student, enrollment,
968 'ПРИЗНАВАЊЕ НА ПОЛОЖЕНИ ИСПИТИ',
969 [
970 `До: Деканат на ФИНКИ`,
971 '',
972 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
973 `побарувам признавање на положени испити од претходна институција / програма.`,
974 '',
975 `Претходна институција: ___________________________________`,
976 `Претходна програма: ___________________________________`,
977 '',
978 `Листа на предмети за признавање:`,
979 `1. ___________________________________`,
980 `2. ___________________________________`,
981 `3. ___________________________________`,
982 '',
983 `Прилози: Уверение за положени испити од претходна институција.`,
984 ],
985 archiveNumber, date, 'Признавање_испити');
986}
987
988// --- Student Card (Old) ---
989export async function generateStudentCardOld(
990 student: StudentInfo, enrollment: EnrollmentInfo, archiveNumber: string, date: string,
991): Promise<void> {
992 await generateRequestDocument(student, enrollment,
993 'СТУДЕНТСКИ КАРТОН (СТАРО)',
994 [
995 `До: Деканат на ФИНКИ`,
996 '',
997 `Јас, ${student.firstName} ${student.middleName} ${student.lastName}, студент/ка со индекс ${student.index},`,
998 `побарувам издавање на студентски картон (стар формат).`,
999 '',
1000 `Студиска програма: ${enrollment.program}`,
1001 `Година на упис: ${enrollment.enrollmentYear}`,
1002 '',
1003 `Молам да ми биде издаден студентски картон.`,
1004 ],
1005 archiveNumber, date, 'Студентски_картон');
1006}
1007
1008/* ==================================================================
1009 Main dispatcher — called from the Documents page
1010 ================================================================== */
1011
1012// Map document type IDs → generator function names
1013const DOCUMENT_TYPE_MAP: Record<string, string> = {
1014 'административно регулирање': 'administrative_regulation',
1015 'задочно пријавување': 'exam_application_satisfaction',
1016 'запишување на семестар по истек': 'semester_enrollment_after_deadline',
1017 'издавање потврди': 'certificate_issuance_various',
1018 'мирување на студиите': 'student_status_verification',
1019 'откажување на пријавена тема': 'diploma_thesis_postponement',
1020 'пакет за дипломирање': 'diploma_package',
1021 'поништување на испит': 'exam_postponement',
1022 'потврда за редовен студент': 'regular_student_certificate',
1023 'продолжување на студии во мирување': 'study_continuation_verification',
1024 'промена на изборен предмет': 'failed_subject_grade_change',
1025 'промена на положен предмет': 'passed_subject_grade_change',
1026 'промена на студиска програма од иста': 'study_program_change_same_accreditation',
1027 'промена на студиска програма од понова': 'study_program_change_new_accreditation',
1028 'додаток на диплома (втор циклус)': 'diploma_supplement_second_cycle',
1029 'додаток на диплома': 'diploma_supplement',
1030 'мкса': 'mksa',
1031 'признавање на положени испити': 'exam_recognition',
1032 'студентски картон': 'student_card_old',
1033};
1034
1035function identifyDocumentType(request: string): string | null {
1036 const lower = request.toLowerCase();
1037
1038 // Exact-match patterns (check longer patterns first)
1039 if (lower.includes('положени испити')) return 'passed_exams_certificate';
1040 if (lower.includes('уппи')) return 'uppi_form';
1041 if (lower.includes('редовен студент')) return 'regular_student_certificate';
1042
1043 // Match by document type keywords
1044 for (const [keyword, docType] of Object.entries(DOCUMENT_TYPE_MAP)) {
1045 if (lower.includes(keyword)) return docType;
1046 }
1047
1048 return null;
1049}
1050
1051export async function downloadDocumentPDF(
1052 documentRequest: string,
1053 archiveNumber: string,
1054 documentDate: string,
1055 accessToken: string,
1056): Promise<void> {
1057 // Fetch student profile
1058 const profileRes = await fetch('https://iknow-api.onrender.com/api/user/getUser', {
1059 headers: { Authorization: `Bearer ${accessToken}` },
1060 });
1061 if (!profileRes.ok) throw new Error('Не може да се вчита профилот на студентот');
1062
1063 const profile = await profileRes.json();
1064 const { personalInfo, enrollmentInfo, birthInfo, contact, previousEducation } = profile;
1065
1066 const student: StudentInfo = {
1067 firstName: personalInfo.firstName,
1068 middleName: personalInfo.middleName,
1069 lastName: personalInfo.lastName,
1070 index: personalInfo.index,
1071 embg: personalInfo.embg,
1072 dateOfBirth: personalInfo.dateOfBirth,
1073 gender: personalInfo.gender,
1074 nationality: personalInfo.nationality,
1075 citizenship: personalInfo.citizenship,
1076 currentPlan: personalInfo.currentPlan,
1077 registryNumber: personalInfo.registryNumber,
1078 studyGroup: personalInfo.studyGroup,
1079 scholarship: personalInfo.scholarship,
1080 };
1081
1082 const enrollment: EnrollmentInfo = {
1083 enrollmentYear: enrollmentInfo.enrollmentYear,
1084 status: enrollmentInfo.status,
1085 cycle: enrollmentInfo.cycle,
1086 program: enrollmentInfo.program,
1087 quota: enrollmentInfo.quota,
1088 };
1089
1090 const docType = identifyDocumentType(documentRequest);
1091
1092 // Types that need passed exams data
1093 const needsExams = docType === 'passed_exams_certificate' || docType === 'uppi_form' || docType === 'regular_student_certificate';
1094
1095 let passedExams: PassedExam[] = [];
1096 if (needsExams) {
1097 const examsRes = await fetch('https://iknow-api.onrender.com/api/user/getPassedSubjects', {
1098 headers: { Authorization: `Bearer ${accessToken}` },
1099 });
1100 if (examsRes.ok) {
1101 const examsData = await examsRes.json();
1102 passedExams = (examsData?.passedSubjects ?? []).map((s: Record<string, unknown>) => ({
1103 id: s.id as number,
1104 code: s.code as string,
1105 subject: s.subject as string,
1106 credits: s.credits as number,
1107 grade: s.grade as number,
1108 date: s.date as string,
1109 semester: s.semester as string,
1110 professor: (s.professor as string) || '—',
1111 }));
1112 }
1113 }
1114
1115 switch (docType) {
1116 case 'passed_exams_certificate':
1117 await generatePassedExamsCertificate(student, enrollment, passedExams, archiveNumber, documentDate);
1118 break;
1119 case 'uppi_form': {
1120 const birth: BirthInfo = {
1121 placeOfBirth: birthInfo.placeOfBirth,
1122 municipalityOfBirth: birthInfo.municipalityOfBirth,
1123 country: birthInfo.country,
1124 };
1125 const contactInfo: ContactInfo = {
1126 placeOfResidence: contact.placeOfResidence,
1127 municipalityOfResidence: contact.municipalityOfResidence,
1128 address: contact.address,
1129 phone: contact.phone,
1130 mobilePhone: contact.mobilePhone,
1131 email: contact.email,
1132 };
1133 const prevEdu: PreviousEducation = {
1134 type: previousEducation.type,
1135 profession: previousEducation.profession,
1136 average: previousEducation.average,
1137 language: previousEducation.language,
1138 country: previousEducation.country,
1139 previousUniversity: previousEducation.previousUniversity,
1140 previousFaculty: previousEducation.previousFaculty,
1141 };
1142 await generateUPPIForm(student, enrollment, birth, contactInfo, prevEdu, passedExams, archiveNumber, documentDate);
1143 break;
1144 }
1145 case 'regular_student_certificate':
1146 await generateRegularStudentCertificate(student, enrollment, passedExams, archiveNumber, documentDate);
1147 break;
1148 case 'administrative_regulation':
1149 await generateAdministrativeRegulation(student, enrollment, archiveNumber, documentDate);
1150 break;
1151 case 'exam_application_satisfaction':
1152 await generateExamApplicationSatisfaction(student, enrollment, archiveNumber, documentDate);
1153 break;
1154 case 'semester_enrollment_after_deadline':
1155 await generateSemesterEnrollmentAfterDeadline(student, enrollment, archiveNumber, documentDate);
1156 break;
1157 case 'certificate_issuance_various':
1158 await generateCertificateIssuanceVarious(student, enrollment, archiveNumber, documentDate);
1159 break;
1160 case 'student_status_verification':
1161 await generateStudySuspension(student, enrollment, archiveNumber, documentDate);
1162 break;
1163 case 'diploma_thesis_postponement':
1164 await generateDiplomaThesisCancellation(student, enrollment, archiveNumber, documentDate);
1165 break;
1166 case 'diploma_package':
1167 await generateDiplomaPackage(student, enrollment, archiveNumber, documentDate);
1168 break;
1169 case 'exam_postponement':
1170 await generateExamCancellation(student, enrollment, archiveNumber, documentDate);
1171 break;
1172 case 'study_continuation_verification':
1173 await generateStudyContinuation(student, enrollment, archiveNumber, documentDate);
1174 break;
1175 case 'failed_subject_grade_change':
1176 await generateElectiveSubjectChange(student, enrollment, archiveNumber, documentDate);
1177 break;
1178 case 'passed_subject_grade_change':
1179 await generatePassedSubjectChange(student, enrollment, archiveNumber, documentDate);
1180 break;
1181 case 'study_program_change_same_accreditation':
1182 await generateProgramChangeSameAccreditation(student, enrollment, archiveNumber, documentDate);
1183 break;
1184 case 'study_program_change_new_accreditation':
1185 await generateProgramChangeNewAccreditation(student, enrollment, archiveNumber, documentDate);
1186 break;
1187 case 'diploma_supplement':
1188 await generateDiplomaSupplement(student, enrollment, archiveNumber, documentDate);
1189 break;
1190 case 'diploma_supplement_second_cycle':
1191 await generateDiplomaSupplementSecondCycle(student, enrollment, archiveNumber, documentDate);
1192 break;
1193 case 'mksa':
1194 await generateMKSA(student, enrollment, archiveNumber, documentDate);
1195 break;
1196 case 'exam_recognition':
1197 await generateExamRecognition(student, enrollment, archiveNumber, documentDate);
1198 break;
1199 case 'student_card_old':
1200 await generateStudentCardOld(student, enrollment, archiveNumber, documentDate);
1201 break;
1202 default:
1203 await generateGenericDocument(student, enrollment, documentRequest, archiveNumber, documentDate);
1204 break;
1205 }
1206}
Note: See TracBrowser for help on using the repository browser.