feat: Fase 2 — SENAE DSI, verificación bodega, fotos, notificaciones, auditoría, tarifas CRUD, páginas faltantes
API:
- PackagesService: verifyPackage (discrepancia >10%), addPhotos (multer disk), generateSenaeDeclaration (DSI stub)
- PATCH /packages/:id/verify — peso real + dimensiones + detección automática de discrepancia
- POST /packages/:id/photos — upload multipart fotos (hasta 10, 10 MB c/u)
- POST /packages/:id/senae/declare — genera DSI, N° autorización SENAE, cambia estado DECLARACION_ADUANERA
- GET /packages/pending-declaration — cola de paquetes VERIFICADO para agente aduanero
- AuditLogModule: GET /audit-logs con filtros (acción, recurso, fechas, paginación)
- NotificationsModule: notifyStatusChange → crea registros EMAIL/WHATSAPP/PUSH en DB (stub dispatch)
- TariffsModule: GET/PUT /tariffs — configuración de tarifas por tenant (pricePerLb, IVA, FODINFA…)
- ProductsModule: POST /products/scan — stub extracción de producto desde URL Amazon/eBay/Walmart
- StorageModule: saveFile/deleteFile con disco local (S3-ready)
- main.ts: NestExpressApplication + useStaticAssets('/uploads')
- Dirección bodega: de hardcoded a env vars (WAREHOUSE_ADDRESS_*)
Web:
- /bodega/verificacion — formulario real: peso real, dims, fotos, detección discrepancia, notificación
- /bodega/despacho — lista paquetes DECLARACION_ADUANERA → despacha a EN_TRANSITO_ECUADOR
- /bodega/declaraciones — cola SENAE: auto-detecta categoría 4×4, genera DSI por paquete
- /bodega/layout — agrega enlace Declaraciones SENAE, corrige ALLOWED roles
- /admin/tarifas — CRUD real: edita pricePerLb, IVA, FODINFA, límites 4×4; preview fórmulas
- /admin/auditoria — tabla paginada con filtros desde GET /audit-logs
- /portal/pre-alerta — tabs URL-scan / Manual, upload de factura, campos correctos
- /casillero/como-usar — timeline 9 pasos con dirección NJ y CTA
- /carga-pesada/como-funciona — flujo B2B 5 pasos, servicios incluidos
- /carga-pesada/inen — 6 categorías reguladas, callout de advertencia, proceso de asistencia
- api.ts: verify, uploadPhotos, senaeDeclare, pendingDeclaration, tariffs, auditLogs, products.scan
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { StorageService } from "./storage.service";
|
||||
|
||||
@Module({ providers: [StorageService], exports: [StorageService] })
|
||||
export class StorageModule {}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
@Injectable()
|
||||
export class StorageService {
|
||||
private readonly logger = new Logger(StorageService.name);
|
||||
private readonly uploadDir: string;
|
||||
private readonly baseUrl: string;
|
||||
|
||||
constructor(private config: ConfigService) {
|
||||
this.uploadDir = path.join(process.cwd(), "uploads");
|
||||
this.baseUrl = this.config.get("API_URL", "http://localhost:3001");
|
||||
// ensure uploads dir exists
|
||||
if (!fs.existsSync(this.uploadDir)) {
|
||||
fs.mkdirSync(this.uploadDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async saveFile(subdir: string, originalName: string, buffer: Buffer): Promise<string> {
|
||||
const ext = path.extname(originalName);
|
||||
const filename = `${randomUUID()}${ext}`;
|
||||
const dir = path.join(this.uploadDir, subdir);
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||
const filepath = path.join(dir, filename);
|
||||
fs.writeFileSync(filepath, buffer);
|
||||
// Return URL relative to API base
|
||||
return `${this.baseUrl}/uploads/${subdir}/${filename}`;
|
||||
}
|
||||
|
||||
async deleteFile(url: string): Promise<void> {
|
||||
try {
|
||||
const relative = url.replace(/^https?:\/\/[^/]+\/uploads\//, "");
|
||||
const filepath = path.join(this.uploadDir, relative);
|
||||
if (fs.existsSync(filepath)) fs.unlinkSync(filepath);
|
||||
} catch (e: unknown) {
|
||||
this.logger.warn(`deleteFile failed: ${(e as Error).message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user