diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..ad5811a --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,11 @@ +{ + "permissions": { + "allow": [ + "Bash(npm run build:*)", + "Bash(npm test)", + "Bash(npx ts-node:*)", + "Bash(findstr:*)" + ], + "defaultMode": "bypassPermissions" + } +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04f30a8..96ee8cf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,10 +46,19 @@ jobs: echo "ESM build missing" exit 1 fi - if [ ! -d "dist/types" ]; then + if [ ! -f "dist/cjs/index.d.ts" ]; then echo "TypeScript definitions missing" exit 1 fi + if [ ! -f "dist/cjs/package.json" ]; then + echo "CJS package.json missing" + exit 1 + fi + if [ ! -f "dist/esm/package.json" ]; then + echo "ESM package.json missing" + exit 1 + fi + echo "Build artifacts verified successfully" - name: Publish to NPM run: npm publish --access public diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4266545 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,67 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +FiscalAPI SDK for Node.js - Official TypeScript SDK for Mexican electronic invoicing (CFDI 4.0) and fiscal services. Wraps the FiscalAPI REST API for invoice creation, payment complements, bulk XML downloads, and SAT catalog queries. + +## Build Commands + +```bash +npm run build # Full build: clean + esm + cjs + types +npm run build:esm # TypeScript to ES Modules (dist/esm) +npm run build:cjs # TypeScript to CommonJS (dist/cjs) +npm run build:types # TypeScript declaration files (dist/types) +npm run clean # Remove dist directory +npm test # Run Jest tests +npm run lint # ESLint on src/**/*.ts +npm run main # Run examples/main.ts with ts-node +``` + +## Architecture + +**Facade Pattern**: `FiscalapiClient` is the main entry point exposing all services: +- `invoices` - CFDI invoice creation, cancellation, PDF generation +- `products` - Product/service catalog management +- `persons` - Issuer/recipient (emisor/receptor) management +- `taxFiles` - CSD certificate upload +- `catalogs` - SAT catalog queries +- `downloadCatalogs`, `downloadRules`, `downloadRequests` - Bulk XML download + +**Directory Structure**: +- `src/abstractions/` - Service interfaces (I*Service) +- `src/services/` - Service implementations extending `BaseFiscalapiService` +- `src/models/` - Data models (Invoice, Person, Product, etc.) +- `src/common/` - Shared DTOs (ApiResponse, PagedList, FiscalapiSettings) +- `src/utils/` - Date formatting (Luxon), Base64 encoding, validation helpers + +**Key Patterns**: +- All services implement interfaces from `abstractions/` +- HTTP client injected via factory pattern +- Dual-package output: ESM + CommonJS + TypeScript declarations +- Date handling uses Luxon with `America/Mexico_City` timezone +- SAT date format: `yyyy-MM-ddTHH:mm:ss` + +## Configuration + +```typescript +const settings: FiscalapiSettings = { + apiUrl: "https://test.fiscalapi.com", // or https://live.fiscalapi.com + apiKey: "", + tenant: "", + apiVersion?: "v4", // default + timeZone?: "America/Mexico_City", // default + debug?: false +}; +const client = FiscalapiClient.create(settings); +``` + +## Two Operation Modes + +1. **By References**: Send only IDs of pre-configured entities in FiscalAPI dashboard +2. **By Values**: Send complete data in each request (no prior setup needed) + +## TypeScript Configuration + +Strict mode enabled with all checks: `noImplicitAny`, `strictNullChecks`, `noImplicitReturns`, `noUnusedParameters`. Target ES2018. diff --git a/README.md b/README.md index c8f63e1..46a82f3 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,15 @@ - **Soporte completo para CFDI 4.0** con todas las especificaciones oficiales - **Timbrado de facturas de ingreso** con validación automática - **Timbrado de notas de crédito** (facturas de egreso) -- **Timbrado de complementos de pago** en MXN, USD y EUR. +- **Timbrado de complementos de pago** en MXN, USD y EUR +- **Timbrado de facturas de nómina** (CFDI Nómina 1.2) - **Consulta del estatus de facturas** en el SAT en tiempo real -- **Cancelación de facturas** +- **Cancelación de facturas** - **Generación de archivos PDF** de las facturas con formato profesional - **Personalización de logos y colores** en los PDF generados - **Envío de facturas por correo electrónico** automatizado - **Descarga de archivos XML** con estructura completa -- **Almacenamiento y recuperación** de facturas por 5 años. +- **Almacenamiento y recuperación** de facturas por 5 años - Dos [modos de operación](https://docs.fiscalapi.com/modes-of-operation): **Por valores** o **Por referencias** ## 📥 Descarga Masiva @@ -32,11 +33,16 @@ - **Administración de personas** (emisores, receptores, clientes, usuarios, etc.) - **Gestión de certificados CSD y FIEL** (subir archivos .cer y .key a FiscalAPI) - **Configuración de datos fiscales** (RFC, domicilio fiscal, régimen fiscal) +- **Datos de empleado** (agrega/actualiza/elimina datos de empleado a una persona. CFDI Nómina) +- **Datos de empleador** (agrega/actualiza/elimina datos de empleador a una persona. CFDI Nómina) ## 🛍️ Gestión de Productos/Servicios - **Gestión de productos y servicios** con catálogo personalizable - **Administración de impuestos aplicables** (IVA, ISR, IEPS) +## 🎖️ Gestión de Timbres +- **Gestión de folios fiscales** Compra timbres a fiscalapi y transfiere/retira a las personas de tu organizacion segun tus reglas de negocio. + ## 📚 Consulta de Catálogos SAT - **Consulta en catálogos oficiales del SAT** actualizados - **Consulta en catálogos oficiales de Descarga masiva del SAT** actualizados @@ -381,16 +387,24 @@ try { } ``` +## 📂 Más Ejemplos + +- [Gestión de Timbres](examples/ejemplo-timbres.ts) +- [Datos Empleador/Empleado](examples/ejemplo-datos-empleado-empleador.ts) +- [Facturas de Nómina (Por Valores)](examples/ejemplos-factura-nomina-valores.ts) +- [Facturas de Nómina (Por Referencias)](examples/ejemplos-factura-nomina-referencias.ts) +- [Facturas de Impuestos Locales (Por Referencias)](examples/ejemplos-factura-impuestos-locales-referencias.ts) +- [Facturas de Impuestos Locales (Por Valores)](examples/ejemplos-factura-impuestos-locales-valores.ts) + + --- ## 📋 Operaciones Principales -- **Facturas (CFDI)** - Crear facturas de ingreso, notas de crédito, complementos de pago, cancelaciones, generación de PDF/XML. -- **Personas (Clientes/Emisores)** - Alta y administración de personas, gestión de certificados (CSD). -- **Productos y Servicios** - Administración de catálogos de productos, búsqueda en catálogos SAT. +- **Facturas (CFDI)** - Ingreso, egreso, pago, nómina, cancelaciones, PDF/XML +- **Personas** - Emisores, receptores, certificados CSD +- **Productos** - Catálogo de productos/servicios +- **Timbres** - Transferencias y retiros ## 🤝 Contribuir diff --git a/examples/ejemplo-datos-empleado-empleador.ts b/examples/ejemplo-datos-empleado-empleador.ts new file mode 100644 index 0000000..6e368e8 --- /dev/null +++ b/examples/ejemplo-datos-empleado-empleador.ts @@ -0,0 +1,60 @@ +import { + FiscalapiClient, + FiscalapiSettings, + CreateEmployerRequest, + CreateEmployeeRequest +} from '../src'; + +async function main(): Promise { + + const settings: FiscalapiSettings = { + apiUrl: 'https://test.fisalapi.com', + apiKey: '', + tenant: '', + debug: true + }; + + const client = FiscalapiClient.create(settings); + + const escuelaKemperUrgateId = ''; + const karlaFuenteNolascoId = ''; + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: karlaFuenteNolascoId, + employeeNumber: '123456789', + socialSecurityNumber: '04078873454', + laborRelationStartDate: '2024-08-18', + satContractTypeId: '01', + satTaxRegimeTypeId: '02', + satJobRiskId: '1', + satPaymentPeriodicityId: '05', + satBankId: '012', + satPayrollStateId: 'JAL', + department: 'GenAI', + position: 'Sr Software Engineer', + seniority: 'P54W', + baseSalaryForContributions: 2828.50, + integratedDailySalary: 0.00, + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); + + // Consultar datos + const employer = await client.persons.employer.getById(escuelaKemperUrgateId); + console.log('Consulta empleador:', JSON.stringify(employer, null, 2)); + + const employee = await client.persons.employee.getById(karlaFuenteNolascoId); + console.log('Consulta empleado:', JSON.stringify(employee, null, 2)); +} + +main().catch(console.error); diff --git a/examples/ejemplo-timbres.ts b/examples/ejemplo-timbres.ts new file mode 100644 index 0000000..04844ad --- /dev/null +++ b/examples/ejemplo-timbres.ts @@ -0,0 +1,41 @@ +import { FiscalapiClient, FiscalapiSettings } from '../src/index'; + +async function main(): Promise { + + const settings: FiscalapiSettings = { + apiUrl: 'https://test.fisalapi.com', + apiKey: '', + tenant: '', + debug: true + }; + + const client = FiscalapiClient.create(settings); + + // 1. Listar transacciones de timbres + const list = await client.stamps.getList(1, 10); + console.log('Lista de transacciones:', list); + + // 2. Obtener transacción por ID + const transaction = await client.stamps.getById('77678d6d-94b1-4635-aa91-15cdd7423aab'); + console.log('Transacción por ID:', transaction); + + // 3. Transferir timbres + const transfer = await client.stamps.transferStamps({ + fromPersonId: '2e7b988f-3a2a-4f67-86e9-3f931dd48581', + toPersonId: '5fd9f48c-a6a2-474f-944b-88a01751d432', + amount: 1, + comments: 'Transferencia de prueba' + }); + console.log('Transferencia:', transfer); + + // 4. Retirar timbres + const withdraw = await client.stamps.withdrawStamps({ + fromPersonId: '5fd9f48c-a6a2-474f-944b-88a01751d432', + toPersonId: '2e7b988f-3a2a-4f67-86e9-3f931dd48581', + amount: 1, + comments: 'Retiro de prueba' + }); + console.log('Retiro:', withdraw); +} + +main(); diff --git a/examples/ejemplos-factura-impuestos-locales-referencias.ts b/examples/ejemplos-factura-impuestos-locales-referencias.ts new file mode 100644 index 0000000..ee3e619 --- /dev/null +++ b/examples/ejemplos-factura-impuestos-locales-referencias.ts @@ -0,0 +1,205 @@ +/** + * Ejemplos de facturas con impuestos locales (CFDI complemento Impuestos Locales) usando el SDK de FiscalAPI + * Todos los métodos usan el modo "ByReferences" - se envían IDs de entidades pre-configuradas en FiscalAPI + */ + +import { FiscalapiClient, FiscalapiSettings, Invoice, InvoiceItem, ItemTax } from '../src/index'; +import { inspect } from 'util'; + +// Configuración de la consola para mostrar objetos anidados +inspect.defaultOptions.depth = null; +inspect.defaultOptions.colors = true; + +// Configuración de FiscalAPI +const settings: FiscalapiSettings = { + apiUrl: 'https://test.fiscalapi.com', + apiKey: '', + tenant: '', + debug: true +}; + +// IDs de personas pre-configuradas en FiscalAPI (modo ByReferences) +const currentDate = '2026-01-27T10:04:06'; +const escuelaKemperUrgateId = "2e7b988f-3a2a-4f67-86e9-3f931dd48581"; +const karlaFuenteNolascoId = "109f4d94-63ea-4a21-ab15-20c8b87d8ee9"; + +// Items comunes para las facturas de ingreso +const invoiceItems: InvoiceItem[] = [ + { + itemCode: '01010101', + quantity: '9.5', + unitOfMeasurementCode: 'E48', + description: 'Invoicing software as a service', + unitPrice: '3587.75', + taxObjectCode: '02', + itemSku: '7506022301697', + discount: '255.85', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax + ] + }, + { + itemCode: '01010101', + quantity: '8', + unitOfMeasurementCode: 'E48', + description: 'Software Consultant', + unitPrice: '250.85', + taxObjectCode: '02', + itemSku: '7506022301698', + discount: '255.85', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax + ] + }, + { + itemCode: '01010101', + quantity: '6', + unitOfMeasurementCode: 'E48', + description: 'Computer software', + unitPrice: '1250.75', + taxObjectCode: '02', + itemSku: '7506022301699', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax, + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.106666', taxFlagCode: 'R' } as ItemTax + ] + } +]; + +// ============================================================================ +// 1. FACTURA INGRESO - IMPUESTOS LOCALES CEDULAR + ISH (ByReferences) +// ============================================================================ +async function facturaImpuestosLocalesCedularIshByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales CEDULAR + ISH (ByReferences) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: karlaFuenteNolascoId + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'CEDULAR', taxRate: '3.00', taxAmount: '6.00', taxFlagCode: 'R' }, + { taxName: 'ISH', taxRate: '8.00', taxAmount: '16.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 2. FACTURA INGRESO - IMPUESTOS LOCALES CEDULAR (ByReferences) +// ============================================================================ +async function facturaImpuestosLocalesCedularByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales CEDULAR (ByReferences) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: karlaFuenteNolascoId + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'CEDULAR', taxRate: '3.00', taxAmount: '6.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 3. FACTURA INGRESO - IMPUESTOS LOCALES ISH (ByReferences) +// ============================================================================ +async function facturaImpuestosLocalesIshByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales ISH (ByReferences) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: karlaFuenteNolascoId + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'ISH', taxRate: '8.00', taxAmount: '16.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + + +// ============================================================================ +// FUNCION PRINCIPAL +// ============================================================================ +async function main(): Promise { + console.log('=== Ejemplos de Factura con Impuestos Locales FiscalAPI (ByReferences) ===\n'); + + const client = FiscalapiClient.create(settings); + + try { + // Descomentar el caso de uso que se desea ejecutar + + //await facturaImpuestosLocalesCedularIshByReferences(client); + // await facturaImpuestosLocalesCedularByReferences(client); + await facturaImpuestosLocalesIshByReferences(client); + + + console.log('\nEjecución completada.'); + } catch (error) { + console.error('Error:', error); + } +} + +// Ejecutar función principal +main(); diff --git a/examples/ejemplos-factura-impuestos-locales-valores.ts b/examples/ejemplos-factura-impuestos-locales-valores.ts new file mode 100644 index 0000000..c8340e6 --- /dev/null +++ b/examples/ejemplos-factura-impuestos-locales-valores.ts @@ -0,0 +1,239 @@ +/** + * Ejemplos de facturas con impuestos locales (CFDI complemento Impuestos Locales) usando el SDK de FiscalAPI + * Todos los métodos usan el modo "ByValues" - los datos se pasan directamente en la petición HTTP + */ + +import { FiscalapiClient, FiscalapiSettings, Invoice, InvoiceItem, ItemTax } from '../src/index'; +import { inspect } from 'util'; + +// Configuración de la consola para mostrar objetos anidados +inspect.defaultOptions.depth = null; +inspect.defaultOptions.colors = true; + +// Configuración de FiscalAPI +const settings: FiscalapiSettings = { + apiUrl: 'https://test.fiscalapi.com', + apiKey: '', + tenant: '', + debug: true +}; + +// Sellos SAT de prueba +const escuelaKemperUrgateBase64Cer = "MIIFsDCCA5igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTYwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTE0MzUxWhcNMjcwNTE4MTE0MzUxWjCB1zEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjElMCMGA1UELRMcRUtVOTAwMzE3M0M5IC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmecO6n2GS0zL025gbHGQVxznPDICoXzR2uUngz4DqxVUC/w9cE6FxSiXm2ap8Gcjg7wmcZfm85EBaxCx/0J2u5CqnhzIoGCdhBPuhWQnIh5TLgj/X6uNquwZkKChbNe9aeFirU/JbyN7Egia9oKH9KZUsodiM/pWAH00PCtoKJ9OBcSHMq8Rqa3KKoBcfkg1ZrgueffwRLws9yOcRWLb02sDOPzGIm/jEFicVYt2Hw1qdRE5xmTZ7AGG0UHs+unkGjpCVeJ+BEBn0JPLWVvDKHZAQMj6s5Bku35+d/MyATkpOPsGT/VTnsouxekDfikJD1f7A1ZpJbqDpkJnss3vQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFaUgj5PqgvJigNMgtrdXZnbPfVBbukAbW4OGnUhNrA7SRAAfv2BSGk16PI0nBOr7qF2mItmBnjgEwk+DTv8Zr7w5qp7vleC6dIsZFNJoa6ZndrE/f7KO1CYruLXr5gwEkIyGfJ9NwyIagvHHMszzyHiSZIA850fWtbqtythpAliJ2jF35M5pNS+YTkRB+T6L/c6m00ymN3q9lT1rB03YywxrLreRSFZOSrbwWfg34EJbHfbFXpCSVYdJRfiVdvHnewN0r5fUlPtR9stQHyuqewzdkyb5jTTw02D2cUfL57vlPStBj7SEi3uOWvLrsiDnnCIxRMYJ2UA2ktDKHk+zWnsDmaeleSzonv2CHW42yXYPCvWi88oE1DJNYLNkIjua7MxAnkNZbScNw01A6zbLsZ3y8G6eEYnxSTRfwjd8EP4kdiHNJftm7Z4iRU7HOVh79/lRWB+gd171s3d/mI9kte3MRy6V8MMEMCAnMboGpaooYwgAmwclI2XZCczNWXfhaWe0ZS5PmytD/GDpXzkX0oEgY9K/uYo5V77NdZbGAjmyi8cE2B2ogvyaN2XfIInrZPgEffJ4AB7kFA2mwesdLOCh0BLD9itmCve3A1FGR4+stO2ANUoiI3w3Tv2yQSg4bjeDlJ08lXaaFCLW2peEXMXjQUk7fmpb5MNuOUTW6BE="; +const escuelaKemperUrgateBase64Key = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS/AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucoZQObOaLUEm+I+QZ7Y8Giupo+F1XWkLvAsdk/uZlJcTfKLJyJbJwsQYbSpLOCLataZ4O5MVnnmMbfG//NKJn9kSMvJQZhSwAwoGLYDm1ESGezrvZabgFJnoQv8Si1nAhVGTk9FkFBesxRzq07dmZYwFCnFSX4xt2fDHs1PMpQbeq83aL/PzLCce3kxbYSB5kQlzGtUYayiYXcu0cVRu228VwBLCD+2wTDDoCmRXtPesgrLKUR4WWWb5N2AqAU1mNDC+UEYsENAerOFXWnmwrcTAu5qyZ7GsBMTpipW4Dbou2yqQ0lpA/aB06n1kz1aL6mNqGPaJ+OqoFuc8Ugdhadd+MmjHfFzoI20SZ3b2geCsUMNCsAd6oXMsZdWm8lzjqCGWHFeol0ik/xHMQvuQkkeCsQ28PBxdnUgf7ZGer+TN+2ZLd2kvTBOk6pIVgy5yC6cZ+o1Tloql9hYGa6rT3xcMbXlW+9e5jM2MWXZliVW3ZhaPjptJFDbIfWxJPjz4QvKyJk0zok4muv13Iiwj2bCyefUTRz6psqI4cGaYm9JpscKO2RCJN8UluYGbbWmYQU+Int6LtZj/lv8p6xnVjWxYI+rBPdtkpfFYRp+MJiXjgPw5B6UGuoruv7+vHjOLHOotRo+RdjZt7NqL9dAJnl1Qb2jfW6+d7NYQSI/bAwxO0sk4taQIT6Gsu/8kfZOPC2xk9rphGqCSS/4q3Os0MMjA1bcJLyoWLp13pqhK6bmiiHw0BBXH4fbEp4xjSbpPx4tHXzbdn8oDsHKZkWh3pPC2J/nVl0k/yF1KDVowVtMDXE47k6TGVcBoqe8PDXCG9+vjRpzIidqNo5qebaUZu6riWMWzldz8x3Z/jLWXuDiM7/Yscn0Z2GIlfoeyz+GwP2eTdOw9EUedHjEQuJY32bq8LICimJ4Ht+zMJKUyhwVQyAER8byzQBwTYmYP5U0wdsyIFitphw+/IH8+v08Ia1iBLPQAeAvRfTTIFLCs8foyUrj5Zv2B/wTYIZy6ioUM+qADeXyo45uBLLqkN90Rf6kiTqDld78NxwsfyR5MxtJLVDFkmf2IMMJHTqSfhbi+7QJaC11OOUJTD0v9wo0X/oO5GvZhe0ZaGHnm9zqTopALuFEAxcaQlc4R81wjC4wrIrqWnbcl2dxiBtD73KW+wcC9ymsLf4I8BEmiN25lx/OUc1IHNyXZJYSFkEfaxCEZWKcnbiyf5sqFSSlEqZLc4lUPJFAoP6s1FHVcyO0odWqdadhRZLZC9RCzQgPlMRtji/OXy5phh7diOBZv5UYp5nb+MZ2NAB/eFXm2JLguxjvEstuvTDmZDUb6Uqv++RdhO5gvKf/AcwU38ifaHQ9uvRuDocYwVxZS2nr9rOwZ8nAh+P2o4e0tEXjxFKQGhxXYkn75H3hhfnFYjik/2qunHBBZfcdG148MaNP6DjX33M238T9Zw/GyGx00JMogr2pdP4JAErv9a5yt4YR41KGf8guSOUbOXVARw6+ybh7+meb7w4BeTlj3aZkv8tVGdfIt3lrwVnlbzhLjeQY6PplKp3/a5Kr5yM0T4wJoKQQ6v3vSNmrhpbuAtKxpMILe8CQoo="; +const password = "12345678a"; +const currentDate = '2026-01-27T10:04:06'; + +// Items comunes para las facturas de ingreso +const invoiceItems: InvoiceItem[] = [ + { + itemCode: '01010101', + quantity: '9.5', + unitOfMeasurementCode: 'E48', + description: 'Invoicing software as a service', + unitPrice: '3587.75', + taxObjectCode: '02', + itemSku: '7506022301697', + discount: '255.85', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax + ] + }, + { + itemCode: '01010101', + quantity: '8', + unitOfMeasurementCode: 'E48', + description: 'Software Consultant', + unitPrice: '250.85', + taxObjectCode: '02', + itemSku: '7506022301698', + discount: '255.85', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax + ] + }, + { + itemCode: '01010101', + quantity: '6', + unitOfMeasurementCode: 'E48', + description: 'Computer software', + unitPrice: '1250.75', + taxObjectCode: '02', + itemSku: '7506022301699', + itemTaxes: [ + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.160000', taxFlagCode: 'T' } as ItemTax, + { taxCode: '002', taxTypeCode: 'Tasa', taxRate: '0.106666', taxFlagCode: 'R' } as ItemTax + ] + } +]; + +// ============================================================================ +// 1. FACTURA INGRESO - IMPUESTOS LOCALES CEDULAR + ISH (ByValues) +// ============================================================================ +async function facturaImpuestosLocalesCedularIshByValues(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales CEDULAR + ISH (ByValues) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + zipCode: '42501', + taxRegimeCode: '601', + cfdiUseCode: 'G01', + email: 'someone@somewhere.com' + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'CEDULAR', taxRate: '3.00', taxAmount: '6.00', taxFlagCode: 'R' }, + { taxName: 'ISH', taxRate: '8.00', taxAmount: '16.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 2. FACTURA INGRESO - IMPUESTOS LOCALES CEDULAR (ByValues) +// ============================================================================ +async function facturaImpuestosLocalesCedularByValues(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales CEDULAR (ByValues) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + zipCode: '42501', + taxRegimeCode: '601', + cfdiUseCode: 'G01', + email: 'someone@somewhere.com' + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'CEDULAR', taxRate: '3.00', taxAmount: '6.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 3. FACTURA INGRESO - IMPUESTOS LOCALES ISH (ByValues) +// ============================================================================ +async function facturaImpuestosLocalesIshByValues(client: FiscalapiClient): Promise { + console.log('\n=== Factura Ingreso con Impuestos Locales ISH (ByValues) ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentFormCode: '01', + currencyCode: 'MXN', + typeCode: 'I', + expeditionZipCode: '42501', + paymentMethodCode: 'PUE', + exchangeRate: 1, + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + zipCode: '42501', + taxRegimeCode: '601', + cfdiUseCode: 'G01', + email: 'someone@somewhere.com' + }, + items: invoiceItems, + complement: { + localTaxes: { + taxes: [ + { taxName: 'ISH', taxRate: '8.00', taxAmount: '16.00', taxFlagCode: 'R' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + + +// ============================================================================ +// FUNCION PRINCIPAL +// ============================================================================ +async function main(): Promise { + console.log('=== Ejemplos de Factura con Impuestos Locales FiscalAPI (ByValues) ===\n'); + + const client = FiscalapiClient.create(settings); + + try { + // Descomentar el caso de uso que se desea ejecutar + + await facturaImpuestosLocalesCedularIshByValues(client); + // await facturaImpuestosLocalesCedularByValues(client); + // await facturaImpuestosLocalesIshByValues(client); + + + console.log('\nEjecución completada.'); + } catch (error) { + console.error('Error:', error); + } +} + +// Ejecutar función principal +main(); diff --git a/examples/ejemplos-factura-nomina-referencias.ts b/examples/ejemplos-factura-nomina-referencias.ts new file mode 100644 index 0000000..b122735 --- /dev/null +++ b/examples/ejemplos-factura-nomina-referencias.ts @@ -0,0 +1,1280 @@ +/** + * Ejemplos de facturas de nómina (CFDI Nómina) usando el SDK de FiscalAPI + * Todos los métodos usan el modo "ByReferences" - se pasan solo IDs de entidades preconfiguradas + */ + +import { + FiscalapiClient, + FiscalapiSettings, + Invoice, + CreateEmployerRequest, + CreateEmployeeRequest +} from '../src/index'; +import { inspect } from 'util'; + +// Configuración de la consola para mostrar objetos anidados +inspect.defaultOptions.depth = null; +inspect.defaultOptions.colors = true; + +// Configuración de FiscalAPI +const settings: FiscalapiSettings = { + apiUrl: 'https://test.fisalapi.com', + apiKey: '', + tenant: '', + debug: true + +}; + +// ============================================================================ +// UUID CONSTANTS - IDs de personas preconfiguradas en FiscalAPI +// ============================================================================ +const escuelaKemperUrgateId = "2e7b988f-3a2a-4f67-86e9-3f931dd48581"; +const karlaFuenteNolascoId = "109f4d94-63ea-4a21-ab15-20c8b87d8ee9"; +const organicosNavezOsorioId = "f645e146-f80e-40fa-953f-fd1bd06d4e9f"; +const xochiltCasasChavezId = "e3b4edaa-e4d9-4794-9c5b-3dd5b7e372aa"; +const ingridXodarJimenezId = "9367249f-f0ee-43f4-b771-da2fff3f185f"; + +// Fecha actual para las facturas +const currentDate = '2026-01-27T10:04:06'; + +// ============================================================================ +// 1. NOMINA ORDINARIA (Facturación por referencias) +// ============================================================================ +async function nominaOrdinariaByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Ordinaria ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(karlaFuenteNolascoId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: karlaFuenteNolascoId, + socialSecurityNumber: '04078873454', + laborRelationStartDate: '2024-08-18', + seniority: 'P54W', + satContractTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '123456789', + department: 'GenAI', + position: 'Sr Software Engineer', + satJobRiskId: '1', + satPaymentPeriodicityId: '05', + satBankId: '012', + baseSalaryForContributions: 2828.50, + integratedDailySalary: 0.00, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaOrdinariaByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Ordinaria ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: karlaFuenteNolascoId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2025-08-30', + initialPaymentDate: '2025-07-31', + finalPaymentDate: '2025-08-30', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '1003', concept: 'Sueldo Nominal', taxedAmount: 95030.00, exemptAmount: 0.00 }, + { earningTypeCode: '005', code: '5913', concept: 'Fondo de Ahorro Aportación Patrón', taxedAmount: 0.00, exemptAmount: 4412.46 }, + { earningTypeCode: '038', code: '1885', concept: 'Bono Ingles', taxedAmount: 14254.50, exemptAmount: 0.00 }, + { earningTypeCode: '029', code: '1941', concept: 'Vales Despensa', taxedAmount: 0.00, exemptAmount: 3439.00 }, + { earningTypeCode: '038', code: '1824', concept: 'Herramientas Teletrabajo (telecom y prop. electri)', taxedAmount: 273.00, exemptAmount: 0.00 } + ], + otherPayments: [ + { otherPaymentTypeCode: '002', code: '5050', concept: 'Exceso de subsidio al empleo', amount: 0.00, subsidyCaused: 0.00 } + ] + }, + deductions: [ + { deductionTypeCode: '002', code: '5003', concept: 'ISR Causado', amount: 27645.52 }, + { deductionTypeCode: '004', code: '5910', concept: 'Fondo de ahorro Empleado Inversión', amount: 4412.46 }, + { deductionTypeCode: '004', code: '5914', concept: 'Fondo de Ahorro Patrón Inversión', amount: 4412.46 }, + { deductionTypeCode: '004', code: '1966', concept: 'Contribución póliza exceso GMM', amount: 519.91 }, + { deductionTypeCode: '004', code: '1934', concept: 'Descuento Vales Despensa', amount: 1.00 }, + { deductionTypeCode: '004', code: '1942', concept: 'Vales Despensa Electrónico', amount: 3439.00 }, + { deductionTypeCode: '001', code: '1895', concept: 'IMSS', amount: 2391.13 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 2. NOMINA ASIMILADOS (Facturación por referencias) +// ============================================================================ +async function nominaAsimiladosByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Asimilados ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(xochiltCasasChavezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + originEmployerTin: 'EKU9003173C9' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: xochiltCasasChavezId, + satContractTypeId: '09', + satUnionizedStatusId: 'No', + satTaxRegimeTypeId: '09', + employeeNumber: '00002', + department: 'ADMINISTRACION', + position: 'DIRECTOR DE ADMINISTRACION', + satPaymentPeriodicityId: '99', + satBankId: '012', + bankAccount: '1111111111', + satPayrollStateId: 'CMX' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaAsimiladosByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Asimilados ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '06880', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: xochiltCasasChavezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-02T00:00:00', + initialPaymentDate: '2023-06-01T00:00:00', + finalPaymentDate: '2023-06-02T00:00:00', + daysPaid: 1, + earnings: { + earnings: [ + { earningTypeCode: '046', code: '010046', concept: 'INGRESOS ASIMILADOS A SALARIOS', taxedAmount: 111197.73, exemptAmount: 0.00 } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '002', code: '020002', concept: 'ISR', amount: 36197.73 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 3. NOMINA CON BONOS Y FONDO DE AHORRO (Facturación por referencias) +// ============================================================================ +async function nominaConBonosFondoAhorroByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Con Bonos y Fondo de Ahorro ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'Z0000001234' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '0000000000', + laborRelationStartDate: '2022-03-02T00:00:00', + seniority: 'P66W', + satContractTypeId: '01', + satUnionizedStatusId: 'No', + satTaxRegimeTypeId: '02', + employeeNumber: '111111', + satJobRiskId: '4', + satPaymentPeriodicityId: '02', + integratedDailySalary: 180.96, + satPayrollStateId: 'GUA' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaConBonosFondoAhorroByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Bonos y Fondo de Ahorro ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-06-11T00:00:00', + initialPaymentDate: '2023-06-05T00:00:00', + finalPaymentDate: '2023-06-11T00:00:00', + daysPaid: 7, + earnings: { + earnings: [ + { earningTypeCode: '001', code: 'SP01', concept: 'SUELDO', taxedAmount: 1210.30, exemptAmount: 0.00 }, + { earningTypeCode: '010', code: 'SP02', concept: 'PREMIO PUNTUALIDAD', taxedAmount: 121.03, exemptAmount: 0.00 }, + { earningTypeCode: '029', code: 'SP03', concept: 'MONEDERO ELECTRONICO', taxedAmount: 0.00, exemptAmount: 269.43 }, + { earningTypeCode: '010', code: 'SP04', concept: 'PREMIO DE ASISTENCIA', taxedAmount: 121.03, exemptAmount: 0.00 }, + { earningTypeCode: '005', code: 'SP54', concept: 'APORTACION FONDO AHORRO', taxedAmount: 0.00, exemptAmount: 121.03 } + ], + otherPayments: [ + { + otherPaymentTypeCode: '002', + code: 'ISRSUB', + concept: 'Subsidio ISR para empleo', + amount: 0.0, + subsidyCaused: 0.0, + balanceCompensation: { + favorableBalance: 0.0, + year: 2022, + remainingFavorableBalance: 0.0 + } + } + ] + }, + deductions: [ + { deductionTypeCode: '004', code: 'ZA09', concept: 'APORTACION FONDO AHORRO', amount: 121.03 }, + { deductionTypeCode: '002', code: 'ISR', concept: 'ISR', amount: 36.57 }, + { deductionTypeCode: '001', code: 'IMSS', concept: 'Cuota de Seguridad Social EE', amount: 30.08 }, + { deductionTypeCode: '004', code: 'ZA68', concept: 'DEDUCCION FDO AHORRO PAT', amount: 121.03 }, + { deductionTypeCode: '018', code: 'ZA11', concept: 'APORTACION CAJA AHORRO', amount: 300.00 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 4. NOMINA CON HORAS EXTRA (Facturación por referencias) +// ============================================================================ +async function nominaConHorasExtraByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Con Horas Extra ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaConHorasExtraByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Horas Extra ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 }, + { + earningTypeCode: '019', + code: '00100', + concept: 'Horas Extra', + taxedAmount: 50.00, + exemptAmount: 50.00, + overtime: [ + { days: 1, hoursTypeCode: '01', extraHours: 2, amountPaid: 100.00 } + ] + } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 5. NOMINA CON INCAPACIDADES (Facturación por referencias) +// ============================================================================ +async function nominaConIncapacidadesByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Con Incapacidades ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaConIncapacidadesByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Incapacidades ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ], + disabilities: [ + { disabilityDays: 1, disabilityTypeCode: '01' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 6. NOMINA CON SNCF (Facturación por referencias) +// ============================================================================ +async function nominaConSNCFByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Con SNCF ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(organicosNavezOsorioId); } catch { /* ignore */ } + try { await client.persons.employee.delete(xochiltCasasChavezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: organicosNavezOsorioId, + employerRegistration: '27112029', + satFundSourceId: 'IP' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: organicosNavezOsorioId, + employeePersonId: xochiltCasasChavezId, + socialSecurityNumber: '80997742673', + laborRelationStartDate: '2021-09-01', + seniority: 'P88W', + satContractTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '273', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + integratedDailySalary: 221.48, + satPayrollStateId: 'GRO' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaConSNCFByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con SNCF ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '39074', + exportCode: '01', + issuer: { + id: organicosNavezOsorioId + }, + recipient: { + id: xochiltCasasChavezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-16T00:00:00', + initialPaymentDate: '2023-05-01T00:00:00', + finalPaymentDate: '2023-05-16T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: 'P001', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 3322.20, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P540', concept: 'Compensacion', taxedAmount: 100.00, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P550', concept: 'Compensación Garantizada Extraordinaria', taxedAmount: 2200.00, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P530', concept: 'Servicio Extraordinario', taxedAmount: 200.00, exemptAmount: 0.00 }, + { earningTypeCode: '001', code: 'P506', concept: 'Otras Prestaciones', taxedAmount: 1500.00, exemptAmount: 0.00 }, + { earningTypeCode: '001', code: 'P505', concept: 'Remuneración al Desempeño Legislativo', taxedAmount: 17500.00, exemptAmount: 0.00 } + ], + otherPayments: [ + { otherPaymentTypeCode: '002', code: 'o002', concept: 'Subsidio para el empleo efectivamente entregado al trabajador', amount: 0.00, subsidyCaused: 0.00 } + ] + }, + deductions: [ + { deductionTypeCode: '002', code: 'D002', concept: 'ISR', amount: 4716.61 }, + { deductionTypeCode: '004', code: 'D525', concept: 'Redondeo', amount: 0.81 }, + { deductionTypeCode: '001', code: 'D510', concept: 'Cuota Trabajador ISSSTE', amount: 126.78 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 7. NOMINA EXTRAORDINARIA - AGUINALDO (Facturación por referencias) +// ============================================================================ +async function nominaExtraordinariaByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Extraordinaria (Aguinaldo) ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaExtraordinariaByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Extraordinaria (Aguinaldo) ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-04T00:00:00', + initialPaymentDate: '2023-06-04T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '002', code: '00500', concept: 'Gratificación Anual (Aguinaldo)', taxedAmount: 0.00, exemptAmount: 10000.00 } + ], + otherPayments: [] + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 8. NOMINA SEPARACION INDEMNIZACION (Facturación por referencias) +// ============================================================================ +async function nominaSeparacionIndemnizacionByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Separación Indemnización ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaSeparacionIndemnizacionByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Separación Indemnización ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-04T00:00:00', + initialPaymentDate: '2023-05-05T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '023', code: '00500', concept: 'Pagos por separación', taxedAmount: 0.00, exemptAmount: 10000.00 }, + { earningTypeCode: '025', code: '00900', concept: 'Indemnizaciones', taxedAmount: 0.00, exemptAmount: 500.00 } + ], + otherPayments: [], + severance: { + totalPaid: 10500.00, + yearsOfService: 1, + lastMonthlySalary: 10000.00, + accumulableIncome: 10000.00, + nonAccumulableIncome: 0.00 + } + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 9. NOMINA JUBILACION PENSION RETIRO (Facturación por referencias) +// ============================================================================ +async function nominaJubilacionPensionRetiroByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Jubilación Pensión Retiro ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaJubilacionPensionRetiroByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Jubilación Pensión Retiro ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-05-05T00:00:00', + initialPaymentDate: '2023-06-04T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '039', code: '00500', concept: 'Jubilaciones, pensiones o haberes de retiro', taxedAmount: 0.00, exemptAmount: 10000.00 } + ], + retirement: { + totalOneTime: 10000.00, + accumulableIncome: 10000.00, + nonAccumulableIncome: 0.00 + } + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 10. NOMINA SIN DEDUCCIONES (Facturación por referencias) +// ============================================================================ +async function nominaSinDeduccionesByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Sin Deducciones ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaSinDeduccionesByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Sin Deducciones ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [] + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 11. NOMINA SUBSIDIO CAUSADO (Facturación por referencias) +// ============================================================================ +async function nominaSubsidioCausadoByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Subsidio Causado ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaSubsidioCausadoByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Subsidio Causado ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [ + { otherPaymentTypeCode: '007', code: '0002', concept: 'ISR ajustado por subsidio', amount: 145.80, subsidyCaused: 0.0 } + ] + }, + deductions: [ + { deductionTypeCode: '107', code: 'D002', concept: 'Ajuste al Subsidio Causado', amount: 160.35 }, + { deductionTypeCode: '002', code: 'D002', concept: 'ISR', amount: 145.80 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 12. NOMINA VIATICOS (Facturación por referencias) +// ============================================================================ +async function nominaViaticosByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina Viáticos ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P438W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaViaticosByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Viáticos ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-09-26T00:00:00', + initialPaymentDate: '2023-09-11T00:00:00', + finalPaymentDate: '2023-09-26T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '050', code: '050', concept: 'Viaticos', taxedAmount: 0, exemptAmount: 3000 } + ] + }, + deductions: [ + { deductionTypeCode: '081', code: '081', concept: 'Ajuste en viaticos entregados al trabajador', amount: 3000 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 13. NOMINA GENERAL (Facturación por referencias) +// ============================================================================ +async function nominaGeneralByReferencesSetupData(client: FiscalapiClient): Promise { + console.log('\n=== Setup: Nómina General ByReferences ===\n'); + + // Eliminar datos existentes (idempotente) + try { await client.persons.employer.delete(escuelaKemperUrgateId); } catch { /* ignore */ } + try { await client.persons.employee.delete(ingridXodarJimenezId); } catch { /* ignore */ } + + // Crear datos de empleador + const employerRequest: CreateEmployerRequest = { + personId: escuelaKemperUrgateId, + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }; + const employerResult = await client.persons.employer.create(employerRequest); + console.log('Empleador:', JSON.stringify(employerResult, null, 2)); + + // Crear datos de empleado + const employeeRequest: CreateEmployeeRequest = { + employerPersonId: escuelaKemperUrgateId, + employeePersonId: ingridXodarJimenezId, + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + }; + const employeeResult = await client.persons.employee.create(employeeRequest); + console.log('Empleado:', JSON.stringify(employeeResult, null, 2)); +} + +async function nominaGeneralByReferences(client: FiscalapiClient): Promise { + console.log('\n=== Nómina General ByReferences ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + id: escuelaKemperUrgateId + }, + recipient: { + id: ingridXodarJimenezId + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// FUNCION PRINCIPAL +// ============================================================================ +async function main(): Promise { + console.log('=== Ejemplos de Factura Nómina FiscalAPI (Referencias) ===\n'); + + const client = FiscalapiClient.create(settings); + + try { + // Descomentar el caso de uso que se desea ejecutar + // IMPORTANTE: Ejecutar primero la función de setup, luego la de factura + + // 1. Nómina Ordinaria + await nominaOrdinariaByReferencesSetupData(client); + await nominaOrdinariaByReferences(client); + + // 2. Nómina Asimilados + // await nominaAsimiladosByReferencesSetupData(client); + // await nominaAsimiladosByReferences(client); + + // 3. Nómina Con Bonos y Fondo de Ahorro + // await nominaConBonosFondoAhorroByReferencesSetupData(client); + // await nominaConBonosFondoAhorroByReferences(client); + + // 4. Nómina Con Horas Extra + // await nominaConHorasExtraByReferencesSetupData(client); + // await nominaConHorasExtraByReferences(client); + + // 5. Nómina Con Incapacidades + // await nominaConIncapacidadesByReferencesSetupData(client); + // await nominaConIncapacidadesByReferences(client); + + // 6. Nómina Con SNCF + // await nominaConSNCFByReferencesSetupData(client); + // await nominaConSNCFByReferences(client); + + // 7. Nómina Extraordinaria (Aguinaldo) + // await nominaExtraordinariaByReferencesSetupData(client); + // await nominaExtraordinariaByReferences(client); + + // 8. Nómina Separación Indemnización + // await nominaSeparacionIndemnizacionByReferencesSetupData(client); + // await nominaSeparacionIndemnizacionByReferences(client); + + // 9. Nómina Jubilación Pensión Retiro + // await nominaJubilacionPensionRetiroByReferencesSetupData(client); + // await nominaJubilacionPensionRetiroByReferences(client); + + // 10. Nómina Sin Deducciones + // await nominaSinDeduccionesByReferencesSetupData(client); + // await nominaSinDeduccionesByReferences(client); + + // 11. Nómina Subsidio Causado + // await nominaSubsidioCausadoByReferencesSetupData(client); + // await nominaSubsidioCausadoByReferences(client); + + // 12. Nómina Viáticos + // await nominaViaticosByReferencesSetupData(client); + // await nominaViaticosByReferences(client); + + // 13. Nómina General + await nominaGeneralByReferencesSetupData(client); + await nominaGeneralByReferences(client); + + console.log('\nEjecución completada.'); + } catch (error) { + console.error('Error:', error); + } +} + +// Ejecutar función principal +main(); diff --git a/examples/ejemplos-factura-nomina-valores.ts b/examples/ejemplos-factura-nomina-valores.ts new file mode 100644 index 0000000..69706a3 --- /dev/null +++ b/examples/ejemplos-factura-nomina-valores.ts @@ -0,0 +1,1127 @@ +/** + * Ejemplos de facturas de nómina (CFDI Nómina) usando el SDK de FiscalAPI + * Todos los métodos usan el modo "ByValues" - los datos se pasan directamente en la petición HTTP + */ + +import { FiscalapiClient, FiscalapiSettings, Invoice } from '../src/index'; +import { inspect } from 'util'; + +// Configuración de la consola para mostrar objetos anidados +inspect.defaultOptions.depth = null; +inspect.defaultOptions.colors = true; + +// Configuración de FiscalAPI +const settings: FiscalapiSettings = { + apiUrl: 'https://test.fisalapi.com', + apiKey: '', + tenant: '', + debug: true +}; + +// Sellos SAT de prueba +const escuelaKemperUrgateBase64Cer = "MIIFsDCCA5igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTYwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTE0MzUxWhcNMjcwNTE4MTE0MzUxWjCB1zEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjElMCMGA1UELRMcRUtVOTAwMzE3M0M5IC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmecO6n2GS0zL025gbHGQVxznPDICoXzR2uUngz4DqxVUC/w9cE6FxSiXm2ap8Gcjg7wmcZfm85EBaxCx/0J2u5CqnhzIoGCdhBPuhWQnIh5TLgj/X6uNquwZkKChbNe9aeFirU/JbyN7Egia9oKH9KZUsodiM/pWAH00PCtoKJ9OBcSHMq8Rqa3KKoBcfkg1ZrgueffwRLws9yOcRWLb02sDOPzGIm/jEFicVYt2Hw1qdRE5xmTZ7AGG0UHs+unkGjpCVeJ+BEBn0JPLWVvDKHZAQMj6s5Bku35+d/MyATkpOPsGT/VTnsouxekDfikJD1f7A1ZpJbqDpkJnss3vQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFaUgj5PqgvJigNMgtrdXZnbPfVBbukAbW4OGnUhNrA7SRAAfv2BSGk16PI0nBOr7qF2mItmBnjgEwk+DTv8Zr7w5qp7vleC6dIsZFNJoa6ZndrE/f7KO1CYruLXr5gwEkIyGfJ9NwyIagvHHMszzyHiSZIA850fWtbqtythpAliJ2jF35M5pNS+YTkRB+T6L/c6m00ymN3q9lT1rB03YywxrLreRSFZOSrbwWfg34EJbHfbFXpCSVYdJRfiVdvHnewN0r5fUlPtR9stQHyuqewzdkyb5jTTw02D2cUfL57vlPStBj7SEi3uOWvLrsiDnnCIxRMYJ2UA2ktDKHk+zWnsDmaeleSzonv2CHW42yXYPCvWi88oE1DJNYLNkIjua7MxAnkNZbScNw01A6zbLsZ3y8G6eEYnxSTRfwjd8EP4kdiHNJftm7Z4iRU7HOVh79/lRWB+gd171s3d/mI9kte3MRy6V8MMEMCAnMboGpaooYwgAmwclI2XZCczNWXfhaWe0ZS5PmytD/GDpXzkX0oEgY9K/uYo5V77NdZbGAjmyi8cE2B2ogvyaN2XfIInrZPgEffJ4AB7kFA2mwesdLOCh0BLD9itmCve3A1FGR4+stO2ANUoiI3w3Tv2yQSg4bjeDlJ08lXaaFCLW2peEXMXjQUk7fmpb5MNuOUTW6BE=" +const escuelaKemperUrgateBase64Key = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS/AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucoZQObOaLUEm+I+QZ7Y8Giupo+F1XWkLvAsdk/uZlJcTfKLJyJbJwsQYbSpLOCLataZ4O5MVnnmMbfG//NKJn9kSMvJQZhSwAwoGLYDm1ESGezrvZabgFJnoQv8Si1nAhVGTk9FkFBesxRzq07dmZYwFCnFSX4xt2fDHs1PMpQbeq83aL/PzLCce3kxbYSB5kQlzGtUYayiYXcu0cVRu228VwBLCD+2wTDDoCmRXtPesgrLKUR4WWWb5N2AqAU1mNDC+UEYsENAerOFXWnmwrcTAu5qyZ7GsBMTpipW4Dbou2yqQ0lpA/aB06n1kz1aL6mNqGPaJ+OqoFuc8Ugdhadd+MmjHfFzoI20SZ3b2geCsUMNCsAd6oXMsZdWm8lzjqCGWHFeol0ik/xHMQvuQkkeCsQ28PBxdnUgf7ZGer+TN+2ZLd2kvTBOk6pIVgy5yC6cZ+o1Tloql9hYGa6rT3xcMbXlW+9e5jM2MWXZliVW3ZhaPjptJFDbIfWxJPjz4QvKyJk0zok4muv13Iiwj2bCyefUTRz6psqI4cGaYm9JpscKO2RCJN8UluYGbbWmYQU+Int6LtZj/lv8p6xnVjWxYI+rBPdtkpfFYRp+MJiXjgPw5B6UGuoruv7+vHjOLHOotRo+RdjZt7NqL9dAJnl1Qb2jfW6+d7NYQSI/bAwxO0sk4taQIT6Gsu/8kfZOPC2xk9rphGqCSS/4q3Os0MMjA1bcJLyoWLp13pqhK6bmiiHw0BBXH4fbEp4xjSbpPx4tHXzbdn8oDsHKZkWh3pPC2J/nVl0k/yF1KDVowVtMDXE47k6TGVcBoqe8PDXCG9+vjRpzIidqNo5qebaUZu6riWMWzldz8x3Z/jLWXuDiM7/Yscn0Z2GIlfoeyz+GwP2eTdOw9EUedHjEQuJY32bq8LICimJ4Ht+zMJKUyhwVQyAER8byzQBwTYmYP5U0wdsyIFitphw+/IH8+v08Ia1iBLPQAeAvRfTTIFLCs8foyUrj5Zv2B/wTYIZy6ioUM+qADeXyo45uBLLqkN90Rf6kiTqDld78NxwsfyR5MxtJLVDFkmf2IMMJHTqSfhbi+7QJaC11OOUJTD0v9wo0X/oO5GvZhe0ZaGHnm9zqTopALuFEAxcaQlc4R81wjC4wrIrqWnbcl2dxiBtD73KW+wcC9ymsLf4I8BEmiN25lx/OUc1IHNyXZJYSFkEfaxCEZWKcnbiyf5sqFSSlEqZLc4lUPJFAoP6s1FHVcyO0odWqdadhRZLZC9RCzQgPlMRtji/OXy5phh7diOBZv5UYp5nb+MZ2NAB/eFXm2JLguxjvEstuvTDmZDUb6Uqv++RdhO5gvKf/AcwU38ifaHQ9uvRuDocYwVxZS2nr9rOwZ8nAh+P2o4e0tEXjxFKQGhxXYkn75H3hhfnFYjik/2qunHBBZfcdG148MaNP6DjX33M238T9Zw/GyGx00JMogr2pdP4JAErv9a5yt4YR41KGf8guSOUbOXVARw6+ybh7+meb7w4BeTlj3aZkv8tVGdfIt3lrwVnlbzhLjeQY6PplKp3/a5Kr5yM0T4wJoKQQ6v3vSNmrhpbuAtKxpMILe8CQoo=" +const organicosNavezOsorioBase64Cer = "MIIF1DCCA7ygAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MzkwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTI1NTE2WhcNMjcwNTE4MTI1NTE2WjCB+zEzMDEGA1UEAxQqT1JHQU5JQ09TINFBVkVaIE9TT1JJTyBTLkEgREUgQy5WIFNBIERFIENWMTMwMQYDVQQpFCpPUkdBTklDT1Mg0UFWRVogT1NPUklPIFMuQSBERSBDLlYgU0EgREUgQ1YxMzAxBgNVBAoUKk9SR0FOSUNPUyDRQVZFWiBPU09SSU8gUy5BIERFIEMuViBTQSBERSBDVjElMCMGA1UELRQcT9FPMTIwNzI2UlgzIC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlAF4PoRqITQAEjFBzzfiT/NSN2yvb7Iv1ZMe4qD7tBxBxazRCx+GnimfpR+eaM744RlRDUj+hZfWcsOMn+q65UEIP+Xq5V1NbO1LZDse9uG1fLLSmptfKjyfvTtmBNYBjC3G6YmRv5qVw81CIS4aQOSMXKD+lrxjmRUhV9EAtXVoqGxvyDKeeX4caKuRz8mlrnR8/SMbnpobe5BNoXPrpDbEypemiJXe40pjsltY0RV3b0W0JtJQABUwZ9xn0lPYHY2q7IxYfohibv+o9ldXOXY6tivBZFfbGQSUp7CevC55+Y6uqh35Pi1o0nt/vBVgUOVPNM8d4TvGbXsE0G2J7QIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFp52XykMXfFUtjQqA2zzLPrPIDSMEpkm1vWY0qfz2gC2TlVpbDCWH2vFHpP8D14OifXOmYkws2cvLyE0uBN6se4zXxVHBpTEq+93rvu/tjvMU6r7DISDwB0EX5kmKIFcOugET3/Eq1mxZ6mrI0K26RaEUz+HVyR0EQ2Ll5CLExDkPYV/am0gynhn6QPkxPNbcbm77PEIbH7zc+t7ZB5sgQ6LnubgnKNZDn8bNhkuM1jqFkh7h0owhlJrOvATgrDSLnrot8FoLFkrWQD4uA5udGRwXn5QWx0QM5ScNiSgSRilSFEyXn6rH/CJLO05Sx5OwJJTaxFbAyOXnoNdPMzbQAziaW78478nCNZVSrKWpjwWpScirtM2zcQ9fywd/a3CG66Ff29zasfhHJCp29TIjj1OURp6l1CKc16+UxjuVJ1z5Xh7v3s8S2gtmuYP1sUXPvAEYuVp9CFW87QVMtl3+nGlyJEzSAW/yaps9ua5RmyJK0Mjk1zyXjOJoIY75CIOMN8oqVAxmLJg5XftXJSekGpxybw9aq9qOJdmxVcZoAFaYg4MAdKViBoYxfWfEm4q/ihRz4asnzLp9NJWTXN1YH94rJrK7JSEq820flgr1kiL7z7n1rgWMvhJH9nHriG3yRkno/8OdLJxOSXd7MKZfZx0EWDX8toqWyE7zia8aPM=" +const organicosNavezOsorioBase64Key = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS8AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucRFLOMmsAaFFEdAecnfgJf0IlyJpvyNOGiSwXgY6uZtS0QJmmupWTlQATxbN4xeN7csx7yCMYxMiWXLyTbjVIWzzsFVKHbsxCudz6UDqMZ3aXEEPDDbPECXJC4FxqzuUgifN4QQuIvxfPbk23m3Vtqu9lr/xMrDNqLZ4RiqY2062kgQzGzekq8CSC97qBAbb8SFMgakFjeHN0JiTGaTpYCpGbu4d+i3ZrQ0mlYkxesdvCLqlCwVM0RTMJsNQ8vpBpRDzH372iOTLCO/gXtV8pEsxpUzG9LSUBo7xSMd1/lcfdyqVgnScgUm8/+toxk6uwZkUMWWvp7tqrMYQFYdR5CjiZjgAWrNorgMmawBqkJU6KQO/CpXVn99U1fANPfQoeyQMgLt35k0JKynG8MuWsgb4EG9Z6sRmOsCQQDDMKwhBjqcbEwN2dL4f1HyN8wklFCyYy6j1NTKU2AjRMXVu4+OlAp5jpjgv08RQxEkW/tNMSSBcpvOzNr64u0M692VA2fThR3UMQ/MZ2yVM6yY3GgIu2tJmg08lhmkoLpWZIMy7bZjj/AEbi7B3wSF4vDYZJcr/Djeezm3MMSghoiOIRSqtBjwf7ZjhA2ymdCsrzy7XSMVekT0y1S+ew1WhnzUNKQSucb6V2yRwNbm0EyeEuvVyHgiGEzCrzNbNHCfoFr69YCUi8itiDfiV7/p7LJzD8J/w85nmOkI/9p+aZ2EyaOdThqBmN4CtoDi5ixz/1EElLn7KVI4d/DZsZ4ZMu76kLAy94o0m6ORSbHX5hw12+P5DgGaLu/Dxd9cctRCkvcUdagiECuKGLJpxTJvEBQoZqUB8AJFgwKcNLl3Z5KAWL5hV0t1h8i3N4HllygqpfUSQMLWCtlGwdI4XGlGI5CmnjrL2Uj8sj9C0zSNqZVnAXFMV9f2ND9W6YJqfU89BQ6Y4QQRMGjXcVF7c78bn5r6zI+Qv2QKm3YiGCfuIa64B+PB/BdithpOuBPn5X5Zxc8ju/kYjJk7sau7VtKJseGOJ1bqOq99VzaxoHjzoJgthLHtni9WtGAnnQy7GMWGW4Un2yObHCxvQxx/rIZEaQiCGfRXOcZIZuXBe5xeHJFGrekDxu3YyumEnLWvsirDF3qhpUtxqvbkTuZw2xT3vTR+oWZpSEnYTd3k/09Eb0ovOPLkbhvcvCEeoI91EJvU+KI4Lm7ZsuTUSpECrHiS3uPOjboCigOWGayKzUHUICNrGK0zxgZXhhl6V7y9pImRl34ID/tZhr3veW4pQKgscv6sQjGJzaph2oCP7uZC6arGWcFpc2pgfBcobmOXYPWKskU3eWKClHBJnJ8MoOru+ObOb+izPhINHOmzP26TnKzFxdZiL+onxjadPYslcLtqlmOYpb/5hHgGOvitLhCLHCp0gYNB2uzj0sVxNs3k7k43KrlO5L6gp1KVaIw2a1yZzOCqDWWcePfKM3Mii9JdVyfHZLRRjFCQiOYo41AltHU+9IcaoT4J/j7pKw5tnlu2VaMlnN0dISpoq/ak0m4YjTd3XdRQeH9ktWmclkc65LdLKf9hIqjVqvOhQUJYkuT7OPgr+o7Z9BnClXMz1/CYWftwQE=" +const password = "12345678a" +const currentDate = '2026-01-27T10:04:06'; + + +// ============================================================================ +// 1. NOMINA ORDINARIA (Facturación por valores) +// ============================================================================ +async function nominaOrdinariaByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Ordinaria ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'FUNK671228PH6', + legalName: 'KARLA FUENTE NOLASCO', + zipCode: '01160', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101MNEXXXA8', + socialSecurityNumber: '04078873454', + laborRelationStartDate: '2024-08-18', + seniority: 'P54W', + satContractTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '123456789', + department: 'GenAI', + position: 'Sr Software Engineer', + satJobRiskId: '1', + satPaymentPeriodicityId: '05', + satBankId: '012', + baseSalaryForContributions: 2828.50, + integratedDailySalary: 0.00, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2025-08-30', + initialPaymentDate: '2025-07-31', + finalPaymentDate: '2025-08-30', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '1003', concept: 'Sueldo Nominal', taxedAmount: 95030.00, exemptAmount: 0.00 }, + { earningTypeCode: '005', code: '5913', concept: 'Fondo de Ahorro Aportación Patrón', taxedAmount: 0.00, exemptAmount: 4412.46 }, + { earningTypeCode: '038', code: '1885', concept: 'Bono Ingles', taxedAmount: 14254.50, exemptAmount: 0.00 }, + { earningTypeCode: '029', code: '1941', concept: 'Vales Despensa', taxedAmount: 0.00, exemptAmount: 3439.00 }, + { earningTypeCode: '038', code: '1824', concept: 'Herramientas Teletrabajo (telecom y prop. electri)', taxedAmount: 273.00, exemptAmount: 0.00 } + ], + otherPayments: [ + { otherPaymentTypeCode: '002', code: '5050', concept: 'Exceso de subsidio al empleo', amount: 0.00, subsidyCaused: 0.00 } + ] + }, + deductions: [ + { deductionTypeCode: '002', code: '5003', concept: 'ISR Causado', amount: 27645.52 }, + { deductionTypeCode: '004', code: '5910', concept: 'Fondo de ahorro Empleado Inversión', amount: 4412.46 }, + { deductionTypeCode: '004', code: '5914', concept: 'Fondo de Ahorro Patrón Inversión', amount: 4412.46 }, + { deductionTypeCode: '004', code: '1966', concept: 'Contribución póliza exceso GMM', amount: 519.91 }, + { deductionTypeCode: '004', code: '1934', concept: 'Descuento Vales Despensa', amount: 1.00 }, + { deductionTypeCode: '004', code: '1942', concept: 'Vales Despensa Electrónico', amount: 3439.00 }, + { deductionTypeCode: '001', code: '1895', concept: 'IMSS', amount: 2391.13 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 2. NOMINA ASIMILADOS (Facturación por valores) +// ============================================================================ +async function nominaAsimiladosByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Asimilados ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '06880', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + originEmployerTin: 'EKU9003173C9' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'CACX7605101P8', + legalName: 'XOCHILT CASAS CHAVEZ', + zipCode: '36257', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + satContractTypeId: '09', + satUnionizedStatusId: 'No', + satTaxRegimeTypeId: '09', + employeeNumber: '00002', + department: 'ADMINISTRACION', + position: 'DIRECTOR DE ADMINISTRACION', + satPaymentPeriodicityId: '99', + satBankId: '012', + bankAccount: '1111111111', + satPayrollStateId: 'CMX' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-02T00:00:00', + initialPaymentDate: '2023-06-01T00:00:00', + finalPaymentDate: '2023-06-02T00:00:00', + daysPaid: 1, + earnings: { + earnings: [ + { earningTypeCode: '046', code: '010046', concept: 'INGRESOS ASIMILADOS A SALARIOS', taxedAmount: 111197.73, exemptAmount: 0.00 } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '002', code: '020002', concept: 'ISR', amount: 36197.73 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 3. NOMINA CON BONOS Y FONDO DE AHORRO (Facturación por valores) +// ============================================================================ +async function nominaConBonosFondoAhorroByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Bonos y Fondo de Ahorro ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'Z0000001234' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101MNEXXXA8', + socialSecurityNumber: '0000000000', + laborRelationStartDate: '2022-03-02T00:00:00', + seniority: 'P66W', + satContractTypeId: '01', + satUnionizedStatusId: 'No', + satTaxRegimeTypeId: '02', + employeeNumber: '111111', + satJobRiskId: '4', + satPaymentPeriodicityId: '02', + integratedDailySalary: 180.96, + satPayrollStateId: 'GUA' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-06-11T00:00:00', + initialPaymentDate: '2023-06-05T00:00:00', + finalPaymentDate: '2023-06-11T00:00:00', + daysPaid: 7, + earnings: { + earnings: [ + { earningTypeCode: '001', code: 'SP01', concept: 'SUELDO', taxedAmount: 1210.30, exemptAmount: 0.00 }, + { earningTypeCode: '010', code: 'SP02', concept: 'PREMIO PUNTUALIDAD', taxedAmount: 121.03, exemptAmount: 0.00 }, + { earningTypeCode: '029', code: 'SP03', concept: 'MONEDERO ELECTRONICO', taxedAmount: 0.00, exemptAmount: 269.43 }, + { earningTypeCode: '010', code: 'SP04', concept: 'PREMIO DE ASISTENCIA', taxedAmount: 121.03, exemptAmount: 0.00 }, + { earningTypeCode: '005', code: 'SP54', concept: 'APORTACION FONDO AHORRO', taxedAmount: 0.00, exemptAmount: 121.03 } + ], + otherPayments: [ + { + otherPaymentTypeCode: '002', + code: 'ISRSUB', + concept: 'Subsidio ISR para empleo', + amount: 0.0, + subsidyCaused: 0.0, + balanceCompensation: { + favorableBalance: 0.0, + year: 2022, + remainingFavorableBalance: 0.0 + } + } + ] + }, + deductions: [ + { deductionTypeCode: '004', code: 'ZA09', concept: 'APORTACION FONDO AHORRO', amount: 121.03 }, + { deductionTypeCode: '002', code: 'ISR', concept: 'ISR', amount: 36.57 }, + { deductionTypeCode: '001', code: 'IMSS', concept: 'Cuota de Seguridad Social EE', amount: 30.08 }, + { deductionTypeCode: '004', code: 'ZA68', concept: 'DEDUCCION FDO AHORRO PAT', amount: 121.03 }, + { deductionTypeCode: '018', code: 'ZA11', concept: 'APORTACION CAJA AHORRO', amount: 300.00 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 4. NOMINA CON HORAS EXTRA (Facturación por valores) +// ============================================================================ +async function nominaConHorasExtraByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Horas Extra ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 }, + { + earningTypeCode: '019', + code: '00100', + concept: 'Horas Extra', + taxedAmount: 50.00, + exemptAmount: 50.00, + overtime: [ + { days: 1, hoursTypeCode: '01', extraHours: 2, amountPaid: 100.00 } + ] + } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 5. NOMINA CON INCAPACIDADES (Facturación por valores) +// ============================================================================ +async function nominaConIncapacidadesByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con Incapacidades ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ], + disabilities: [ + { disabilityDays: 1, disabilityTypeCode: '01' } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 6. NOMINA CON SNCF (Facturación por valores) +// ============================================================================ +async function nominaConSNCFByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Con SNCF ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '39074', + exportCode: '01', + issuer: { + tin: 'OÑO120726RX3', + legalName: 'ORGANICOS ÑAVEZ OSORIO', + taxRegimeCode: '601', + employerData: { + employerRegistration: '27112029', + satFundSourceId: 'IP' + }, + taxCredentials: [ + { base64File: organicosNavezOsorioBase64Cer, fileType: 0, password }, + { base64File: organicosNavezOsorioBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'CACX7605101P8', + legalName: 'XOCHILT CASAS CHAVEZ', + zipCode: '36257', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '80997742673', + laborRelationStartDate: '2021-09-01', + seniority: 'P88W', + satContractTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '273', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + integratedDailySalary: 221.48, + satPayrollStateId: 'GRO' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-16T00:00:00', + initialPaymentDate: '2023-05-01T00:00:00', + finalPaymentDate: '2023-05-16T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: 'P001', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 3322.20, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P540', concept: 'Compensacion', taxedAmount: 100.00, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P550', concept: 'Compensación Garantizada Extraordinaria', taxedAmount: 2200.00, exemptAmount: 0.00 }, + { earningTypeCode: '038', code: 'P530', concept: 'Servicio Extraordinario', taxedAmount: 200.00, exemptAmount: 0.00 }, + { earningTypeCode: '001', code: 'P506', concept: 'Otras Prestaciones', taxedAmount: 1500.00, exemptAmount: 0.00 }, + { earningTypeCode: '001', code: 'P505', concept: 'Remuneración al Desempeño Legislativo', taxedAmount: 17500.00, exemptAmount: 0.00 } + ], + otherPayments: [ + { otherPaymentTypeCode: '002', code: 'o002', concept: 'Subsidio para el empleo efectivamente entregado al trabajador', amount: 0.00, subsidyCaused: 0.00 } + ] + }, + deductions: [ + { deductionTypeCode: '002', code: 'D002', concept: 'ISR', amount: 4716.61 }, + { deductionTypeCode: '004', code: 'D525', concept: 'Redondeo', amount: 0.81 }, + { deductionTypeCode: '001', code: 'D510', concept: 'Cuota Trabajador ISSSTE', amount: 126.78 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 7. NOMINA EXTRAORDINARIA - AGUINALDO (Facturación por valores) +// ============================================================================ +async function nominaExtraordinariaByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Extraordinaria (Aguinaldo) ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-04T00:00:00', + initialPaymentDate: '2023-06-04T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '002', code: '00500', concept: 'Gratificación Anual (Aguinaldo)', taxedAmount: 0.00, exemptAmount: 10000.00 } + ], + otherPayments: [] + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 8. NOMINA SEPARACION INDEMNIZACION (Facturación por valores) +// ============================================================================ +async function nominaSeparacionIndemnizacionByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Separación Indemnización ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-06-04T00:00:00', + initialPaymentDate: '2023-05-05T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '023', code: '00500', concept: 'Pagos por separación', taxedAmount: 0.00, exemptAmount: 10000.00 }, + { earningTypeCode: '025', code: '00900', concept: 'Indemnizaciones', taxedAmount: 0.00, exemptAmount: 500.00 } + ], + otherPayments: [], + severance: { + totalPaid: 10500.00, + yearsOfService: 1, + lastMonthlySalary: 10000.00, + accumulableIncome: 10000.00, + nonAccumulableIncome: 0.00 + } + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 9. NOMINA JUBILACION PENSION RETIRO (Facturación por valores) +// ============================================================================ +async function nominaJubilacionPensionRetiroByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Jubilación Pensión Retiro ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P439W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '99', + satBankId: '002', + bankAccount: '1111111111', + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'E', + paymentDate: '2023-05-05T00:00:00', + initialPaymentDate: '2023-06-04T00:00:00', + finalPaymentDate: '2023-06-04T00:00:00', + daysPaid: 30, + earnings: { + earnings: [ + { earningTypeCode: '039', code: '00500', concept: 'Jubilaciones, pensiones o haberes de retiro', taxedAmount: 0.00, exemptAmount: 10000.00 } + ], + retirement: { + totalOneTime: 10000.00, + accumulableIncome: 10000.00, + nonAccumulableIncome: 0.00 + } + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 10. NOMINA SIN DEDUCCIONES (Facturación por valores) +// ============================================================================ +async function nominaSinDeduccionesByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Sin Deducciones ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [] + }, + deductions: [] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 11. NOMINA SUBSIDIO CAUSADO (Facturación por valores) +// ============================================================================ +async function nominaSubsidioCausadoByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Subsidio Causado ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '02', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [ + { otherPaymentTypeCode: '007', code: '0002', concept: 'ISR ajustado por subsidio', amount: 145.80, subsidyCaused: 0.0 } + ] + }, + deductions: [ + { deductionTypeCode: '107', code: 'D002', concept: 'Ajuste al Subsidio Causado', amount: 160.35 }, + { deductionTypeCode: '002', code: 'D002', concept: 'ISR', amount: 145.80 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 12. NOMINA VIATICOS (Facturación por valores) +// ============================================================================ +async function nominaViaticosByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina Viáticos ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P438W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-09-26T00:00:00', + initialPaymentDate: '2023-09-11T00:00:00', + finalPaymentDate: '2023-09-26T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '050', code: '050', concept: 'Viaticos', taxedAmount: 0, exemptAmount: 3000 } + ] + }, + deductions: [ + { deductionTypeCode: '081', code: '081', concept: 'Ajuste en viaticos entregados al trabajador', amount: 3000 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// 13. NOMINA GENERAL (Facturación por valores) +// ============================================================================ +async function nominaGeneralByValues(client: FiscalapiClient): Promise { + console.log('\n=== Nómina General ByValues ===\n'); + + const invoice: Invoice = { + versionCode: '4.0', + series: 'F', + date: currentDate, + paymentMethodCode: 'PUE', + currencyCode: 'MXN', + typeCode: 'N', + expeditionZipCode: '20000', + exportCode: '01', + issuer: { + tin: 'EKU9003173C9', + legalName: 'ESCUELA KEMPER URGATE', + taxRegimeCode: '601', + employerData: { + employerRegistration: 'B5510768108', + originEmployerTin: 'URE180429TM6' + }, + taxCredentials: [ + { base64File: escuelaKemperUrgateBase64Cer, fileType: 0, password }, + { base64File: escuelaKemperUrgateBase64Key, fileType: 1, password } + ] + }, + recipient: { + tin: 'XOJI740919U48', + legalName: 'INGRID XODAR JIMENEZ', + zipCode: '76028', + taxRegimeCode: '605', + cfdiUseCode: 'CN01', + employeeData: { + curp: 'XEXX010101HNEXXXA4', + socialSecurityNumber: '000000', + laborRelationStartDate: '2015-01-01T00:00:00', + seniority: 'P437W', + satContractTypeId: '01', + satWorkdayTypeId: '01', + satTaxRegimeTypeId: '03', + employeeNumber: '120', + department: 'Desarrollo', + position: 'Ingeniero de Software', + satJobRiskId: '1', + satPaymentPeriodicityId: '04', + satBankId: '002', + bankAccount: '1111111111', + baseSalaryForContributions: 490.22, + integratedDailySalary: 146.47, + satPayrollStateId: 'JAL' + } + }, + complement: { + payroll: { + version: '1.2', + payrollTypeCode: 'O', + paymentDate: '2023-05-24T00:00:00', + initialPaymentDate: '2023-05-09T00:00:00', + finalPaymentDate: '2023-05-24T00:00:00', + daysPaid: 15, + earnings: { + earnings: [ + { earningTypeCode: '001', code: '00500', concept: 'Sueldos, Salarios Rayas y Jornales', taxedAmount: 2808.8, exemptAmount: 2191.2 } + ], + otherPayments: [] + }, + deductions: [ + { deductionTypeCode: '001', code: '00301', concept: 'Seguridad Social', amount: 200 }, + { deductionTypeCode: '002', code: '00302', concept: 'ISR', amount: 100 } + ] + } + } + }; + + const response = await client.invoices.create(invoice); + console.log('Response:', response); +} + +// ============================================================================ +// FUNCION PRINCIPAL +// ============================================================================ +async function main(): Promise { + console.log('=== Ejemplos de Factura Nómina FiscalAPI ===\n'); + + const client = FiscalapiClient.create(settings); + + try { + // Descomentar el caso de uso que se desea ejecutar + + await nominaOrdinariaByValues(client); + // await nominaAsimiladosByValues(client); + // await nominaConBonosFondoAhorroByValues(client); + // await nominaConHorasExtraByValues(client); + // await nominaConIncapacidadesByValues(client); + // await nominaConSNCFByValues(client); + // await nominaExtraordinariaByValues(client); + // await nominaSeparacionIndemnizacionByValues(client); + // await nominaJubilacionPensionRetiroByValues(client); + // await nominaSinDeduccionesByValues(client); + // await nominaSubsidioCausadoByValues(client); + // await nominaViaticosByValues(client); + // await nominaGeneralByValues(client); + + console.log('\nEjecución completada.'); + } catch (error) { + console.error('Error:', error); + } +} + +// Ejecutar función principal +main(); diff --git a/package-lock.json b/package-lock.json index b3e6765..336f31a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "fiscalapi", - "version": "4.0.141", + "version": "4.0.270", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fiscalapi", - "version": "4.0.141", + "version": "4.0.270", "license": "MPL-2.0", "dependencies": { "axios": "^1.8.4", + "form-data": "^4.0.4", "luxon": "^3.6.0" }, "devDependencies": { @@ -35,11 +36,35 @@ "node": ">=12" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -112,6 +137,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.20.0" } @@ -141,10 +167,11 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -153,10 +180,11 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -176,30 +204,16 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -217,6 +231,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -228,7 +243,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -270,10 +286,11 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -295,13 +312,15 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-define-property": { "version": "1.0.1", @@ -380,13 +399,15 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -437,14 +458,15 @@ } }, "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -511,6 +533,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -522,10 +545,11 @@ "dev": true }, "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -587,12 +611,13 @@ } }, "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -703,6 +728,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -721,6 +747,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -735,6 +762,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -743,13 +771,15 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -758,10 +788,11 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -778,6 +809,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -790,6 +822,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -842,6 +875,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -882,6 +916,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -900,6 +935,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -917,6 +953,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -926,6 +963,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -940,13 +978,15 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -961,6 +1001,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, diff --git a/package.json b/package.json index b231c18..d853dfe 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "SDK de Node.js para Fiscalapi", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", - "types": "dist/types/index.d.ts", + "types": "dist/cjs/index.d.ts", "files": [ "dist/", "README.md", @@ -14,10 +14,10 @@ ], "scripts": { "clean": "rimraf dist", - "build": "npm run clean && npm run build:esm && npm run build:cjs && npm run build:types", - "build:esm": "tsc -p tsconfig.esm.json", + "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:package-json", + "build:esm": "tsc -p tsconfig.esm.json && node scripts/fix-esm-imports.js", "build:cjs": "tsc -p tsconfig.cjs.json", - "build:types": "tsc -p tsconfig.types.json", + "build:package-json": "node -e \"require('fs').writeFileSync('dist/cjs/package.json','{\\\"type\\\":\\\"commonjs\\\"}');require('fs').writeFileSync('dist/esm/package.json','{\\\"type\\\":\\\"module\\\"}')\"", "test": "jest", "lint": "eslint 'src/**/*.ts'", "prepare": "npm run build", @@ -38,9 +38,9 @@ }, "exports": { ".": { + "types": "./dist/cjs/index.d.ts", "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js", - "types": "./dist/types/index.d.ts" + "require": "./dist/cjs/index.js" } }, "repository": { @@ -53,6 +53,7 @@ }, "dependencies": { "axios": "^1.8.4", + "form-data": "^4.0.4", "luxon": "^3.6.0" }, "devDependencies": { diff --git a/scripts/fix-esm-imports.js b/scripts/fix-esm-imports.js new file mode 100644 index 0000000..c16b9f4 --- /dev/null +++ b/scripts/fix-esm-imports.js @@ -0,0 +1,92 @@ +/** + * Post-build script to add .js extensions to ESM imports/exports + * Required for Node.js native ESM support + */ +const fs = require('fs'); +const path = require('path'); + +const esmDir = path.join(__dirname, '..', 'dist', 'esm'); + +// Extensions that should NOT have .js added +const SKIP_EXTENSIONS = ['.js', '.mjs', '.cjs', '.json', '.node']; + +// Stats for logging +let stats = { + filesProcessed: 0, + importsFixed: 0 +}; + + +function hasSkipExtension(importPath) { + return SKIP_EXTENSIONS.some(ext => importPath.endsWith(ext)); +} + + +function processFile(filePath) { + if (!filePath.endsWith('.js')) return; + + let content = fs.readFileSync(filePath, 'utf8'); + let modified = false; + + + const staticImportRegex = /(from\s+['"])(\.\.?\/[^'"]+)(['"])/g; + + content = content.replace(staticImportRegex, (match, prefix, importPath, suffix) => { + if (hasSkipExtension(importPath)) { + return match; // Already has extension, skip + } + modified = true; + stats.importsFixed++; + return `${prefix}${importPath}.js${suffix}`; + }); + + + const dynamicImportRegex = /(import\s*\(\s*['"])(\.\.?\/[^'"]+)(['"]\s*\))/g; + + content = content.replace(dynamicImportRegex, (match, prefix, importPath, suffix) => { + if (hasSkipExtension(importPath)) { + return match; // Already has extension, skip + } + modified = true; + stats.importsFixed++; + return `${prefix}${importPath}.js${suffix}`; + }); + + if (modified) { + fs.writeFileSync(filePath, content); + stats.filesProcessed++; + } +} + +/** + * Recursively process all files in a directory + */ +function processDirectory(dir) { + if (!fs.existsSync(dir)) { + console.error(`Error: Directory not found: ${dir}`); + process.exit(1); + } + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + processDirectory(fullPath); + } else { + processFile(fullPath); + } + } +} + +// Main execution +console.log('Fixing ESM imports...'); +console.log(`Directory: ${esmDir}`); + +try { + processDirectory(esmDir); + console.log(`Done! Fixed ${stats.importsFixed} imports in ${stats.filesProcessed} files.`); +} catch (error) { + console.error('Error fixing ESM imports:', error.message); + process.exit(1); +} diff --git a/src/abstractions/download-catalog.inteface.ts b/src/abstractions/download-catalog-service.interface.ts similarity index 100% rename from src/abstractions/download-catalog.inteface.ts rename to src/abstractions/download-catalog-service.interface.ts diff --git a/src/abstractions/download-request.service.interface.ts b/src/abstractions/download-request-service.interface.ts similarity index 69% rename from src/abstractions/download-request.service.interface.ts rename to src/abstractions/download-request-service.interface.ts index 7a18266..d2d6f60 100644 --- a/src/abstractions/download-request.service.interface.ts +++ b/src/abstractions/download-request-service.interface.ts @@ -10,49 +10,49 @@ import { FileResponse } from '../common/file-response'; export interface IDownloadRequestService extends IFiscalapiService { /** * Lista los xmls descargados para un requestId. - * + * * @param requestId - ID de la solicitud * @returns Lista paginada de objetos Xml */ - getXmlsAsync(requestId: string): Promise>>; + getXmls(requestId: string): Promise>>; /** * Lista los meta-items descargados para un requestId. - * + * * @param requestId - ID de la solicitud * @returns Lista paginada de objetos MetadataItem */ - getMetadataItemsAsync(requestId: string): Promise>>; + getMetadataItems(requestId: string): Promise>>; /** - * Downloads la lista de paquetes (archivos .zip) de un requestId. - * + * Descarga la lista de paquetes (archivos .zip) de un requestId. + * * @param requestId - ID de la solicitud * @returns Lista de FileResponses */ - downloadPackageAsync(requestId: string): Promise>; + downloadPackage(requestId: string): Promise>; /** * Descarga el archivo crudo de solicitud SAT para un requestId. - * + * * @param requestId - ID de la solicitud * @returns Objeto de respuesta de archivo */ - downloadSatRequestAsync(requestId: string): Promise>; + downloadSatRequest(requestId: string): Promise>; /** * Descarga la respuesta SAT para un requestId. - * + * * @param requestId - ID de la solicitud * @returns Objeto de respuesta de archivo */ - downloadSatResponseAsync(requestId: string): Promise>; + downloadSatResponse(requestId: string): Promise>; /** * Busca solicitudes de descarga creadas en una fecha específica. - * + * * @param createdAt - Fecha de creación * @returns Lista de solicitudes de descarga */ - searchAsync(createdAt: Date): Promise>; + searchByDate(createdAt: Date): Promise>; } \ No newline at end of file diff --git a/src/abstractions/download-rule.service.inteface.ts b/src/abstractions/download-rule-service.interface.ts similarity index 100% rename from src/abstractions/download-rule.service.inteface.ts rename to src/abstractions/download-rule-service.interface.ts diff --git a/src/abstractions/employee-service.interface.ts b/src/abstractions/employee-service.interface.ts new file mode 100644 index 0000000..ec98bb1 --- /dev/null +++ b/src/abstractions/employee-service.interface.ts @@ -0,0 +1,12 @@ +import { ApiResponse } from '../common/api-response'; +import { EmployeeData, CreateEmployeeRequest, UpdateEmployeeRequest } from '../models/employee-data'; + +/** + * Interfaz del servicio de datos de empleado + */ +export interface IEmployeeService { + getById(id: string): Promise>; + create(requestModel: CreateEmployeeRequest): Promise>; + update(requestModel: UpdateEmployeeRequest): Promise>; + delete(personId: string): Promise>; +} diff --git a/src/abstractions/employer-service.interface.ts b/src/abstractions/employer-service.interface.ts new file mode 100644 index 0000000..3a9aeb3 --- /dev/null +++ b/src/abstractions/employer-service.interface.ts @@ -0,0 +1,12 @@ +import { ApiResponse } from '../common/api-response'; +import { EmployerData, CreateEmployerRequest, UpdateEmployerRequest } from '../models/employer-data'; + +/** + * Interfaz del servicio de datos de empleador + */ +export interface IEmployerService { + getById(id: string): Promise>; + create(requestModel: CreateEmployerRequest): Promise>; + update(requestModel: UpdateEmployerRequest): Promise>; + delete(personId: string): Promise>; +} diff --git a/src/abstractions/fiscalapi-client.interface.ts b/src/abstractions/fiscalapi-client.interface.ts index d696fdb..fada5e7 100644 --- a/src/abstractions/fiscalapi-client.interface.ts +++ b/src/abstractions/fiscalapi-client.interface.ts @@ -1,11 +1,12 @@ import { IApiKeyService } from './api-key-service.interface'; import { ICatalogService } from './catalog-service.interface'; -import { IDownloadCatalogService } from './download-catalog.inteface'; -import { IDownloadRequestService } from './download-request.service.interface'; -import { IDownloadRuleService } from './download-rule.service.inteface'; +import { IDownloadCatalogService } from './download-catalog-service.interface'; +import { IDownloadRequestService } from './download-request-service.interface'; +import { IDownloadRuleService } from './download-rule-service.interface'; import { IInvoiceService } from './invoice-service.interface'; import { IPersonService } from './person-service.interface'; import { IProductService } from './product-service.interface'; +import { IStampService } from './stamp-service.interface'; import { ITaxFileService } from './tax-file-service.interface'; /** @@ -56,4 +57,9 @@ export interface IFiscalapiClient { * Servicio de solicitudes de descarga masiva */ downloadRequests: IDownloadRequestService; + + /** + * Servicio de timbres fiscales + */ + stamps: IStampService; } \ No newline at end of file diff --git a/src/abstractions/person-service.interface.ts b/src/abstractions/person-service.interface.ts index 4076fe1..ee73dea 100644 --- a/src/abstractions/person-service.interface.ts +++ b/src/abstractions/person-service.interface.ts @@ -1,10 +1,13 @@ - + import { IFiscalapiService } from './fiscalapi-service.interface'; +import { IEmployeeService } from './employee-service.interface'; +import { IEmployerService } from './employer-service.interface'; import { Person } from '../models/person'; /** * Interfaz del servicio de personas */ export interface IPersonService extends IFiscalapiService { - -} \ No newline at end of file + readonly employee: IEmployeeService; + readonly employer: IEmployerService; +} diff --git a/src/abstractions/stamp-service.interface.ts b/src/abstractions/stamp-service.interface.ts new file mode 100644 index 0000000..5908599 --- /dev/null +++ b/src/abstractions/stamp-service.interface.ts @@ -0,0 +1,22 @@ +import { ApiResponse } from '../common/api-response'; +import { StampTransaction, StampTransactionParams } from '../models/stamp'; +import { IFiscalapiService } from './fiscalapi-service.interface'; + +/** + * Interfaz para el servicio de timbres fiscales + */ +export interface IStampService extends IFiscalapiService { + /** + * Transfiere timbres de una persona a otra + * @param {StampTransactionParams} request - Parámetros de la transferencia + * @returns {Promise>} Resultado de la operación + */ + transferStamps(request: StampTransactionParams): Promise>; + + /** + * Retira timbres de una persona + * @param {StampTransactionParams} request - Parámetros del retiro + * @returns {Promise>} Resultado de la operación + */ + withdrawStamps(request: StampTransactionParams): Promise>; +} diff --git a/src/abstractions/tax-file-service.interface.ts b/src/abstractions/tax-file-service.interface.ts index 079139b..bf61851 100644 --- a/src/abstractions/tax-file-service.interface.ts +++ b/src/abstractions/tax-file-service.interface.ts @@ -1,4 +1,3 @@ - import { IFiscalapiService } from './fiscalapi-service.interface'; import { TaxFile } from '../models/tax-file'; import { ApiResponse } from '../common/api-response'; @@ -16,7 +15,7 @@ export interface ITaxFileService extends IFiscalapiService { * @param personId - Id de la persona propietaria de los certificados * @returns Promise que resuelve en una respuesta API con una lista de un par de certificados, pero sin contenido, solo sus Ids */ - getDefaultReferences(personId: string): Promise> + getDefaultReferences(personId: string): Promise>; /** * Obtiene el último par de certificados válidos y vigente de una persona. Es decir sus certificados por defecto @@ -24,7 +23,6 @@ export interface ITaxFileService extends IFiscalapiService { * @param personId - Id de la persona dueña de los certificados * @returns Promise que resuelve en una respuesta API con una lista de un par de certificados */ - getDefaultValues(personId: string): Promise> - + getDefaultValues(personId: string): Promise>; } diff --git a/src/http/fiscalapi-http-client-factory.ts b/src/http/fiscalapi-http-client-factory.ts index b5c8cf5..e568b11 100644 --- a/src/http/fiscalapi-http-client-factory.ts +++ b/src/http/fiscalapi-http-client-factory.ts @@ -46,10 +46,9 @@ export class FiscalapiHttpClientFactory { * @private */ private static createAxiosInstance(settings: FiscalapiSettings): AxiosInstance { - // Agente HTTPS que ignora la validación del certificado autofirmado si está en modo depuración + // Agente HTTPS que ignora la validación del certificado solo en modo debug const httpsAgent = new https.Agent({ - //rejectUnauthorized: !settings.debug - rejectUnauthorized: false, // Cambiado a true para producción + rejectUnauthorized: !settings.debug, }); // Crea y configura una nueva instancia de axios diff --git a/src/http/fiscalapi-http-client.interface.ts b/src/http/fiscalapi-http-client.interface.ts index 71c7479..dadb896 100644 --- a/src/http/fiscalapi-http-client.interface.ts +++ b/src/http/fiscalapi-http-client.interface.ts @@ -33,14 +33,6 @@ export interface IFiscalapiHttpClient { */ getAsync(endpoint: string, config?: AxiosRequestConfig): Promise>; - /** - * Realiza una petición GET por ID a la API - * @param endpoint - Punto final de la API con ID - * @param config - Configuración adicional para la petición - * @returns Respuesta de la API - */ - getByIdAsync(endpoint: string, config?: AxiosRequestConfig): Promise>; - /** * Realiza una petición POST a la API * @param endpoint - Punto final de la API diff --git a/src/http/fiscalapi-http-client.ts b/src/http/fiscalapi-http-client.ts index 507a0ec..c5496e4 100644 --- a/src/http/fiscalapi-http-client.ts +++ b/src/http/fiscalapi-http-client.ts @@ -228,21 +228,42 @@ export class FiscalapiHttpClient implements IFiscalapiHttpClient { * @private */ private handleRequestError(error: unknown): ApiResponse { + // Verificar si es un error de Axios usando type guard + if (!(error instanceof Error)) { + return { + data: {} as T, + succeeded: false, + message: 'Error desconocido en la comunicación con el servidor', + details: String(error), + httpStatusCode: 500 + }; + } + + // Verificar si es un AxiosError (tiene la propiedad isAxiosError) + const isAxiosError = 'isAxiosError' in error && error.isAxiosError === true; + if (!isAxiosError) { + return { + data: {} as T, + succeeded: false, + message: error.message || 'Error en la comunicación con el servidor', + details: error.stack || '', + httpStatusCode: 500 + }; + } + const axiosError = error as AxiosError; - - // Extraer datos de respuesta const responseData = axiosError.response?.data; - + // Revisar si es un ProblemDetails según RFC 9457 if ( - responseData && - typeof responseData === 'object' && - 'type' in responseData && + responseData && + typeof responseData === 'object' && + 'type' in responseData && 'title' in responseData && 'status' in responseData ) { const problemDetails = responseData as ProblemDetails; - + return { data: {} as T, succeeded: false, @@ -251,17 +272,17 @@ export class FiscalapiHttpClient implements IFiscalapiHttpClient { httpStatusCode: axiosError.response?.status || 500 }; } - + // Revisar si es un ApiResponse para errores 400 if ( axiosError.response?.status === 400 && responseData && typeof responseData === 'object' && 'data' in responseData && - Array.isArray(responseData.data) + Array.isArray((responseData as { data: unknown }).data) ) { const apiResponse = responseData as ApiResponse; - + // Si hay errores de validación, extraer el primer mensaje if (apiResponse.data && apiResponse.data.length > 0) { const firstFailure = apiResponse.data[0]; @@ -274,8 +295,8 @@ export class FiscalapiHttpClient implements IFiscalapiHttpClient { }; } } - - // Respuesta de error genérica + + // Respuesta de error genérica para AxiosError return { data: {} as T, succeeded: false, @@ -296,17 +317,6 @@ export class FiscalapiHttpClient implements IFiscalapiHttpClient { return this.executeRequest('GET', endpoint, { config }); } - /** - * Realiza una petición GET por ID a la API - * @param {string} endpoint - Punto final de la API con ID - * @param {AxiosRequestConfig} [config] - Configuración adicional para la petición - * @returns {Promise>} Respuesta de la API - * @template T - Tipo de datos esperado en la respuesta - */ - async getByIdAsync(endpoint: string, config?: AxiosRequestConfig): Promise> { - return this.executeRequest('GET', endpoint, { config }); - } - /** * Realiza una petición POST a la API * @param {string} endpoint - Punto final de la API diff --git a/src/index.ts b/src/index.ts index a35c8d7..3588073 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,18 +3,28 @@ export { FiscalapiClient } from './services/fiscalapi-client'; export type { IFiscalapiClient } from './abstractions/fiscalapi-client.interface'; -export type { IFiscalapiService } from './abstractions/fiscalapi-service.interface'; +export type { IFiscalapiService, OperationOptions, RequestOptions } from './abstractions/fiscalapi-service.interface'; export type { IApiKeyService } from './abstractions/api-key-service.interface'; export type { ICatalogService } from './abstractions/catalog-service.interface'; export type { IInvoiceService } from './abstractions/invoice-service.interface'; export type { IPersonService } from './abstractions/person-service.interface'; export type { IProductService } from './abstractions/product-service.interface'; export type { ITaxFileService } from './abstractions/tax-file-service.interface'; -export type { IDownloadCatalogService } from './abstractions/download-catalog.inteface'; +export type { IDownloadCatalogService } from './abstractions/download-catalog-service.interface'; +export type { IDownloadRequestService } from './abstractions/download-request-service.interface'; +export type { IDownloadRuleService } from './abstractions/download-rule-service.interface'; +export type { IStampService } from './abstractions/stamp-service.interface'; +export type { IEmployeeService } from './abstractions/employee-service.interface'; +export type { IEmployerService } from './abstractions/employer-service.interface'; + +// HTTP types +export type { HttpMethod } from './http/fiscalapi-http-client.interface'; // Models types export type { ApiKey } from './models/api-key'; export type { Person } from './models/person'; +export type { EmployeeData, CreateEmployeeRequest, UpdateEmployeeRequest } from './models/employee-data'; +export type { EmployerData, CreateEmployerRequest, UpdateEmployerRequest } from './models/employer-data'; export type { Product, ProductTax } from './models/product'; export type { TaxFile } from './models/tax-file'; export type { @@ -27,7 +37,7 @@ export type { GlobalInformation, RelatedInvoice, InvoiceResponse, - //Payments + // Payments (legacy) InvoicePayment, PaidInvoice, PaidInvoiceTax, @@ -37,7 +47,28 @@ export type { SendInvoiceRequest, InvoiceStatusRequest, InvoiceStatusResponse, - + // Inline data types for payroll (ByValues) + InvoiceIssuerEmployerData, + InvoiceRecipientEmployeeData, + // Complement types + Complement, + LocalTaxesComplement, + LocalTax, + PaymentComplement, + PaymentPaidInvoice, + PaymentPaidInvoiceTax, + PayrollComplement, + PayrollEarnings, + PayrollEarning, + PayrollStockOptions, + PayrollOvertime, + PayrollOtherPayment, + PayrollBalanceCompensation, + PayrollRetirement, + PayrollSeverance, + PayrollDeduction, + PayrollDisability, + LadingComplement, } from './models/invoice'; export type { @@ -57,6 +88,17 @@ export type { XmlComplement } from './models/download'; +export type { + StampTransaction, + StampTransactionParams, + UserLookupDto +} from './models/stamp'; + +export { + StampTransactionType, + StampTransactionStatus +} from './models/stamp'; + // Common types export type { FiscalapiSettings } from './common/fiscalapi-settings'; diff --git a/src/models/employee-data.ts b/src/models/employee-data.ts new file mode 100644 index 0000000..c6cb2fb --- /dev/null +++ b/src/models/employee-data.ts @@ -0,0 +1,61 @@ +import { BaseDto } from '../common/base-dto'; +import { CatalogDto } from '../common/catalog-dto'; + +/** + * Datos de empleado (response model) + */ +export interface EmployeeData extends BaseDto { + employerPersonId?: string; + employeePersonId?: string; + employeeNumber?: string; + socialSecurityNumber?: string; + laborRelationStartDate?: string; + satContractType?: CatalogDto; + satTaxRegimeType?: CatalogDto; + satWorkdayType?: CatalogDto; + satJobRisk?: CatalogDto; + satPaymentPeriodicity?: CatalogDto; + satBank?: CatalogDto; + satPayrollState?: CatalogDto; + satUnionizedStatus?: CatalogDto; + department?: string; + position?: string; + seniority?: string; + bankAccount?: string; + baseSalaryForContributions?: number; + integratedDailySalary?: number; + subcontractorRfc?: string; + timePercentage?: number; +} + +/** + * Request para crear datos de empleado + */ +export interface CreateEmployeeRequest { + employerPersonId: string; + employeePersonId: string; + employeeNumber?: string; + satContractTypeId?: string; + satTaxRegimeTypeId?: string; + satPaymentPeriodicityId?: string; + satPayrollStateId?: string; + socialSecurityNumber?: string; + laborRelationStartDate?: string; + satWorkdayTypeId?: string; + satJobRiskId?: string; + satBankId?: string; + satUnionizedStatusId?: string; + department?: string; + position?: string; + seniority?: string; + bankAccount?: string; + baseSalaryForContributions?: number; + integratedDailySalary?: number; + subcontractorRfc?: string; + timePercentage?: number; +} + +/** + * Request para actualizar datos de empleado + */ +export interface UpdateEmployeeRequest extends CreateEmployeeRequest {} diff --git a/src/models/employer-data.ts b/src/models/employer-data.ts new file mode 100644 index 0000000..f4a8f8f --- /dev/null +++ b/src/models/employer-data.ts @@ -0,0 +1,30 @@ +import { BaseDto } from '../common/base-dto'; +import { CatalogDto } from '../common/catalog-dto'; + +/** + * Datos de empleador (response model) + * Extiende BaseDto porque la respuesta incluye id/createdAt/updatedAt + */ +export interface EmployerData extends BaseDto { + personId?: string; + employerRegistration?: string; + originEmployerTin?: string; + satFundSource?: CatalogDto; + ownResourceAmount?: number; +} + +/** + * Request para crear datos de empleador + */ +export interface CreateEmployerRequest { + personId: string; + employerRegistration?: string; + originEmployerTin?: string; + satFundSourceId?: string; + ownResourceAmount?: number; +} + +/** + * Request para actualizar datos de empleador + */ +export interface UpdateEmployerRequest extends CreateEmployerRequest {} diff --git a/src/models/invoice.ts b/src/models/invoice.ts index 458e320..fffd497 100644 --- a/src/models/invoice.ts +++ b/src/models/invoice.ts @@ -1,14 +1,473 @@ import { DateTime } from 'luxon'; - + // src/models/invoice.ts import { BaseDto } from '../common/base-dto'; +// ============================================================================ +// Inline Data Types for Payroll (ByValues) +// ============================================================================ + +/** + * Datos del empleador para facturas de nómina (inline en issuer) + */ +export interface InvoiceIssuerEmployerData { + /** CURP del empleador */ + curp?: string; + + /** Registro patronal del empleador */ + employerRegistration?: string; + + /** RFC del empleador origen (para subcontratación) */ + originEmployerTin?: string; + + /** ID del origen de los recursos (catálogo SAT c_OrigenRecurso) */ + satFundSourceId?: string; + + /** Monto de recursos propios */ + ownResourceAmount?: number; +} + +/** + * Datos del empleado para facturas de nómina (inline en recipient) + */ +export interface InvoiceRecipientEmployeeData { + /** CURP del empleado */ + curp?: string; + + /** Número de seguridad social del empleado */ + socialSecurityNumber?: string; + + /** Fecha de inicio de la relación laboral */ + laborRelationStartDate?: string; + + /** Antigüedad del empleado en formato ISO 8601 duration (ej: P1Y5M15D) */ + seniority?: string; + + /** ID del tipo de contrato (catálogo SAT c_TipoContrato) */ + satContractTypeId?: string; + + /** ID del estatus de sindicalización (catálogo SAT c_TipoSindicalizado) */ + satUnionizedStatusId?: string; + + /** ID del tipo de jornada laboral (catálogo SAT c_TipoJornada) */ + satWorkdayTypeId?: string; + + /** ID del tipo de régimen fiscal del empleado (catálogo SAT c_TipoRegimen) */ + satTaxRegimeTypeId?: string; + + /** Número de empleado */ + employeeNumber?: string; + + /** Departamento del empleado */ + department?: string; + + /** Puesto del empleado */ + position?: string; + + /** ID del riesgo del puesto (catálogo SAT c_RiesgosPuesto) */ + satJobRiskId?: string; + + /** ID de la periodicidad de pago (catálogo SAT c_PeriodicidadPago) */ + satPaymentPeriodicityId?: string; + + /** ID del banco (catálogo SAT c_Banco) */ + satBankId?: string; + + /** Cuenta bancaria del empleado */ + bankAccount?: string; + + /** Salario base de cotización */ + baseSalaryForContributions?: number; + + /** Salario diario integrado */ + integratedDailySalary?: number; + + /** ID del estado donde se presta el servicio (catálogo SAT c_Estado) */ + satPayrollStateId?: string; +} + +// ============================================================================ +// Complement Types +// ============================================================================ + +/** + * Contenedor principal de complementos de factura + */ +export interface Complement { + /** Complemento de impuestos locales */ + localTaxes?: LocalTaxesComplement; + + /** Complemento de pago */ + payment?: PaymentComplement; + + /** Complemento de nómina */ + payroll?: PayrollComplement; + + /** Complemento de carta porte */ + lading?: LadingComplement; +} + +// ============================================================================ +// Local Taxes Complement +// ============================================================================ + +/** + * Complemento de impuestos locales + */ +export interface LocalTaxesComplement { + /** Lista de impuestos locales */ + taxes?: LocalTax[]; +} + +/** + * Impuesto local + */ +export interface LocalTax { + /** Nombre del impuesto local */ + taxName?: string; + + /** Tasa del impuesto local (debe tener 2 posiciones decimales) */ + taxRate?: number | string; + + /** Monto del impuesto local (debe tener 2 posiciones decimales) */ + taxAmount?: number | string; + + /** Código que indica la naturaleza del impuesto. "T": Traslado, "R": Retenido */ + taxFlagCode?: string; +} + +// ============================================================================ +// Payment Complement +// ============================================================================ + +/** + * Complemento de pago + */ +export interface PaymentComplement { + /** Fecha de pago. Se expresa en la forma AAAA-MM-DDThh:mm:ss */ + paymentDate: string; + + /** Código de la forma de pago del pago recibido. Catálogo del SAT c_FormaPago */ + paymentFormCode: string; + + /** Código de la moneda utilizada en el pago. Catálogo del SAT c_Moneda. Default: "MXN" */ + currencyCode: string; + + /** Tipo de cambio FIX conforme a la moneda registrada en la factura. Default: 1 */ + exchangeRate?: number | string; + + /** Monto del pago */ + amount: number | string; + + /** Número de operación asignado por el banco */ + operationNumber?: string; + + /** RFC del banco origen. (Rfc del banco emisor del pago) */ + sourceBankTin?: string; + + /** Cuenta bancaria origen. (Cuenta bancaria del banco emisor del pago) */ + sourceBankAccount?: string; + + /** RFC del banco destino. (Rfc del banco receptor del pago) */ + targetBankTin?: string; + + /** Cuenta bancaria destino (Cuenta bancaria del banco receptor del pago) */ + targetBankAccount?: string; + + /** Facturas pagadas con el pago recibido */ + paidInvoices: PaymentPaidInvoice[]; +} + +/** + * Factura pagada en el complemento de pago + */ +export interface PaymentPaidInvoice { + /** UUID de la factura pagada */ + uuid: string; + + /** Serie de la factura pagada */ + series: string; + + /** Folio de la factura pagada */ + number: string; + + /** Monto pagado en la factura */ + paymentAmount: number | string; + + /** Código de la moneda utilizada en la factura pagada. Default: "MXN" */ + currencyCode: string; + + /** Número de parcialidad */ + partialityNumber: number; + + /** Subtotal de la factura pagada */ + subTotal: number | string; + + /** Saldo anterior de la factura pagada */ + previousBalance: number | string; + + /** Saldo restante de la factura pagada */ + remainingBalance: number | string; + + /** Código de obligaciones de impuesto aplicables a la factura pagada */ + taxObjectCode: string; + + /** Equivalencia de la moneda. Default: 1 */ + equivalence?: number | string; + + /** Impuestos aplicables a la factura pagada */ + paidInvoiceTaxes?: PaymentPaidInvoiceTax[]; +} + +/** + * Impuesto de factura pagada en el complemento de pago + */ +export interface PaymentPaidInvoiceTax { + /** Código del impuesto. Catálogo del SAT c_Impuesto */ + taxCode: string; + + /** Tipo de factor. Catálogo del SAT c_TipoFactor */ + taxTypeCode: string; + + /** Tasa del impuesto. Catálogo del SAT c_TasaOCuota */ + taxRate: number | string; + + /** Código que indica la naturaleza del impuesto. "T": Impuesto Traslado, "R": Impuesto Retenido */ + taxFlagCode: string; +} + +// ============================================================================ +// Payroll Complement +// ============================================================================ + +/** + * Complemento de nómina + */ +export interface PayrollComplement { + /** Versión del complemento de nómina. Default: "1.2" */ + version?: string; + + /** Código del tipo de nómina. "O": Ordinaria, "E": Extraordinaria */ + payrollTypeCode: string; + + /** Fecha de pago de la nómina */ + paymentDate: string; + + /** Fecha inicial del periodo de pago */ + initialPaymentDate: string; + + /** Fecha final del periodo de pago */ + finalPaymentDate: string; + + /** Número de días pagados */ + daysPaid: number; + + /** Percepciones del empleado */ + earnings?: PayrollEarnings; + + /** Deducciones del empleado */ + deductions?: PayrollDeduction[]; + + /** Incapacidades del empleado */ + disabilities?: PayrollDisability[]; +} + +/** + * Contenedor de percepciones de nómina + */ +export interface PayrollEarnings { + /** Lista de percepciones */ + earnings?: PayrollEarning[]; + + /** Lista de otros pagos */ + otherPayments?: PayrollOtherPayment[]; + + /** Información de jubilación, pensión o retiro */ + retirement?: PayrollRetirement; + + /** Información de separación o indemnización */ + severance?: PayrollSeverance; +} + +/** + * Percepción de nómina + */ +export interface PayrollEarning { + /** Código del tipo de percepción. Catálogo SAT c_TipoPercepcion */ + earningTypeCode: string; + + /** Clave de control interno de la percepción */ + code: string; + + /** Concepto de la percepción */ + concept: string; + + /** Monto gravado de la percepción */ + taxedAmount: number; + + /** Monto exento de la percepción */ + exemptAmount: number; + + /** Horas extra trabajadas */ + overtime?: PayrollOvertime[]; + + /** Opciones de acciones */ + stockOptions?: PayrollStockOptions; +} + +/** + * Opciones de acciones en percepción + */ +export interface PayrollStockOptions { + /** Precio de mercado de la acción */ + marketPrice: number; + + /** Precio de ejercicio de la acción */ + grantPrice: number; +} + +/** + * Horas extra en percepción + */ +export interface PayrollOvertime { + /** Número de días con horas extra */ + days: number; + + /** Código del tipo de horas. "01": Dobles, "02": Triples */ + hoursTypeCode: string; + + /** Número de horas extra trabajadas */ + extraHours: number; + + /** Monto pagado por las horas extra */ + amountPaid: number; +} + +/** + * Otros pagos de nómina + */ +export interface PayrollOtherPayment { + /** Código del tipo de otro pago. Catálogo SAT c_TipoOtroPago */ + otherPaymentTypeCode: string; + + /** Clave de control interno del otro pago */ + code: string; + + /** Concepto del otro pago */ + concept: string; + + /** Monto del otro pago */ + amount: number; + + /** Subsidio causado (para tipo 002) */ + subsidyCaused?: number; + + /** Compensación de saldos a favor */ + balanceCompensation?: PayrollBalanceCompensation; +} + +/** + * Compensación de saldos a favor en otros pagos + */ +export interface PayrollBalanceCompensation { + /** Saldo a favor */ + favorableBalance: number; + + /** Año del saldo a favor */ + year: number; + + /** Saldo a favor remanente */ + remainingFavorableBalance: number; +} + +/** + * Información de jubilación, pensión o retiro + */ +export interface PayrollRetirement { + /** Total de pago único */ + totalOneTime?: number; + + /** Total de parcialidades */ + totalInstallments?: number; + + /** Monto diario */ + dailyAmount?: number; + + /** Ingreso acumulable */ + accumulableIncome?: number; + + /** Ingreso no acumulable */ + nonAccumulableIncome?: number; +} + +/** + * Información de separación o indemnización + */ +export interface PayrollSeverance { + /** Total pagado */ + totalPaid: number; + + /** Años de servicio */ + yearsOfService: number; + + /** Último sueldo mensual ordinario */ + lastMonthlySalary: number; + + /** Ingreso acumulable */ + accumulableIncome: number; + + /** Ingreso no acumulable */ + nonAccumulableIncome: number; +} + +/** + * Deducción de nómina + */ +export interface PayrollDeduction { + /** Código del tipo de deducción. Catálogo SAT c_TipoDeduccion */ + deductionTypeCode: string; + + /** Clave de control interno de la deducción */ + code: string; + + /** Concepto de la deducción */ + concept: string; + + /** Monto de la deducción */ + amount: number; +} + +/** + * Incapacidad de nómina + */ +export interface PayrollDisability { + /** Número de días de incapacidad */ + disabilityDays: number; + + /** Código del tipo de incapacidad. Catálogo SAT c_TipoIncapacidad */ + disabilityTypeCode: string; + + /** Monto monetario de la incapacidad */ + monetaryAmount?: number; +} + +// ============================================================================ +// Lading Complement (Carta Porte) +// ============================================================================ + +/** + * Complemento de carta porte (placeholder para futura implementación) + */ +export interface LadingComplement { + // Carta Porte complement - to be implemented +} + /** * Modelo factura * Contiene toda la información de una factura, como datos del emisor, receptor, * productos/servicios, importes, método de pago, el tipo de factura, entre otros. */ -export interface Invoice { +export interface Invoice extends BaseDto { /** Código de la versión de la facura. Default: "4.0" */ versionCode?: string; @@ -72,9 +531,15 @@ export interface Invoice { /** Facturas relacionadas */ relatedInvoices?: RelatedInvoice[]; - /** Pago o pagos recibidos para liquidar parcial o totalmente una factura de ingreso emitida previamente */ + /** + * Pago o pagos recibidos para liquidar parcial o totalmente una factura de ingreso emitida previamente + * @deprecated Use complement.payment instead + */ payments?: InvoicePayment[]; + /** Complementos de la factura (nómina, pago, impuestos locales, carta porte) */ + complement?: Complement; + /** Respuesta del SAT. Contiene la información del timbrado. (Sólo lectura) */ responses?: InvoiceResponse[]; } @@ -95,6 +560,9 @@ export interface InvoiceIssuer { /** Código del régimen fiscal del emisor. Catálogo del SAT c_RegimenFiscal */ taxRegimeCode?: string; + /** Datos del empleador para facturas de nómina (inline, modo ByValues) */ + employerData?: InvoiceIssuerEmployerData; + /** Sellos del emisor (archivos .cer y .key) */ taxCredentials?: TaxCredential[]; } @@ -137,6 +605,9 @@ export interface InvoiceRecipient { /** Correo electrónico del receptor. Para enviar la factura desde el dasborard */ email?: string; + + /** Datos del empleado para facturas de nómina (inline, modo ByValues) */ + employeeData?: InvoiceRecipientEmployeeData; } /** diff --git a/src/models/stamp.ts b/src/models/stamp.ts new file mode 100644 index 0000000..6c8ca32 --- /dev/null +++ b/src/models/stamp.ts @@ -0,0 +1,79 @@ +// src/models/stamp.ts +import { BaseDto } from '../common/base-dto'; + +/** + * Tipo de transacción de timbres + */ +export enum StampTransactionType { + Purchase = 1, + Transfer = 2, + Consumption = 3, + AutoInvoice = 4, + Rollback = 5, +} + +/** + * Estado de transacción de timbres + */ +export enum StampTransactionStatus { + Completed = 1, + Cancelled = 2, + RolledBack = 3, +} + +/** + * DTO para información resumida de persona en transacciones de timbres + */ +export interface UserLookupDto extends BaseDto { + /** RFC de la persona (Tax Identification Number) */ + tin?: string; + + /** Razón social de la persona */ + legalName?: string; +} + +/** + * Representa una transacción de timbres fiscales + */ +export interface StampTransaction extends BaseDto { + /** Número consecutivo de la transacción */ + consecutive?: number; + + /** Persona origen de la transferencia */ + fromPerson?: UserLookupDto; + + /** Persona destino de la transferencia */ + toPerson?: UserLookupDto; + + /** Cantidad de timbres en la transacción */ + amount?: number; + + /** Tipo de transacción */ + transactionType?: StampTransactionType; + + /** Estado de la transacción */ + transactionStatus?: StampTransactionStatus; + + /** ID de referencia de la transacción */ + referenceId?: string; + + /** Comentarios de la transacción */ + comments?: string; +} + +/** + * Parámetros para realizar una transferencia o retiro de timbres + */ +export interface StampTransactionParams { + /** ID de la persona origen */ + fromPersonId: string; + + /** ID de la persona destino */ + toPersonId: string; + + /** Cantidad de timbres a transferir */ + amount: number; + + /** Comentarios opcionales */ + comments?: string; +} diff --git a/src/sdk-constants.ts b/src/sdk-constants.ts deleted file mode 100644 index c8686f2..0000000 --- a/src/sdk-constants.ts +++ /dev/null @@ -1,25 +0,0 @@ - -/** - * Constantes utilizadas en todo el SDK - */ -export const SdkConstants = { - /** - * Formato de fecha SAT (para facturas mexicanas) - */ - SAT_DATE_FORMAT: 'yyyy-MM-ddTHH:mm:ss', - - /** - * Versión predeterminada de la API - */ - DEFAULT_API_VERSION: 'v4', - - /** - * Zona horaria predeterminada - */ - DEFAULT_TIME_ZONE: 'America/Mexico_City', - - /** - * Tiempo de espera HTTP predeterminado en milisegundos - */ - DEFAULT_HTTP_TIMEOUT: 30000 - }; \ No newline at end of file diff --git a/src/services/download-catalog.service.ts b/src/services/download-catalog-service.ts similarity index 98% rename from src/services/download-catalog.service.ts rename to src/services/download-catalog-service.ts index 710cde8..cc3148e 100644 --- a/src/services/download-catalog.service.ts +++ b/src/services/download-catalog-service.ts @@ -1,9 +1,7 @@ - import { CatalogDto } from '../common/catalog-dto'; import { ApiResponse } from '../common/api-response'; import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; -import { IDownloadCatalogService } from '../abstractions/download-catalog.inteface'; - +import { IDownloadCatalogService } from '../abstractions/download-catalog-service.interface'; /** * Implementación del servicio de catálogos de descarga masiva @@ -18,7 +16,6 @@ export class DownloadCatalogService implements IDownloadCatalogService { this.apiVersion = apiVersion; this.baseEndpoint = `api/${apiVersion}/download-catalogs`; } - /** * Construye el endpoint completo para las peticiones @@ -46,6 +43,4 @@ export class DownloadCatalogService implements IDownloadCatalogService { const endpoint = this.buildEndpoint(catalogName); return this.httpClient.getAsync(endpoint); } - } - diff --git a/src/services/download-request.service.ts b/src/services/download-request-service.ts similarity index 74% rename from src/services/download-request.service.ts rename to src/services/download-request-service.ts index ccd6bdc..9c3dec2 100644 --- a/src/services/download-request.service.ts +++ b/src/services/download-request-service.ts @@ -1,7 +1,7 @@ import { DownloadRequest, Xml, MetadataItem } from '../models/download'; import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; import { BaseFiscalapiService } from './base-fiscalapi-service'; -import { IDownloadRequestService } from '../abstractions/download-request.service.interface'; +import { IDownloadRequestService } from '../abstractions/download-request-service.interface'; import { ApiResponse } from '../common/api-response'; import { PagedList } from '../common/paged-list'; import { FileResponse } from '../common/file-response'; @@ -21,12 +21,11 @@ export class DownloadRequestService extends BaseFiscalapiService>> { - // GET /api/v4/download-requests//xmls + getXmls(requestId: string): Promise>> { const path = `${requestId}/xmls`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync>(endpoint); @@ -34,25 +33,23 @@ export class DownloadRequestService extends BaseFiscalapiService>> { - // GET /api/v4/download-requests//meta-items + getMetadataItems(requestId: string): Promise>> { const path = `${requestId}/meta-items`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync>(endpoint); } /** - * Downloads la lista de paquetes (archivos .zip) de un requestId. - * + * Descarga la lista de paquetes (archivos .zip) de un requestId. + * * @param requestId - ID de la solicitud * @returns Lista de FileResponses */ - downloadPackageAsync(requestId: string): Promise> { - // GET /api/v4/download-requests//package + downloadPackage(requestId: string): Promise> { const path = `${requestId}/package`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync(endpoint); @@ -60,12 +57,11 @@ export class DownloadRequestService extends BaseFiscalapiService> { - // GET /api/v4/download-requests//raw-request + downloadSatRequest(requestId: string): Promise> { const path = `${requestId}/raw-request`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync(endpoint); @@ -73,12 +69,11 @@ export class DownloadRequestService extends BaseFiscalapiService> { - // GET /api/v4/download-requests//raw-response + downloadSatResponse(requestId: string): Promise> { const path = `${requestId}/raw-response`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync(endpoint); @@ -86,13 +81,12 @@ export class DownloadRequestService extends BaseFiscalapiService> { - // GET /api/v4/download-requests/search?createdAt=2025-08-21 - const formattedDate = createdAt.toISOString().split('T')[0]; // Format as YYYY-MM-DD + searchByDate(createdAt: Date): Promise> { + const formattedDate = createdAt.toISOString().split('T')[0]; const path = `search?createdAt=${formattedDate}`; const endpoint = this.buildEndpoint(path); return this.httpClient.getAsync(endpoint); diff --git a/src/services/download-rule.service.ts b/src/services/download-rule-service.ts similarity index 83% rename from src/services/download-rule.service.ts rename to src/services/download-rule-service.ts index 865f6d8..db221ae 100644 --- a/src/services/download-rule.service.ts +++ b/src/services/download-rule-service.ts @@ -1,7 +1,7 @@ import { DownloadRequest, DownloadRule } from '../models/download'; import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; import { BaseFiscalapiService } from './base-fiscalapi-service'; -import { IDownloadRuleService } from '../abstractions/download-rule.service.inteface'; +import { IDownloadRuleService } from '../abstractions/download-rule-service.interface'; import { ApiResponse } from '../common/api-response'; /** @@ -9,7 +9,7 @@ import { ApiResponse } from '../common/api-response'; */ export class DownloadRuleService extends BaseFiscalapiService implements IDownloadRuleService { /** -* Crea una nueva instancia del servicio de reglas de descarga masiva + * Crea una nueva instancia del servicio de reglas de descarga masiva * @param {IFiscalapiHttpClient} httpClient - Cliente HTTP * @param {string} apiVersion - Versión de la API */ @@ -23,9 +23,9 @@ export class DownloadRuleService extends BaseFiscalapiService impl * @returns solicitud de descarga masiva de prueba */ createTestRule(): Promise> { - // GET /api/v4/download-rules/test - var path = "test"; - var endpoint = this.buildEndpoint(path); + // POST /api/v4/download-rules/test + const path = 'test'; + const endpoint = this.buildEndpoint(path); return this.httpClient.postAsync(endpoint, {}); } } \ No newline at end of file diff --git a/src/services/employee-service.ts b/src/services/employee-service.ts new file mode 100644 index 0000000..304445a --- /dev/null +++ b/src/services/employee-service.ts @@ -0,0 +1,43 @@ +import { ApiResponse } from '../common/api-response'; +import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; +import { IEmployeeService } from '../abstractions/employee-service.interface'; +import { EmployeeData, CreateEmployeeRequest, UpdateEmployeeRequest } from '../models/employee-data'; + +/** + * Implementación del servicio de datos de empleado + */ +export class EmployeeService implements IEmployeeService { + private readonly httpClient: IFiscalapiHttpClient; + private readonly apiVersion: string; + + constructor(httpClient: IFiscalapiHttpClient, apiVersion: string) { + this.httpClient = httpClient; + this.apiVersion = apiVersion; + } + + private buildEndpoint(personId: string): string { + return `api/${this.apiVersion}/people/${personId}/employee`; + } + + async getById(id: string): Promise> { + return this.httpClient.getAsync(this.buildEndpoint(id)); + } + + async create(requestModel: CreateEmployeeRequest): Promise> { + return this.httpClient.postAsync( + this.buildEndpoint(requestModel.employeePersonId), + requestModel + ); + } + + async update(requestModel: UpdateEmployeeRequest): Promise> { + return this.httpClient.putAsync( + this.buildEndpoint(requestModel.employeePersonId), + requestModel + ); + } + + async delete(personId: string): Promise> { + return this.httpClient.deleteAsync(this.buildEndpoint(personId)); + } +} diff --git a/src/services/employer-service.ts b/src/services/employer-service.ts new file mode 100644 index 0000000..acf5fa0 --- /dev/null +++ b/src/services/employer-service.ts @@ -0,0 +1,43 @@ +import { ApiResponse } from '../common/api-response'; +import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; +import { IEmployerService } from '../abstractions/employer-service.interface'; +import { EmployerData, CreateEmployerRequest, UpdateEmployerRequest } from '../models/employer-data'; + +/** + * Implementación del servicio de datos de empleador + */ +export class EmployerService implements IEmployerService { + private readonly httpClient: IFiscalapiHttpClient; + private readonly apiVersion: string; + + constructor(httpClient: IFiscalapiHttpClient, apiVersion: string) { + this.httpClient = httpClient; + this.apiVersion = apiVersion; + } + + private buildEndpoint(personId: string): string { + return `api/${this.apiVersion}/people/${personId}/employer`; + } + + async getById(id: string): Promise> { + return this.httpClient.getAsync(this.buildEndpoint(id)); + } + + async create(requestModel: CreateEmployerRequest): Promise> { + return this.httpClient.postAsync( + this.buildEndpoint(requestModel.personId), + requestModel + ); + } + + async update(requestModel: UpdateEmployerRequest): Promise> { + return this.httpClient.putAsync( + this.buildEndpoint(requestModel.personId), + requestModel + ); + } + + async delete(personId: string): Promise> { + return this.httpClient.deleteAsync(this.buildEndpoint(personId)); + } +} diff --git a/src/services/fiscalapi-client.ts b/src/services/fiscalapi-client.ts index e661a8c..e236178 100644 --- a/src/services/fiscalapi-client.ts +++ b/src/services/fiscalapi-client.ts @@ -1,5 +1,3 @@ - - import { IFiscalapiClient } from '../abstractions/fiscalapi-client.interface'; import { IInvoiceService } from '../abstractions/invoice-service.interface'; import { IProductService } from '../abstractions/product-service.interface'; @@ -7,20 +5,22 @@ import { IPersonService } from '../abstractions/person-service.interface'; import { IApiKeyService } from '../abstractions/api-key-service.interface'; import { ICatalogService } from '../abstractions/catalog-service.interface'; import { ITaxFileService } from '../abstractions/tax-file-service.interface'; -import { IDownloadCatalogService } from '../abstractions/download-catalog.inteface'; +import { IDownloadCatalogService } from '../abstractions/download-catalog-service.interface'; +import { IDownloadRuleService } from '../abstractions/download-rule-service.interface'; +import { IDownloadRequestService } from '../abstractions/download-request-service.interface'; +import { IStampService } from '../abstractions/stamp-service.interface'; import { FiscalapiSettings } from '../common/fiscalapi-settings'; import { FiscalapiHttpClientFactory } from '../http/fiscalapi-http-client-factory'; import { ApiKeyService } from './api-key-service'; import { CatalogService } from './catalog-service'; -import { DownloadCatalogService } from './download-catalog.service'; +import { DownloadCatalogService } from './download-catalog-service'; +import { DownloadRuleService } from './download-rule-service'; +import { DownloadRequestService } from './download-request-service'; import { InvoiceService } from './invoice-service'; import { PersonService } from './person-service'; import { ProductService } from './product-service'; +import { StampService } from './stamp-service'; import { TaxFileService } from './tax-file-service'; -import { DownloadRuleService } from './download-rule.service'; -import { IDownloadRuleService } from '../abstractions/download-rule.service.inteface'; -import { DownloadRequestService } from './download-request.service'; -import { IDownloadRequestService } from '../abstractions/download-request.service.interface'; /** * Cliente principal para FiscalAPI @@ -71,6 +71,11 @@ export class FiscalapiClient implements IFiscalapiClient { */ readonly downloadRequests: IDownloadRequestService; + /** + * Servicio de timbres fiscales + */ + readonly stamps: IStampService; + /** * Crea una nueva instancia del cliente de FiscalAPI * @param {FiscalapiSettings} settings - Configuración @@ -91,6 +96,7 @@ export class FiscalapiClient implements IFiscalapiClient { this.downloadCatalogs = new DownloadCatalogService(httpClient, apiVersion); this.downloadRules = new DownloadRuleService(httpClient, apiVersion); this.downloadRequests = new DownloadRequestService(httpClient, apiVersion); + this.stamps = new StampService(httpClient, apiVersion); } /** diff --git a/src/services/invoice-service.ts b/src/services/invoice-service.ts index 87b97d9..9006227 100644 --- a/src/services/invoice-service.ts +++ b/src/services/invoice-service.ts @@ -17,9 +17,6 @@ import { * Implementación del servicio de facturas */ export class InvoiceService extends BaseFiscalapiService implements IInvoiceService { - private readonly INCOME_ENDPOINT = 'income'; - private readonly CREDIT_NOTE_ENDPOINT = 'credit-note'; - private readonly PAYMENT_ENDPOINT = 'payment'; /** * Crea una nueva instancia del servicio de facturas @@ -30,37 +27,6 @@ export class InvoiceService extends BaseFiscalapiService implements IIn super(httpClient, 'invoices', apiVersion); } - /** - * @inheritdoc - */ - override async create(requestModel: Invoice): Promise> { - if (!requestModel) { - throw new Error('requestModel cannot be null'); - } - - let endpoint: string; - - switch (requestModel.typeCode) { - case 'I': - endpoint = this.INCOME_ENDPOINT; - break; - case 'E': - endpoint = this.CREDIT_NOTE_ENDPOINT; - break; - case 'P': - endpoint = this.PAYMENT_ENDPOINT; - break; - default: - throw new Error(`Unsupported invoice type: ${requestModel.typeCode}`); - } - - return await this.executeRequest({ - path:endpoint, - data:requestModel, - method:'POST', - }); - } - /** * Cancela una factura * @param {CancelInvoiceRequest} request - Solicitud para cancelar factura @@ -72,8 +38,8 @@ export class InvoiceService extends BaseFiscalapiService implements IIn } return await this.executeRequest({ - data:request, - method:'DELETE', + data: request, + method: 'DELETE', }); } @@ -87,9 +53,9 @@ export class InvoiceService extends BaseFiscalapiService implements IIn throw new Error('request cannot be null'); } return await this.executeRequest({ - path:'pdf', - data:request, - method:'POST', + path: 'pdf', + data: request, + method: 'POST', }); } @@ -118,9 +84,9 @@ export class InvoiceService extends BaseFiscalapiService implements IIn */ async send(request: SendInvoiceRequest): Promise> { return await this.executeRequest({ - path:'send', - data:request, - method:'POST', + path: 'send', + data: request, + method: 'POST', }); } @@ -130,10 +96,10 @@ export class InvoiceService extends BaseFiscalapiService implements IIn * @returns {Promise>} Respuesta con el estado de la factura */ async getStatus(request: InvoiceStatusRequest): Promise> { - return await this.executeRequest({ - path:'status', - data:request, - method:'POST', - }); + return await this.executeRequest({ + path: 'status', + data: request, + method: 'POST', + }); } } \ No newline at end of file diff --git a/src/services/person-service.ts b/src/services/person-service.ts index d438075..c0ea37e 100644 --- a/src/services/person-service.ts +++ b/src/services/person-service.ts @@ -2,12 +2,19 @@ import { Person } from '../models/person'; import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; import { BaseFiscalapiService } from './base-fiscalapi-service'; -import { IPersonService } from '..'; +import { IPersonService } from '../abstractions/person-service.interface'; +import { IEmployeeService } from '../abstractions/employee-service.interface'; +import { IEmployerService } from '../abstractions/employer-service.interface'; +import { EmployeeService } from './employee-service'; +import { EmployerService } from './employer-service'; /** * Implementación del servicio de personas */ export class PersonService extends BaseFiscalapiService implements IPersonService { + readonly employee: IEmployeeService; + readonly employer: IEmployerService; + /** * Crea una nueva instancia del servicio de personas * @param {IFiscalapiHttpClient} httpClient - Cliente HTTP @@ -15,5 +22,7 @@ export class PersonService extends BaseFiscalapiService implements IPers */ constructor(httpClient: IFiscalapiHttpClient, apiVersion: string) { super(httpClient, 'people', apiVersion); + this.employee = new EmployeeService(httpClient, apiVersion); + this.employer = new EmployerService(httpClient, apiVersion); } } diff --git a/src/services/stamp-service.ts b/src/services/stamp-service.ts new file mode 100644 index 0000000..f7d2ff7 --- /dev/null +++ b/src/services/stamp-service.ts @@ -0,0 +1,51 @@ +import { IFiscalapiHttpClient } from '../http/fiscalapi-http-client.interface'; +import { ApiResponse } from '../common/api-response'; +import { BaseFiscalapiService } from './base-fiscalapi-service'; +import { IStampService } from '../abstractions/stamp-service.interface'; +import { StampTransaction, StampTransactionParams } from '../models/stamp'; + +/** + * Implementación del servicio de timbres fiscales + */ +export class StampService extends BaseFiscalapiService implements IStampService { + /** + * Crea una nueva instancia del servicio de timbres + * @param {IFiscalapiHttpClient} httpClient - Cliente HTTP + * @param {string} apiVersion - Versión de la API + */ + constructor(httpClient: IFiscalapiHttpClient, apiVersion: string) { + super(httpClient, 'stamps', apiVersion); + } + + /** + * Transfiere timbres de una persona a otra + * @param {StampTransactionParams} request - Parámetros de la transferencia + * @returns {Promise>} Resultado de la operación + */ + async transferStamps(request: StampTransactionParams): Promise> { + if (!request) { + throw new Error('request cannot be null'); + } + + return await this.executeRequest({ + data: request, + method: 'POST', + }); + } + + /** + * Retira timbres de una persona + * @param {StampTransactionParams} request - Parámetros del retiro + * @returns {Promise>} Resultado de la operación + */ + async withdrawStamps(request: StampTransactionParams): Promise> { + if (!request) { + throw new Error('request cannot be null'); + } + + return await this.executeRequest({ + data: request, + method: 'POST', + }); + } +} diff --git a/src/utils/date-utils.ts b/src/utils/date-utils.ts index 040d7e7..022d22c 100644 --- a/src/utils/date-utils.ts +++ b/src/utils/date-utils.ts @@ -1,4 +1,3 @@ - import { DateTime } from 'luxon'; @@ -30,6 +29,5 @@ export function formatSatDate(date: Date | string | DateTime): string { * @returns {DateTime} Objeto DateTime */ export function parseSatDate(dateStr: string): DateTime { - return DateTime.fromFormat(dateStr, SAT_DATE_FORMAT); - } - \ No newline at end of file + return DateTime.fromFormat(dateStr, SAT_DATE_FORMAT); +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..ab813ad --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2019", + "lib": ["ES2019"], + "moduleResolution": "node", + "rootDir": "./src", + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "examples", "**/*.test.ts"] +} diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json index 1d000e8..99d16d0 100644 --- a/tsconfig.cjs.json +++ b/tsconfig.cjs.json @@ -1,10 +1,7 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS", - "moduleResolution": "node", - "outDir": "dist/cjs", - "declaration": false - }, - "include": ["src"] - } \ No newline at end of file + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "./dist/cjs" + } +} diff --git a/tsconfig.esm.json b/tsconfig.esm.json index 4ae7e8b..c3caec7 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -1,10 +1,7 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "ESNext", - "moduleResolution": "node", - "outDir": "dist/esm", - "declaration": false - }, - "include": ["src"] - } \ No newline at end of file + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "outDir": "./dist/esm" + } +} diff --git a/tsconfig.json b/tsconfig.json index 240f612..165d94c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,14 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { - "target": "ES2018", - "module": "CommonJS", - "moduleResolution": "node", - "lib": ["ES2018", "DOM"], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true + "module": "ESNext", + "outDir": "./dist" }, - "exclude": ["node_modules", "dist"] -} \ No newline at end of file + "ts-node": { + "compilerOptions": { + "module": "CommonJS", + "rootDir": "." + }, + "include": ["src/**/*", "examples/**/*"] + } +} diff --git a/tsconfig.types.json b/tsconfig.types.json deleted file mode 100644 index 6980268..0000000 --- a/tsconfig.types.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "moduleResolution": "node", - "outDir": "dist/types" - }, - "include": ["src"] - } \ No newline at end of file