From fb0e2e733334753c23ab42b312b13402c01de83f Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Fri, 10 Oct 2025 16:27:40 -0600 Subject: [PATCH] =?UTF-8?q?ADD:=20Remisi=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Holos/DocumentSection/ClientSection.vue | 22 +- .../DocumentSection/CompanyInfoSection.vue | 16 +- .../Holos/DocumentSection/HeaderSection.vue | 12 +- .../DocumentSection/ProductsTableView.vue | 30 +- .../Holos/DocumentSection/RemisionTable.vue | 259 ++++++++++ src/composables/useDocument.js | 464 ++++++++++++++++++ src/pages/Templates/Configs/ConfigRemision.js | 95 ++++ src/pages/Templates/Form.vue | 200 +++++--- src/services/documentService.js | 102 ++++ src/types/documents.d.ts | 273 +++++++++++ 10 files changed, 1402 insertions(+), 71 deletions(-) create mode 100644 src/components/Holos/DocumentSection/RemisionTable.vue create mode 100644 src/composables/useDocument.js create mode 100644 src/pages/Templates/Configs/ConfigRemision.js create mode 100644 src/services/documentService.js create mode 100644 src/types/documents.d.ts diff --git a/src/components/Holos/DocumentSection/ClientSection.vue b/src/components/Holos/DocumentSection/ClientSection.vue index e6e52d6..1a789be 100644 --- a/src/components/Holos/DocumentSection/ClientSection.vue +++ b/src/components/Holos/DocumentSection/ClientSection.vue @@ -20,25 +20,35 @@ const props = defineProps({ DATOS FISCALES CLIENTE
+
Nombre: {{ data.clienteNombre }}
+ +
Domicilio: {{ data.clienteDomicilio }}
+ +
- RFC: {{ data.clienteRFC }} + RFC: + {{ data.clienteRFC }}
-
- Telefono: + + +
+ Teléfono: {{ data.clienteTelefono }}
-
- Régimen Fiscal: {{ data.clienteRegimen }} + +
+ Régimen Fiscal: + {{ data.clienteRegimen }}
- + \ No newline at end of file diff --git a/src/components/Holos/DocumentSection/CompanyInfoSection.vue b/src/components/Holos/DocumentSection/CompanyInfoSection.vue index c9b1da5..52ab04d 100644 --- a/src/components/Holos/DocumentSection/CompanyInfoSection.vue +++ b/src/components/Holos/DocumentSection/CompanyInfoSection.vue @@ -36,9 +36,7 @@ const props = defineProps({
-
+

{{ data.empresaNombre }}

@@ -53,5 +51,17 @@ const props = defineProps({
+ +
+ +
+

{{ data.empresaNombre }}

+

RFC: {{ data.empresaRFC }}

+

{{ data.empresaDireccion }}

+
+
diff --git a/src/components/Holos/DocumentSection/HeaderSection.vue b/src/components/Holos/DocumentSection/HeaderSection.vue index 7a2ea14..1a9d837 100644 --- a/src/components/Holos/DocumentSection/HeaderSection.vue +++ b/src/components/Holos/DocumentSection/HeaderSection.vue @@ -14,7 +14,7 @@ const getTipoFolioLabel = (documentType) => { const labels = { COTIZACION: "Número de Folio:", FACTURA: "Folio:", - REMISION: "Número de Remisión:", + REMISION: "Folio:", }; return labels[documentType] || "Número de Folio:"; }; @@ -54,7 +54,7 @@ const getTipoFolioLabel = (documentType) => { {{ template.documentType || "COTIZACION" }}
-

+

Serie: {{ data.serie }}

@@ -68,13 +68,17 @@ const getTipoFolioLabel = (documentType) => { Fecha de Emisión: {{ data.fechaEmision }}

+

+ Fecha de Remisión: + {{ data.fechaRemision }} +

Fecha de Realización: {{ data.fechaRealizacion }}

-

- Vigencia:: +

+ Vigencia: {{ data.vigencia }}

diff --git a/src/components/Holos/DocumentSection/ProductsTableView.vue b/src/components/Holos/DocumentSection/ProductsTableView.vue index e92e441..a3d28ab 100644 --- a/src/components/Holos/DocumentSection/ProductsTableView.vue +++ b/src/components/Holos/DocumentSection/ProductsTableView.vue @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/services/documentService.js b/src/services/documentService.js new file mode 100644 index 0000000..e95a1e0 --- /dev/null +++ b/src/services/documentService.js @@ -0,0 +1,102 @@ +import { apiURL } from '@/services/Api'; + +/** + * Servicio para gestión de documentos + */ +export const documentService = { + /** + * Guardar documento (crear o actualizar) + * + */ + save(payload, documentId = null) { + if (!payload || typeof payload !== 'object') { + throw new Error('Payload es requerido y debe ser un objeto'); + } + + if (documentId !== null && (typeof documentId !== 'number' && typeof documentId !== 'string')) { + throw new Error('documentId debe ser un número o string'); + } + const method = documentId ? 'put' : 'post'; + const url = documentId + ? apiURL(`documents/${documentId}`) + : apiURL('documents'); + + return { + method, + url, + data: payload + }; + }, + + /** + * Obtener documento por ID + * + */ + getById(documentId) { + return { + method: 'get', + url: apiURL(`documents/${documentId}`) + }; + }, + + /** + * Listar documentos con filtros + * + */ + list(filters = {}) { + return { + method: 'get', + url: apiURL('documents'), + params: filters + }; + }, + + /** + * Generar PDF del documento + * + */ + generatePDF(documentId, options = {}) { + if (!documentId) { + throw new Error('Id es requerido'); + } + + const config = { + method: 'post', + url: apiURL(`documents/${documentId}/generate-pdf`) + }; + + if (Object.keys(options).length > 0) { + config.data = { + format: options.format || 'A4', + orientation: options.orientation || 'portrait' + }; + } + + return config; + }, + + /** + * Subir logo + * + */ + uploadLogo(file) { + if (!(file instanceof File)) { + throw new Error('El parámetro debe ser un archivo (File)'); + } + return { + method: 'post', + url: apiURL('documents/upload-logo'), + data: { logo: file } + }; + }, + + /** + * Eliminar documento + */ + delete(documentId) { + return { + method: 'delete', + url: apiURL(`documents/${documentId}`) + }; + } +}; \ No newline at end of file diff --git a/src/types/documents.d.ts b/src/types/documents.d.ts new file mode 100644 index 0000000..52a686d --- /dev/null +++ b/src/types/documents.d.ts @@ -0,0 +1,273 @@ +/** + * Tipos y interfaces para el sistema de documentos + * + * Este archivo define los contratos de datos entre frontend y backend + * para el módulo de generación de documentos (Cotizaciones, Facturas, Remisiones) + * + */ + +/** + * Tipos de documentos soportados + */ +export type DocumentType = 'COTIZACION' | 'FACTURA' | 'REMISION'; + +/** + * Estados posibles de un documento + */ +export type DocumentStatus = 'BORRADOR' | 'FINALIZADO' | 'ENVIADO' | 'CANCELADO'; + +/** + * Régimen fiscal + */ +export type RegimenFiscal = + | 'Régimen Simplificado de Confianza' + | 'Personas Físicas con Actividades Empresariales y Profesionales'; + +/** + * Tipo de comprobante (para facturas CFDI) + */ +export type TipoComprobante = 'Ingreso' | 'Egreso' | 'Traslado'; + +/** + * Configuración de branding/tema del documento + */ +export interface DocumentTemplate { + documentType?: DocumentType; // Para compatibilidad con código actual + primaryColor: string; + secondaryColor?: string; + logoUrl?: string; // URL del logo almacenado en servidor + logo?: string | File; // Temporal para upload (Base64 o File) + logoPreview?: string; // Preview en frontend + slogan?: string; +} + +/** + * Datos de la empresa emisora + */ +export interface EmpresaData { + nombre: string; + rfc: string; + email: string; + telefono: string; + direccion: string; + web?: string; + + // Específico para facturas (CFDI) + lugar?: string; // Lugar de expedición (código postal) + cfdi?: string; // Uso de CFDI (ej: "G03 - Gastos en general") + regimen?: RegimenFiscal; // Régimen fiscal +} + +/** + * Datos bancarios (opcional, principalmente para cotizaciones) + */ +export interface BancosData { + banco?: string; + tipoCuenta?: string; // Ej: "Cuenta de cheques" + cuenta?: string; // Número de cuenta +} + +/** + * Datos del cliente receptor + */ +export interface ClienteData { + nombre: string; + rfc?: string; + domicilio?: string; + telefono?: string; + + // Específico para facturas (CFDI) + regimen?: RegimenFiscal; // Régimen fiscal del cliente +} + +/** + * Datos del ejecutivo de ventas (solo cotizaciones) + */ +export interface EjecutivoData { + nombre?: string; + correo?: string; + celular?: string; +} + +/** + * Detalles específicos del documento + */ +export interface DocumentoDetalles { + // Común para todos + folio?: string; + observaciones?: string; + + // Específico para cotizaciones + fechaRealizacion?: string; + vigencia?: string; + + // Específico para facturas + serie?: string; + fechaEmision?: string; + tipoComprobante?: TipoComprobante; + + // Específico para remisiones + fechaRemision?: string; +} + +/** + * Producto en la tabla de productos + */ +export interface Producto { + id?: number; // ID + clave?: string; // Clave del producto/servicio + descripcion: string; + cantidad: number; + unidad?: string; // Unidad de medida (ej: "Pieza", "Servicio", "Hora") + precioUnitario: number; + descuento?: number; // Porcentaje de descuento + iva?: number; // Porcentaje de IVA + + // Calculados (pueden venir del frontend o backend) + subtotal?: number; + total?: number; +} + +/** + * Totales calculados del documento + */ +export interface Totales { + // Totales principales + subtotal: number; + iva: number; + total: number; + + // Totales adicionales (opcionales) + subtotal1?: number; // Subtotal antes de descuento + descuentoTotal?: number; // Total de descuentos aplicados + subtotal2?: number; // Subtotal después de descuento + impuestosTrasladados?: number; // Para facturas +} + +/** + * Estructura completa de datos del documento en la BD + * + * Este es el formato JSON que se guarda en la base de datos + * y el que el backend enviará/recibirá + */ +export interface DocumentRecord { + // Identificadores + id?: string | number; + tipo: DocumentType; + estado: DocumentStatus; + folio: string; + + // Configuración de template/branding + templateConfig: DocumentTemplate; + + // Datos agrupados del documento + datos: { + empresa: EmpresaData; + bancos?: BancosData; // Solo para cotizaciones + cliente: ClienteData; + ejecutivo?: EjecutivoData; // Solo para cotizaciones + documento: DocumentoDetalles; + productos: Producto[]; + totales: Totales; + }; + + // URLs de archivos generados + pdfUrl?: string; + logoUrl?: string; + + // Metadatos + userId?: string | number; // ID del usuario que creó el documento + createdAt?: string; + updatedAt?: string; +} + +/** + * Payload para crear o actualizar un documento + * + * Este es el objeto que el frontend envía al backend + */ +export interface SaveDocumentPayload { + tipo: DocumentType; + estado?: DocumentStatus; + templateConfig: DocumentTemplate; + datos: DocumentRecord['datos']; +} + +/** + * Respuesta del backend al guardar un documento + */ +export interface SaveDocumentResponse { + status: 'success' | 'fail' | 'error'; + data: DocumentRecord; + message?: string; +} + +/** + * Payload para generar PDF + */ +export interface GeneratePDFPayload { + documentId: string | number; + options?: { + format?: 'A4' | 'Letter'; + orientation?: 'portrait' | 'landscape'; + }; +} + +/** + * Respuesta al generar PDF + */ +export interface GeneratePDFResponse { + status: 'success' | 'fail' | 'error'; + data: { + pdfUrl: string; + }; + message?: string; +} + +/** + * Payload para subir logo + */ +export interface UploadLogoPayload { + logo: File; + documentType?: DocumentType; +} + +/** + * Respuesta al subir logo + */ +export interface UploadLogoResponse { + status: 'success' | 'fail' | 'error'; + data: { + logoUrl: string; + }; + message?: string; +} + +/** + * Filtros para listar documentos + */ +export interface DocumentFilters { + tipo?: DocumentType; + estado?: DocumentStatus; + fechaInicio?: string; + fechaFin?: string; + search?: string; // Búsqueda por folio, cliente, etc. + page?: number; + perPage?: number; +} + +/** + * Respuesta de listado de documentos + */ +export interface DocumentListResponse { + status: 'success' | 'fail' | 'error'; + data: { + data: DocumentRecord[]; + pagination: { + total: number; + perPage: number; + currentPage: number; + lastPage: number; + }; + }; +} \ No newline at end of file