diff --git a/api-v3/src/inspection-reports/inspection-document-delivery.service.ts b/api-v3/src/inspection-reports/inspection-document-delivery.service.ts index d855648..7b92038 100644 --- a/api-v3/src/inspection-reports/inspection-document-delivery.service.ts +++ b/api-v3/src/inspection-reports/inspection-document-delivery.service.ts @@ -10,7 +10,7 @@ import { InspectionActPdfService } from './inspection-act-pdf.service'; import { InspectionReportWordService } from './inspection-report-word.service'; import { SmtpDeliveryService } from './smtp-delivery.service'; -export type DeliveryRecipientKind = 'COMPANY' | 'OFFICE' | 'DIRECTOR' | 'INSPECTOR'; +export type DeliveryRecipientKind = 'COMPANY' | 'OFFICE' | 'INSPECTOR'; export interface DeliveryRow { id: string; @@ -40,21 +40,14 @@ export class InspectionDocumentDeliveryService { async settings() { const [row] = await this.dataSource.query(` - SELECT - office_email AS "officeEmail", - director_email AS "directorEmail", - updated_at AS "updatedAt" + SELECT office_email AS "officeEmail",updated_at AS "updatedAt" FROM institutional_delivery_settings WHERE id=1 - `) as Array<{ - officeEmail: string | null; - directorEmail: string | null; - updatedAt: Date; - }>; + `) as Array<{ officeEmail: string | null; updatedAt: Date }>; return { ...row, - smtpConfigured: this.smtp.configured(), - mailFrom: this.config.get('MAIL_FROM') ?? null, + smtpConfigured: await this.smtp.configured(), + mailFrom: await this.smtp.fromAddress() ?? this.config.get('MAIL_FROM') ?? null, }; } @@ -66,9 +59,9 @@ export class InspectionDocumentDeliveryService { const before = await this.settings(); await this.dataSource.query(` UPDATE institutional_delivery_settings - SET office_email=$1,director_email=$2,updated_by=$3,updated_at=CURRENT_TIMESTAMP + SET office_email=$1,updated_by=$2,updated_at=CURRENT_TIMESTAMP WHERE id=1 - `, [dto.officeEmail ?? null, dto.directorEmail ?? null, principal.userId]); + `, [dto.officeEmail ?? null, principal.userId]); const after = await this.settings(); await this.audit.record({ ...administrationAuditContext(principal, request), @@ -84,33 +77,23 @@ export class InspectionDocumentDeliveryService { async list() { const data = await this.dataSource.query(` SELECT - d.id, - d.act_id AS "actId", - d.report_id AS "reportId", - d.document_kind AS "documentKind", - d.recipient_kind AS "recipientKind", - d.recipient_asset_id AS "recipientAssetId", - d.recipient_user_id AS "recipientUserId", - d.recipient_email AS "recipientEmail", - d.status, - d.attempts, - d.last_attempt_at AS "lastAttemptAt", - d.sent_at AS "sentAt", - d.provider_message_id AS "providerMessageId", - d.last_error AS "lastError", - d.created_at AS "createdAt", - a.code AS "actCode", - r.code AS "reportCode", + d.id,d.act_id AS "actId",d.report_id AS "reportId", + d.document_kind AS "documentKind",d.recipient_kind AS "recipientKind", + d.recipient_asset_id AS "recipientAssetId",d.recipient_user_id AS "recipientUserId", + d.recipient_email AS "recipientEmail",d.status,d.attempts, + d.last_attempt_at AS "lastAttemptAt",d.sent_at AS "sentAt", + d.provider_message_id AS "providerMessageId",d.last_error AS "lastError", + d.created_at AS "createdAt",a.code AS "actCode",r.code AS "reportCode", recipient.name AS "recipientAssetName", - CASE - WHEN recipient_user.id IS NULL THEN NULL - ELSE btrim(concat_ws(' ', recipient_user.first_name, recipient_user.last_name)) + CASE WHEN recipient_user.id IS NULL THEN NULL + ELSE btrim(concat_ws(' ',recipient_user.first_name,recipient_user.last_name)) END AS "recipientUserName" FROM inspection_document_deliveries d JOIN inspection_acts a ON a.id=d.act_id LEFT JOIN inspection_reports r ON r.id=d.report_id LEFT JOIN assets recipient ON recipient.id=d.recipient_asset_id LEFT JOIN users recipient_user ON recipient_user.id=d.recipient_user_id + WHERE d.recipient_kind<>'DIRECTOR' ORDER BY d.created_at DESC LIMIT 200 `); @@ -154,11 +137,9 @@ export class InspectionDocumentDeliveryService { async retryPending(principal: AuthPrincipal, request: RequestWithContext) { const rows = await this.dataSource.query(` - SELECT id - FROM inspection_document_deliveries - WHERE status<>'SENT' - ORDER BY created_at ASC - LIMIT 100 + SELECT id FROM inspection_document_deliveries + WHERE status<>'SENT' AND recipient_kind<>'DIRECTOR' + ORDER BY created_at ASC LIMIT 100 `) as Array<{ id: string }>; for (const item of rows) await this.retry(item.id, principal, request).catch(() => undefined); return { processed: rows.length }; @@ -166,10 +147,9 @@ export class InspectionDocumentDeliveryService { private async ensureRows(actId: string, reportId: string | null) { const [settings] = await this.dataSource.query(` - SELECT office_email AS "officeEmail",director_email AS "directorEmail" - FROM institutional_delivery_settings - WHERE id=1 - `) as Array<{ officeEmail: string | null; directorEmail: string | null }>; + SELECT office_email AS "officeEmail" + FROM institutional_delivery_settings WHERE id=1 + `) as Array<{ officeEmail: string | null }>; const companies = await this.dataSource.query(` SELECT DISTINCT company.id,profile.notification_email AS email @@ -185,9 +165,7 @@ export class InspectionDocumentDeliveryService { `, [actId]) as Array<{ id: string; email: string | null }>; const [inspector] = await this.dataSource.query(` - SELECT - inspector.id, - inspector.email + SELECT inspector.id,inspector.email FROM inspection_acts act JOIN inspection_visits visit ON visit.id=act.visit_id JOIN users inspector ON inspector.id=visit.lead_inspector_user_id @@ -196,52 +174,32 @@ export class InspectionDocumentDeliveryService { for (const company of companies) { await this.upsertRow({ - actId, - reportId, - documentKind: 'ACT_PDF', - recipientKind: 'COMPANY', - recipientAssetId: company.id, - recipientUserId: null, - recipientKey: company.id, - recipientEmail: company.email, + actId,reportId,documentKind:'ACT_PDF',recipientKind:'COMPANY', + recipientAssetId:company.id,recipientUserId:null,recipientKey:company.id, + recipientEmail:company.email, }); } await this.upsertRow({ - actId, - reportId, - documentKind: 'ACT_PDF', - recipientKind: 'OFFICE', - recipientAssetId: null, - recipientUserId: null, - recipientKey: '00000000-0000-0000-0000-000000000000', - recipientEmail: settings?.officeEmail ?? null, + actId,reportId,documentKind:'ACT_PDF',recipientKind:'OFFICE', + recipientAssetId:null,recipientUserId:null, + recipientKey:'00000000-0000-0000-0000-000000000000', + recipientEmail:settings?.officeEmail ?? null, }); if (inspector) { await this.upsertRow({ - actId, - reportId, - documentKind: 'ACT_PDF', - recipientKind: 'INSPECTOR', - recipientAssetId: null, - recipientUserId: inspector.id, - recipientKey: inspector.id, - recipientEmail: inspector.email, - }); - } - - if (reportId) { - await this.upsertRow({ - actId, - reportId, - documentKind: 'REPORT_WORD', - recipientKind: 'DIRECTOR', - recipientAssetId: null, - recipientUserId: null, - recipientKey: '00000000-0000-0000-0000-000000000000', - recipientEmail: settings?.directorEmail ?? null, + actId,reportId,documentKind:'ACT_PDF',recipientKind:'INSPECTOR', + recipientAssetId:null,recipientUserId:inspector.id,recipientKey:inspector.id, + recipientEmail:inspector.email, }); + if (reportId) { + await this.upsertRow({ + actId,reportId,documentKind:'REPORT_WORD',recipientKind:'INSPECTOR', + recipientAssetId:null,recipientUserId:inspector.id,recipientKey:inspector.id, + recipientEmail:inspector.email, + }); + } } } @@ -267,31 +225,21 @@ export class InspectionDocumentDeliveryService { recipient_user_id=COALESCE(EXCLUDED.recipient_user_id,inspection_document_deliveries.recipient_user_id), recipient_email=CASE WHEN inspection_document_deliveries.status='SENT' THEN inspection_document_deliveries.recipient_email - ELSE EXCLUDED.recipient_email - END, + ELSE EXCLUDED.recipient_email END, status=CASE WHEN inspection_document_deliveries.status='SENT' THEN 'SENT' WHEN EXCLUDED.recipient_email IS NULL THEN 'WAITING_RECIPIENT' - ELSE inspection_document_deliveries.status - END, + ELSE inspection_document_deliveries.status END, updated_at=CURRENT_TIMESTAMP `, [ - input.actId, - input.reportId, - input.documentKind, - input.recipientKind, - input.recipientAssetId, - input.recipientUserId, - input.recipientKey, - input.recipientEmail, - initialStatus, + input.actId,input.reportId,input.documentKind,input.recipientKind, + input.recipientAssetId,input.recipientUserId,input.recipientKey,input.recipientEmail,initialStatus, ]); } private async rowsForAct(actId: string): Promise { return this.dataSource.query(` - SELECT - d.id,d.act_id AS "actId",d.report_id AS "reportId", + SELECT d.id,d.act_id AS "actId",d.report_id AS "reportId", d.document_kind AS "documentKind",d.recipient_kind AS "recipientKind", d.recipient_asset_id AS "recipientAssetId",d.recipient_user_id AS "recipientUserId", d.recipient_email AS "recipientEmail",d.status,d.attempts, @@ -299,15 +247,14 @@ export class InspectionDocumentDeliveryService { FROM inspection_document_deliveries d JOIN inspection_acts a ON a.id=d.act_id LEFT JOIN inspection_reports r ON r.id=d.report_id - WHERE d.act_id=$1 + WHERE d.act_id=$1 AND d.recipient_kind<>'DIRECTOR' ORDER BY d.created_at `, [actId]) as Promise; } private async load(id: string): Promise { const [row] = await this.dataSource.query(` - SELECT - d.id,d.act_id AS "actId",d.report_id AS "reportId", + SELECT d.id,d.act_id AS "actId",d.report_id AS "reportId", d.document_kind AS "documentKind",d.recipient_kind AS "recipientKind", d.recipient_asset_id AS "recipientAssetId",d.recipient_user_id AS "recipientUserId", d.recipient_email AS "recipientEmail",d.status,d.attempts, @@ -315,12 +262,11 @@ export class InspectionDocumentDeliveryService { FROM inspection_document_deliveries d JOIN inspection_acts a ON a.id=d.act_id LEFT JOIN inspection_reports r ON r.id=d.report_id - WHERE d.id=$1 + WHERE d.id=$1 AND d.recipient_kind<>'DIRECTOR' `, [id]) as DeliveryRow[]; if (!row) { throw new NotFoundException({ - code: 'DOCUMENT_DELIVERY_NOT_FOUND', - message: 'Entrega documental no encontrada', + code:'DOCUMENT_DELIVERY_NOT_FOUND',message:'Entrega documental no encontrada', }); } return row; @@ -330,47 +276,37 @@ export class InspectionDocumentDeliveryService { let email: string | null = null; if (row.recipientKind === 'COMPANY' && row.recipientAssetId) { const [company] = await this.dataSource.query(` - SELECT notification_email AS email - FROM organization_profiles - WHERE asset_id=$1 + SELECT notification_email AS email FROM organization_profiles WHERE asset_id=$1 `, [row.recipientAssetId]) as Array<{ email: string | null }>; email = company?.email ?? null; } else if (row.recipientKind === 'INSPECTOR' && row.recipientUserId) { const [inspector] = await this.dataSource.query(` - SELECT email - FROM users - WHERE id=$1 AND is_active=true + SELECT email FROM users WHERE id=$1 AND is_active=true `, [row.recipientUserId]) as Array<{ email: string | null }>; email = inspector?.email ?? null; } else { const [settings] = await this.dataSource.query(` - SELECT office_email AS "officeEmail",director_email AS "directorEmail" - FROM institutional_delivery_settings - WHERE id=1 - `) as Array<{ officeEmail: string | null; directorEmail: string | null }>; - email = row.recipientKind === 'OFFICE' - ? settings?.officeEmail ?? null - : settings?.directorEmail ?? null; + SELECT office_email AS "officeEmail" FROM institutional_delivery_settings WHERE id=1 + `) as Array<{ officeEmail: string | null }>; + email = settings?.officeEmail ?? null; } - await this.dataSource.query(` UPDATE inspection_document_deliveries SET recipient_email=$2, status=CASE WHEN $2::text IS NULL THEN 'WAITING_RECIPIENT' ELSE 'PENDING' END, - last_error=NULL, - updated_at=CURRENT_TIMESTAMP + last_error=NULL,updated_at=CURRENT_TIMESTAMP WHERE id=$1 AND status<>'SENT' - `, [row.id, email]); + `, [row.id,email]); } private async attempt(row: DeliveryRow) { if (row.status === 'SENT') return; if (!row.recipientEmail) { - await this.setStatus(row.id, 'WAITING_RECIPIENT', 'Destinatario no configurado'); + await this.setStatus(row.id,'WAITING_RECIPIENT','Destinatario no configurado'); return; } - if (!this.smtp.configured()) { - await this.setStatus(row.id, 'WAITING_TRANSPORT', 'SMTP no configurado'); + if (!await this.smtp.configured()) { + await this.setStatus(row.id,'WAITING_TRANSPORT','SMTP no configurado'); return; } @@ -379,28 +315,18 @@ export class InspectionDocumentDeliveryService { if (row.documentKind === 'ACT_PDF') { await this.pdf.ensure(row.actId); const file = await this.pdf.content(row.actId); - attachment = { - filename: file.originalName, - mimeType: file.mimeType, - content: file.buffer, - }; + attachment = { filename:file.originalName,mimeType:file.mimeType,content:file.buffer }; } else { if (!row.reportId) throw new Error('Informe no vinculado'); await this.word.ensure(row.reportId); const file = await this.word.content(row.reportId); const { readFile } = await import('node:fs/promises'); attachment = { - filename: file.originalName, - mimeType: file.mimeType, - content: await readFile(file.filePath), + filename:file.originalName,mimeType:file.mimeType,content:await readFile(file.filePath), }; } } catch (error) { - await this.setStatus( - row.id, - 'WAITING_ARTIFACT', - error instanceof Error ? error.message : 'Documento no disponible', - ); + await this.setStatus(row.id,'WAITING_ARTIFACT',error instanceof Error ? error.message : 'Documento no disponible'); return; } @@ -409,62 +335,41 @@ export class InspectionDocumentDeliveryService { SET attempts=attempts+1,last_attempt_at=CURRENT_TIMESTAMP,status='PENDING', last_error=NULL,updated_at=CURRENT_TIMESTAMP WHERE id=$1 - `, [row.id]); + `,[row.id]); try { const label = row.documentKind === 'ACT_PDF' ? `Acta ${row.actCode}` : `Informe ${row.reportCode ?? ''}`; const text = row.documentKind === 'REPORT_WORD' - ? `Se adjunta el informe Word automático ${row.reportCode ?? ''} para revisión del Director de Hidrocarburos.` + ? `Se adjunta el INF editable ${row.reportCode ?? ''} para su revisión y preparación antes de cargarlo en GEDO.` : row.recipientKind === 'INSPECTOR' - ? `Se adjunta copia del acta cerrada e inmutable ${row.actCode} correspondiente a tu inspección.` - : `Se adjunta el acta cerrada e inmutable ${row.actCode}.`; + ? `Se adjunta copia del acta sellada e inmutable ${row.actCode} correspondiente a tu inspección.` + : `Se adjunta el acta sellada e inmutable ${row.actCode}.`; const sent = await this.smtp.send({ - to: row.recipientEmail, - subject: `DH Inspección · ${label}`, - text, - attachment, + to:row.recipientEmail,subject:`DH Inspección · ${label}`,text,attachment, }); await this.dataSource.query(` UPDATE inspection_document_deliveries SET status='SENT',sent_at=CURRENT_TIMESTAMP,provider_message_id=$2, - last_error=NULL,updated_at=CURRENT_TIMESTAMP - WHERE id=$1 - `, [row.id, sent.messageId]); + last_error=NULL,updated_at=CURRENT_TIMESTAMP WHERE id=$1 + `,[row.id,sent.messageId]); await this.audit.record({ - action: AuditAction.DOCUMENT_DELIVERY_SENT, - entityType: 'inspection_document_delivery', - entityId: row.id, - source: AuditSource.SYSTEM, - actorUserId: null, - actorUsername: null, - requestId: null, - ip: null, - userAgent: null, - beforeData: null, - afterData: { - recipientKind: row.recipientKind, - recipientUserId: row.recipientUserId, - documentKind: row.documentKind, - status: 'SENT', - }, - metadata: { actId: row.actId, reportId: row.reportId }, + action:AuditAction.DOCUMENT_DELIVERY_SENT, + entityType:'inspection_document_delivery',entityId:row.id,source:AuditSource.SYSTEM, + actorUserId:null,actorUsername:null,requestId:null,ip:null,userAgent:null,beforeData:null, + afterData:{recipientKind:row.recipientKind,recipientUserId:row.recipientUserId,documentKind:row.documentKind,status:'SENT'}, + metadata:{actId:row.actId,reportId:row.reportId}, }); } catch (error) { - await this.setStatus( - row.id, - 'FAILED', - error instanceof Error ? error.message : 'Error de entrega', - ); + await this.setStatus(row.id,'FAILED',error instanceof Error ? error.message : 'Error de entrega'); } } - private async setStatus(id: string, status: string, error: string) { + private async setStatus(id: string,status: string,error: string) { await this.dataSource.query(` UPDATE inspection_document_deliveries - SET status=$2,last_error=$3,updated_at=CURRENT_TIMESTAMP - WHERE id=$1 - `, [id, status, error.slice(0, 500)]); + SET status=$2,last_error=$3,updated_at=CURRENT_TIMESTAMP WHERE id=$1 + `,[id,status,error.slice(0,500)]); } }