[docx] Generate SegOpMaqPes certificate

master
Fernando 2023-06-09 11:23:42 -05:00
parent dcc27291c0
commit ac20c9391f
18 changed files with 360 additions and 43 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
img/logo_hazescorp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
img/mundo_maq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -10,20 +10,32 @@ import { PersonService } from "./controller/person/person.service";
import { SubjectController } from "./controller/subject/subject.controller";
import { SubjectService } from "./controller/subject/subject.service";
import * as dotenv from "dotenv";
import { MigratorController } from "./controller/migrator/migrator.controller";
// Must be done before initializing DB.
console.log(dotenv.config());
const user = process.env.MY_SQL_USER;
const host = process.env.MY_SQL_HOST;
const port = parseInt(process.env.MY_SQL_PORT ?? "3306", 10);
const password = process.env.MY_SQL_PASSWORD;
const db = process.env.MY_SQL_DB;
@Module({
imports: [
TypeOrmModule.forRoot({
type: "mysql",
host: "localhost",
// To allow remote connections a manual URI is used, for some
url: `mysql://${user}:${password}@${host}:${port}/${db}`,
// These values are rejected when using a remote connection, for some reason
/*
host: process.env.MY_SQL_HOST,
port: parseInt(process.env.MY_SQL_PORT ?? "3306", 10),
username: process.env.MY_SQL_USER,
password: process.env.MY_SQL_PASSWORD,
database: process.env.MY_SQL_DB,
*/
entities: [
Persona,
CursoGIE,

View File

@ -145,6 +145,7 @@ const photoSection = new Paragraph({
export async function _4X4Cert(props: CertData<null>): Promise<Buffer> {
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.04,
width: 2.04,

View File

@ -35,4 +35,8 @@ export type CertData<T> = {
* E.g.: "23"
*/
certDay: string,
/**
* Id of the certificate
*/
certIId: number,
}

View File

@ -148,6 +148,7 @@ const photoSection = new Paragraph({
export async function manejoDefensivoCert(props: CertData<null>): Promise<Buffer> {
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.45,
width: 2.45,

View File

@ -132,6 +132,7 @@ const photoSection = new Paragraph({
export async function mecanicaBasicaCert(props: CertData<null>): Promise<Buffer> {
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.2,
width: 2.2,

253
src/certs/SEG_OP_MAQ_PES.ts Normal file
View File

@ -0,0 +1,253 @@
import {
Document, Packer, Paragraph, PageOrientation,
FrameAnchorType,
TextRun,
AlignmentType,
BorderStyle,
} from "docx";
import { cmText, createSimpleText, getImage, getQR } from "./utils";
import { CertData } from "./CertData";
const imgFondoDoc = getImage({
name: "fondo_seg_op_maq_pes.png",
height: 21.02,
width: 29.63,
horizontalOffset: 0.03,
verticalOffset: 0.04,
behindDocument: true,
});
const imgCIP = getImage({
name: "colegio_ingenieros_logo.png",
height: 2.02,
width: 2.02,
horizontalOffset: 25.53,
verticalOffset: 14.17,
});
const imgMundomaq = getImage({
name: "mundo_maq.png",
height: 4.82,
width: 6.73,
horizontalOffset: 20.87,
verticalOffset: 0.9,
});
const imgHazescorp = getImage({
name: "logo_hazescorp.png",
height: 2.95,
width: 3.33,
horizontalOffset: 0.4,
verticalOffset: 17.73,
});
const imgEEG = getImage({
name: "eeg_logo.png",
height: 2.38,
width: 4.94,
horizontalOffset: 1.66,
verticalOffset: 1.6,
});
// SEGURIDAD EN LA OPERACIÓN DE MAQUINARIA PESADA
const tCourse = createSimpleText({
xPosition: 5.5,
yPosition: 6.65,
width: 12.83,
height: 1.5,
text: "SEGURIDAD EN LA OPERACIÓN DE MAQUINARIA PESADA",
size: 44,
bold: true,
});
// En temas de...
const tTopics = createSimpleText({
xPosition: 1.65,
yPosition: 8.9,
width: 20.42,
height: 1.5,
text: "En los temas de: Seguridad en maquinaria pesada, IPERC, Prevención de riesgos con Maquinas, Medidas de prevención con máquinas en movimientos de tierra, Seguridad en la operación, Procedimiento de mantenimiento, Correcto llenado de IPERC, Correcto llenado de Check List, Mantenimiento Preventivo, predictivo, correctivo.",
size: 22,
font: "Arial",
alignment: AlignmentType.JUSTIFIED,
});
// Con una duracion...
const tHours = createSimpleText({
xPosition: 1.65,
yPosition: 10.7,
width: 13.15,
height: 0.5,
text: "Con una duración de 36 horas lectivas",
size: 22,
font: "Arial",
alignment: AlignmentType.LEFT,
});
// Se expide certificado...
const tFinishLabel = createSimpleText({
xPosition: 5.3,
yPosition: 11.5,
width: 13.15,
height: 0.5,
text: "Se expide el presente certificado para los fines que se estime conveniente",
size: 22,
font: "Arial",
italics: true,
alignment: AlignmentType.CENTER,
});
// Recuadro de foto
const photoSection = new Paragraph({
frame: {
position: {
x: cmText(22.62),
y: cmText(7),
},
height: cmText(3.57),
width: cmText(2.81),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [],
border: {
top: {
style: BorderStyle.DASHED,
},
bottom: {
style: BorderStyle.DASHED,
},
left: {
style: BorderStyle.DASHED,
},
right: {
style: BorderStyle.DASHED,
},
},
alignment: AlignmentType.LEFT,
});
export async function segOpMaqPesCert(props: CertData<null>): Promise<Buffer> {
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.01,
width: 2.01,
horizontalOffset: 25.53,
verticalOffset: 16.43,
});
// FERNANDO ARAOZ
const tName = createSimpleText({
xPosition: 0,
yPosition: 4.15,
width: 23.56,
height: 1.5,
text: props.personFullName,
size: 52,
bold: true,
underline: {},
});
// Identificado con DNI...
const tContentPart1 = new Paragraph({
frame: {
position: {
x: cmText(1.4),
y: cmText(5.9),
},
width: cmText(20.92),
height: cmText(0.6),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [
new TextRun({
text: "Identificado con DNI N° ",
font: "Arial",
size: 22,
}),
new TextRun({
text: props.personDni,
font: "Arial",
size: 24,
bold: true,
}),
new TextRun({
text: ", al haber aprobado el curso de capacitación sobre:",
font: "Arial",
size: 22,
}),
],
alignment: AlignmentType.CENTER,
});
// Fecha de Emision: ...
const certificateDate = createSimpleText({
xPosition: 19.25,
yPosition: 16.2,
width: 5.87,
height: 0.5,
text: `Fecha de Emisión:\t${props.certDay} / ${props.certMonth} / ${props.certYear}`,
font: "Times New Roman",
size: 20,
alignment: AlignmentType.LEFT,
});
// N° XXXX-20XX-EEG
const tCertCode = createSimpleText({
xPosition: 22.15,
yPosition: 10.85,
width: 3.67,
height: 0.8,
text: `${props.certCode}-${props.certYear}-EEG`,
size: 20,
alignment: AlignmentType.CENTER,
});
const doc = new Document({
sections: [
{
properties: {
page: {
size: {
orientation: PageOrientation.LANDSCAPE,
},
},
},
children: [
tName,
tContentPart1,
tCourse,
tTopics,
tHours,
tFinishLabel,
certificateDate,
photoSection,
tCertCode,
new Paragraph({
children: [
imgFondoDoc,
imgQR,
imgCIP,
imgMundomaq,
imgHazescorp,
imgEEG,
],
}),
],
},
],
});
// Return document as a buffer
return await Packer.toBuffer(doc);
}

View File

@ -52,6 +52,8 @@ export function getImage(data: ImgConfig): ImageRun {
}
export async function getQR(data : {
/** ID of the entity in the DB */
iid: number,
dni: string,
height: number,
width: number,
@ -59,7 +61,7 @@ export async function getQR(data : {
verticalOffset: number,
behindDocument?: boolean,
}): Promise<ImageRun> {
const qr = await QR.toDataURL(`https://www.eegsac.com/alumnoscertificados.php?DNI=${data.dni}`, {margin: 1});
const qr = await QR.toDataURL(`https://www.eegsac.com/alumnoscertificados.php?DNI=${data.dni}&iid=${data.iid}`, {margin: 1});
return new ImageRun({
data: qr,

View File

@ -10,8 +10,9 @@ import { getMatpel } from "./generator/matpel";
import { getMecanicaBasica } from "./generator/mecanicaBasica";
import { getManejoDefensivo } from "./generator/manejoDefensivo";
import { get4x4 } from "./generator/4x4";
import { getSegOpMaqPesada } from "./generator/segOpMaqPes";
const generatable = [1, 2, 3, 10, 11, 12];
const generatable = [1, 2, 3, 10, 11, 12, 72];
@Injectable()
export class CertificateService {
@ -136,6 +137,16 @@ export class CertificateService {
certDay,
);
}
// Seguridad en la Operacion de Maquinaria Pesada
case 72: {
return await getSegOpMaqPesada(
person,
register,
certYear,
certMonth,
certDay,
);
}
default: {
throw new BadRequestException("Curso no soportado");
}

View File

@ -20,6 +20,7 @@ export async function get4x4(
certYear,
certMonth,
certDay,
certIId: register.id,
};
return [await _4X4Cert(data), `4X4 - ${personFullName.toUpperCase()}.docx`];

View File

@ -20,6 +20,7 @@ export async function getManejoDefensivo(
certYear,
certMonth,
certDay,
certIId: register.id,
};
return [await manejoDefensivoCert(data), `MANEJO DEFENSIVO - ${personFullName.toUpperCase()}.docx`];

View File

@ -22,6 +22,7 @@ export async function getMatpel(
certYear,
certMonth,
certDay,
certIId: register.id,
};
return [await matpelCert(data), `${getMatpelFileName(matpel)} - ${personFullName.toUpperCase()}.docx`];

View File

@ -20,6 +20,7 @@ export async function getMecanicaBasica(
certYear,
certMonth,
certDay,
certIId: register.id,
};
return [await mecanicaBasicaCert(data), `MECANICA BASICA - ${personFullName.toUpperCase()}.docx`];

View File

@ -0,0 +1,27 @@
import { CertData } from "src/certs/CertData";
import { segOpMaqPesCert } from "src/certs/SEG_OP_MAQ_PES";
import { Persona } from "src/model/Persona/persona.entity";
import { RegistroGIE } from "src/model/RegistroGIE/registroGIE.entity";
export async function getSegOpMaqPesada(
person: Persona,
register: RegistroGIE,
certYear: string,
certMonth: string,
certDay: string
): Promise<[Buffer, string]> {
const personFullName = `${person.nombres} ${person.apellidoPaterno} ${person.apellidoMaterno}`;
const data: CertData<null> = {
matpel: null,
personFullName: personFullName,
personDni: register.dni,
certCode: register.codigo.toString().padStart(4, "0"),
certYear,
certMonth,
certDay,
certIId: register.id,
};
return [await segOpMaqPesCert(data), `SEGURIDAD OPERACION MAQUINARIA PESADA - ${personFullName.toUpperCase()}.docx`];
}

View File

@ -87,6 +87,7 @@ export function Registers(props: { person: Person | null, lastUpdate: number })
function Register(props: { cert: RegisterReturn, onUpdate: () => void }) {
const [deleteConfirmation, setDeleteConfirmation] = createSignal(false);
const [deleteText, setDeleteText] = createSignal("Eliminar");
const [downloading, setDownloading] = createSignal(false);
const deleteRegister = async() => {
if (deleteConfirmation()) {
@ -113,6 +114,7 @@ function Register(props: { cert: RegisterReturn, onUpdate: () => void }) {
};
const getCertificate = async() => {
setDownloading(true);
const response = await fetch(`/certificate/generate/${props.cert.id}`, {
method: "POST",
});
@ -141,13 +143,12 @@ function Register(props: { cert: RegisterReturn, onUpdate: () => void }) {
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
return;
} else {
const json = await response.json();
alert(json);
}
setDownloading(false);
};
return (

View File

@ -91,8 +91,7 @@ export function RegisterPerson(props: {dni: string, onSuccess: () => void}) {
<br/>
<br/>
<div class="grid grid-cols-2 w-1/2">
<div>
<label for="add-person-apellido-paterno">Apellido Paterno</label>
<br/>
<input
@ -108,8 +107,10 @@ export function RegisterPerson(props: {dni: string, onSuccess: () => void}) {
onChange={(e) => setApellidoP(e.target.value.toUpperCase())}
disabled={loading()}
/>
</div>
<div>
<br />
<br />
<label for="add-person-apellido-paterno">Apellido Materno</label>
<br/>
<input
@ -125,8 +126,7 @@ export function RegisterPerson(props: {dni: string, onSuccess: () => void}) {
onChange={(e) => setApellidoM(e.target.value.toUpperCase())}
disabled={loading()}
/>
</div>
</div>
<br/>
<br />
<input