n8n-nodes-sunat-peru 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/credentials/SunatPeruApi.credentials.d.ts +8 -0
- package/dist/credentials/SunatPeruApi.credentials.js +66 -0
- package/dist/nodes/SunatPeru/SunatPeru.node.d.ts +5 -0
- package/dist/nodes/SunatPeru/SunatPeru.node.js +411 -0
- package/dist/nodes/SunatPeru/sunat.svg +8 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Manuel Reyes Bravo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# n8n-nodes-sunat-peru
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Nodo n8n para integración con **SUNAT Perú** (Superintendencia Nacional de Aduanas y de Administración Tributaria).
|
|
8
|
+
|
|
9
|
+
## 🚀 Funcionalidades
|
|
10
|
+
|
|
11
|
+
### RUC/DNI (Registro Único de Contribuyentes)
|
|
12
|
+
- ✅ **Validar RUC** - Verifica formato y dígito verificador (11 dígitos)
|
|
13
|
+
- ✅ **Validar DNI** - Verifica formato (8 dígitos)
|
|
14
|
+
- ✅ **Formatear RUC** - Limpia y normaliza
|
|
15
|
+
- ✅ **Consultar RUC** - Obtiene razón social (API pública)
|
|
16
|
+
|
|
17
|
+
### Indicadores Económicos (BCRP/SUNAT)
|
|
18
|
+
- 💵 **Tipo de Cambio** - Dólar compra/venta SBS
|
|
19
|
+
- 📊 **Valor UIT** - Unidad Impositiva Tributaria
|
|
20
|
+
- 🔄 **Convertir UIT ↔ Soles**
|
|
21
|
+
|
|
22
|
+
### Emisión Factura Electrónica (requiere proveedor)
|
|
23
|
+
- 📄 **Factura** - 01
|
|
24
|
+
- 📄 **Boleta de Venta** - 03
|
|
25
|
+
- 📄 **Nota de Crédito** - 07
|
|
26
|
+
- 📄 **Nota de Débito** - 08
|
|
27
|
+
|
|
28
|
+
## 📦 Instalación
|
|
29
|
+
|
|
30
|
+
### En n8n (recomendado)
|
|
31
|
+
1. Ve a **Settings** → **Community Nodes**
|
|
32
|
+
2. Clic en **Install**
|
|
33
|
+
3. Escribe: `n8n-nodes-sunat-peru`
|
|
34
|
+
4. Clic en **Install**
|
|
35
|
+
|
|
36
|
+
### Via npm
|
|
37
|
+
```bash
|
|
38
|
+
npm install n8n-nodes-sunat-peru
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## ⚙️ Configuración
|
|
42
|
+
|
|
43
|
+
### Sin credenciales (funciones locales)
|
|
44
|
+
Las siguientes funciones NO requieren credenciales:
|
|
45
|
+
- Validar/formatear RUC/DNI
|
|
46
|
+
- Tipo de cambio (API pública)
|
|
47
|
+
- Valor UIT
|
|
48
|
+
- Conversiones
|
|
49
|
+
|
|
50
|
+
### Con credenciales (emisión facturas)
|
|
51
|
+
Para emitir facturas electrónicas necesitas:
|
|
52
|
+
- **Nubefact** - https://nubefact.com
|
|
53
|
+
- **Efact** - https://efact.pe
|
|
54
|
+
|
|
55
|
+
## 📋 Ejemplos de Uso
|
|
56
|
+
|
|
57
|
+
### Validar RUC
|
|
58
|
+
```
|
|
59
|
+
Recurso: RUC/DNI
|
|
60
|
+
Operación: Validar RUC
|
|
61
|
+
RUC: 20123456789
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Obtener tipo de cambio
|
|
65
|
+
```
|
|
66
|
+
Recurso: Indicadores
|
|
67
|
+
Operación: Tipo de Cambio
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Convertir UIT a Soles
|
|
71
|
+
```
|
|
72
|
+
Recurso: Indicadores
|
|
73
|
+
Operación: UIT a Soles
|
|
74
|
+
Monto UIT: 10
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 📊 Tipos de Comprobante
|
|
78
|
+
|
|
79
|
+
| Código | Tipo | Descripción |
|
|
80
|
+
|--------|------|-------------|
|
|
81
|
+
| 01 | Factura | Para empresas con RUC |
|
|
82
|
+
| 03 | Boleta | Para consumidor final |
|
|
83
|
+
| 07 | Nota Crédito | Anula/modifica factura |
|
|
84
|
+
| 08 | Nota Débito | Cargo adicional |
|
|
85
|
+
|
|
86
|
+
## 🔗 APIs Utilizadas
|
|
87
|
+
|
|
88
|
+
- **SUNAT** - Consulta RUC (pública)
|
|
89
|
+
- **SBS** - Tipo de cambio oficial
|
|
90
|
+
- **Nubefact / Efact** - Emisión facturas (requiere cuenta)
|
|
91
|
+
|
|
92
|
+
## 🤝 Contribuir
|
|
93
|
+
|
|
94
|
+
Las contribuciones son bienvenidas. Por favor abre un issue o pull request.
|
|
95
|
+
|
|
96
|
+
## 📄 Licencia
|
|
97
|
+
|
|
98
|
+
MIT © Manuel Reyes Bravo
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ICredentialType, INodeProperties, ICredentialTestRequest } from 'n8n-workflow';
|
|
2
|
+
export declare class SunatPeruApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
test: ICredentialTestRequest;
|
|
8
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SunatPeruApi = void 0;
|
|
4
|
+
class SunatPeruApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'sunatPeruApi';
|
|
7
|
+
this.displayName = 'SUNAT Perú / Facturación Electrónica';
|
|
8
|
+
this.documentationUrl = 'https://nubefact.com/documentacion';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Proveedor de Facturación',
|
|
12
|
+
name: 'provider',
|
|
13
|
+
type: 'options',
|
|
14
|
+
options: [
|
|
15
|
+
{ name: 'Nubefact', value: 'nubefact' },
|
|
16
|
+
{ name: 'Efact', value: 'efact' },
|
|
17
|
+
{ name: 'Solo Funciones Locales (sin emisión)', value: 'none' },
|
|
18
|
+
],
|
|
19
|
+
default: 'none',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
displayName: 'API Key Nubefact',
|
|
23
|
+
name: 'nubefactApiKey',
|
|
24
|
+
type: 'string',
|
|
25
|
+
typeOptions: { password: true },
|
|
26
|
+
default: '',
|
|
27
|
+
displayOptions: { show: { provider: ['nubefact'] } },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Ambiente Nubefact',
|
|
31
|
+
name: 'nubefactEnvironment',
|
|
32
|
+
type: 'options',
|
|
33
|
+
options: [
|
|
34
|
+
{ name: 'Demo (Pruebas)', value: 'demo' },
|
|
35
|
+
{ name: 'Producción', value: 'production' },
|
|
36
|
+
],
|
|
37
|
+
default: 'demo',
|
|
38
|
+
displayOptions: { show: { provider: ['nubefact'] } },
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
displayName: 'API Key Efact',
|
|
42
|
+
name: 'efactApiKey',
|
|
43
|
+
type: 'string',
|
|
44
|
+
typeOptions: { password: true },
|
|
45
|
+
default: '',
|
|
46
|
+
displayOptions: { show: { provider: ['efact'] } },
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
displayName: 'RUC Emisor',
|
|
50
|
+
name: 'rucEmisor',
|
|
51
|
+
type: 'string',
|
|
52
|
+
default: '',
|
|
53
|
+
placeholder: '20123456789',
|
|
54
|
+
displayOptions: { hide: { provider: ['none'] } },
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
this.test = {
|
|
58
|
+
request: {
|
|
59
|
+
baseURL: 'https://api.exchangerate-api.com',
|
|
60
|
+
url: '/v4/latest/USD',
|
|
61
|
+
method: 'GET',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.SunatPeruApi = SunatPeruApi;
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SunatPeru = void 0;
|
|
4
|
+
// Funciones RUC/DNI
|
|
5
|
+
function validarRuc(ruc) {
|
|
6
|
+
const rucLimpio = ruc.replace(/[-\s]/g, '');
|
|
7
|
+
if (!/^\d{11}$/.test(rucLimpio)) {
|
|
8
|
+
return { valido: false, ruc: rucLimpio, tipo: 'Desconocido', mensaje: 'RUC debe tener 11 dígitos' };
|
|
9
|
+
}
|
|
10
|
+
// Validar prefijo
|
|
11
|
+
const prefijo = rucLimpio.substring(0, 2);
|
|
12
|
+
const prefijosValidos = ['10', '15', '17', '20'];
|
|
13
|
+
if (!prefijosValidos.includes(prefijo)) {
|
|
14
|
+
return { valido: false, ruc: rucLimpio, tipo: 'Desconocido', mensaje: 'Prefijo de RUC inválido' };
|
|
15
|
+
}
|
|
16
|
+
// Calcular dígito verificador
|
|
17
|
+
const factores = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
|
|
18
|
+
let suma = 0;
|
|
19
|
+
for (let i = 0; i < 10; i++) {
|
|
20
|
+
suma += parseInt(rucLimpio.charAt(i)) * factores[i];
|
|
21
|
+
}
|
|
22
|
+
const resto = suma % 11;
|
|
23
|
+
const dvCalculado = 11 - resto;
|
|
24
|
+
const dvFinal = dvCalculado === 10 ? 0 : dvCalculado === 11 ? 1 : dvCalculado;
|
|
25
|
+
const dvIngresado = parseInt(rucLimpio.charAt(10));
|
|
26
|
+
if (dvFinal !== dvIngresado) {
|
|
27
|
+
return { valido: false, ruc: rucLimpio, tipo: 'Desconocido', mensaje: 'Dígito verificador inválido' };
|
|
28
|
+
}
|
|
29
|
+
let tipo = 'Desconocido';
|
|
30
|
+
if (prefijo === '10')
|
|
31
|
+
tipo = 'Persona Natural';
|
|
32
|
+
else if (prefijo === '20')
|
|
33
|
+
tipo = 'Persona Jurídica';
|
|
34
|
+
else if (prefijo === '15')
|
|
35
|
+
tipo = 'Persona Natural sin negocio';
|
|
36
|
+
else if (prefijo === '17')
|
|
37
|
+
tipo = 'Persona Natural no domiciliada';
|
|
38
|
+
return { valido: true, ruc: rucLimpio, tipo, mensaje: 'RUC válido' };
|
|
39
|
+
}
|
|
40
|
+
function validarDni(dni) {
|
|
41
|
+
const dniLimpio = dni.replace(/[-\s]/g, '');
|
|
42
|
+
if (!/^\d{8}$/.test(dniLimpio)) {
|
|
43
|
+
return { valido: false, dni: dniLimpio, mensaje: 'DNI debe tener 8 dígitos' };
|
|
44
|
+
}
|
|
45
|
+
return { valido: true, dni: dniLimpio, mensaje: 'DNI válido' };
|
|
46
|
+
}
|
|
47
|
+
function formatearRuc(ruc) {
|
|
48
|
+
return ruc.replace(/[^0-9]/g, '');
|
|
49
|
+
}
|
|
50
|
+
async function consultarRuc(that, ruc) {
|
|
51
|
+
const rucLimpio = ruc.replace(/[^0-9]/g, '');
|
|
52
|
+
try {
|
|
53
|
+
// API pública de consulta RUC
|
|
54
|
+
const response = await that.helpers.httpRequest({
|
|
55
|
+
method: 'GET',
|
|
56
|
+
url: `https://api.apis.net.pe/v1/ruc?numero=${rucLimpio}`,
|
|
57
|
+
json: true,
|
|
58
|
+
});
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return {
|
|
63
|
+
ruc: rucLimpio,
|
|
64
|
+
mensaje: 'No se pudo consultar el RUC. Use API con token para consultas frecuentes.',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Funciones Indicadores
|
|
69
|
+
async function obtenerTipoCambio(that) {
|
|
70
|
+
var _a;
|
|
71
|
+
try {
|
|
72
|
+
// API tipo de cambio Perú
|
|
73
|
+
const response = await that.helpers.httpRequest({
|
|
74
|
+
method: 'GET',
|
|
75
|
+
url: 'https://api.apis.net.pe/v1/tipo-cambio-sunat',
|
|
76
|
+
json: true,
|
|
77
|
+
});
|
|
78
|
+
const data = response;
|
|
79
|
+
return {
|
|
80
|
+
indicador: 'Tipo de Cambio',
|
|
81
|
+
compra: data.compra,
|
|
82
|
+
venta: data.venta,
|
|
83
|
+
fecha: data.fecha,
|
|
84
|
+
fuente: 'SUNAT/SBS',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Fallback con API alternativa
|
|
89
|
+
try {
|
|
90
|
+
const response = await that.helpers.httpRequest({
|
|
91
|
+
method: 'GET',
|
|
92
|
+
url: 'https://api.exchangerate-api.com/v4/latest/USD',
|
|
93
|
+
json: true,
|
|
94
|
+
});
|
|
95
|
+
const data = response;
|
|
96
|
+
const pen = ((_a = data.rates) === null || _a === void 0 ? void 0 : _a.PEN) || 3.75;
|
|
97
|
+
return {
|
|
98
|
+
indicador: 'Tipo de Cambio',
|
|
99
|
+
compra: pen - 0.02,
|
|
100
|
+
venta: pen + 0.02,
|
|
101
|
+
fecha: new Date().toISOString().split('T')[0],
|
|
102
|
+
fuente: 'Exchange Rate API',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return {
|
|
107
|
+
indicador: 'Tipo de Cambio',
|
|
108
|
+
compra: 3.73,
|
|
109
|
+
venta: 3.77,
|
|
110
|
+
fecha: new Date().toISOString().split('T')[0],
|
|
111
|
+
fuente: 'Estimado',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function obtenerUit(that) {
|
|
117
|
+
// UIT 2025: S/ 5,350 (valor fijo anual)
|
|
118
|
+
const uit2025 = 5350;
|
|
119
|
+
return {
|
|
120
|
+
indicador: 'UIT',
|
|
121
|
+
valor: uit2025,
|
|
122
|
+
año: 2025,
|
|
123
|
+
fuente: 'SUNAT',
|
|
124
|
+
nota: 'Unidad Impositiva Tributaria vigente',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function convertirUitSoles(uit, valorUit) {
|
|
128
|
+
return {
|
|
129
|
+
uit,
|
|
130
|
+
valorUit,
|
|
131
|
+
soles: Math.round(uit * valorUit * 100) / 100,
|
|
132
|
+
fecha: new Date().toISOString().split('T')[0],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function convertirSolesUit(soles, valorUit) {
|
|
136
|
+
return {
|
|
137
|
+
soles,
|
|
138
|
+
valorUit,
|
|
139
|
+
uit: Math.round((soles / valorUit) * 100) / 100,
|
|
140
|
+
fecha: new Date().toISOString().split('T')[0],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Función para emitir factura
|
|
144
|
+
async function emitirFactura(that, provider, apiKey, tipoComprobante, datos) {
|
|
145
|
+
if (provider === 'none') {
|
|
146
|
+
throw new Error('Configura credenciales de Nubefact o Efact para emitir facturas');
|
|
147
|
+
}
|
|
148
|
+
if (provider === 'nubefact') {
|
|
149
|
+
const response = await that.helpers.httpRequest({
|
|
150
|
+
method: 'POST',
|
|
151
|
+
url: 'https://api.nubefact.com/api/v1/invoices',
|
|
152
|
+
headers: {
|
|
153
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
},
|
|
156
|
+
body: {
|
|
157
|
+
operacion: 'generar_comprobante',
|
|
158
|
+
tipo_de_comprobante: tipoComprobante,
|
|
159
|
+
serie: datos.serie,
|
|
160
|
+
numero: datos.numero,
|
|
161
|
+
cliente_tipo_de_documento: datos.tipoDoc,
|
|
162
|
+
cliente_numero_de_documento: datos.numDoc,
|
|
163
|
+
cliente_denominacion: datos.razonSocial,
|
|
164
|
+
total: datos.total,
|
|
165
|
+
},
|
|
166
|
+
json: true,
|
|
167
|
+
});
|
|
168
|
+
return response;
|
|
169
|
+
}
|
|
170
|
+
throw new Error(`Proveedor ${provider} no implementado`);
|
|
171
|
+
}
|
|
172
|
+
class SunatPeru {
|
|
173
|
+
constructor() {
|
|
174
|
+
this.description = {
|
|
175
|
+
displayName: 'SUNAT Perú',
|
|
176
|
+
name: 'sunatPeru',
|
|
177
|
+
icon: 'file:sunat.svg',
|
|
178
|
+
group: ['transform'],
|
|
179
|
+
version: 1,
|
|
180
|
+
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
|
181
|
+
description: 'Facturación Electrónica Perú - RUC/DNI, Tipo Cambio, UIT',
|
|
182
|
+
defaults: {
|
|
183
|
+
name: 'SUNAT Perú',
|
|
184
|
+
},
|
|
185
|
+
inputs: ['main'],
|
|
186
|
+
outputs: ['main'],
|
|
187
|
+
credentials: [
|
|
188
|
+
{
|
|
189
|
+
name: 'sunatPeruApi',
|
|
190
|
+
required: false,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
properties: [
|
|
194
|
+
{
|
|
195
|
+
displayName: 'Recurso',
|
|
196
|
+
name: 'resource',
|
|
197
|
+
type: 'options',
|
|
198
|
+
noDataExpression: true,
|
|
199
|
+
options: [
|
|
200
|
+
{ name: 'RUC/DNI', value: 'ruc' },
|
|
201
|
+
{ name: 'Indicadores', value: 'indicadores' },
|
|
202
|
+
{ name: 'Emitir Factura', value: 'factura' },
|
|
203
|
+
],
|
|
204
|
+
default: 'ruc',
|
|
205
|
+
},
|
|
206
|
+
// Operaciones RUC/DNI
|
|
207
|
+
{
|
|
208
|
+
displayName: 'Operación',
|
|
209
|
+
name: 'operation',
|
|
210
|
+
type: 'options',
|
|
211
|
+
noDataExpression: true,
|
|
212
|
+
displayOptions: { show: { resource: ['ruc'] } },
|
|
213
|
+
options: [
|
|
214
|
+
{ name: 'Validar RUC', value: 'validar_ruc', description: 'Validar formato de RUC', action: 'Validar RUC' },
|
|
215
|
+
{ name: 'Validar DNI', value: 'validar_dni', description: 'Validar formato de DNI', action: 'Validar DNI' },
|
|
216
|
+
{ name: 'Formatear RUC', value: 'formatear', description: 'Limpiar RUC', action: 'Formatear RUC' },
|
|
217
|
+
{ name: 'Consultar RUC', value: 'consultar', description: 'Obtener datos de SUNAT', action: 'Consultar RUC' },
|
|
218
|
+
],
|
|
219
|
+
default: 'validar_ruc',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
displayName: 'RUC',
|
|
223
|
+
name: 'ruc',
|
|
224
|
+
type: 'string',
|
|
225
|
+
default: '',
|
|
226
|
+
placeholder: '20123456789',
|
|
227
|
+
required: true,
|
|
228
|
+
displayOptions: { show: { resource: ['ruc'], operation: ['validar_ruc', 'formatear', 'consultar'] } },
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
displayName: 'DNI',
|
|
232
|
+
name: 'dni',
|
|
233
|
+
type: 'string',
|
|
234
|
+
default: '',
|
|
235
|
+
placeholder: '12345678',
|
|
236
|
+
required: true,
|
|
237
|
+
displayOptions: { show: { resource: ['ruc'], operation: ['validar_dni'] } },
|
|
238
|
+
},
|
|
239
|
+
// Operaciones Indicadores
|
|
240
|
+
{
|
|
241
|
+
displayName: 'Operación',
|
|
242
|
+
name: 'operation',
|
|
243
|
+
type: 'options',
|
|
244
|
+
noDataExpression: true,
|
|
245
|
+
displayOptions: { show: { resource: ['indicadores'] } },
|
|
246
|
+
options: [
|
|
247
|
+
{ name: 'Tipo de Cambio', value: 'tipo_cambio', description: 'Dólar compra/venta SBS', action: 'Obtener tipo cambio' },
|
|
248
|
+
{ name: 'Valor UIT', value: 'uit', description: 'Unidad Impositiva Tributaria', action: 'Obtener UIT' },
|
|
249
|
+
{ name: 'UIT a Soles', value: 'uit_soles', description: 'Convertir UIT a Soles', action: 'Convertir UIT a Soles' },
|
|
250
|
+
{ name: 'Soles a UIT', value: 'soles_uit', description: 'Convertir Soles a UIT', action: 'Convertir Soles a UIT' },
|
|
251
|
+
],
|
|
252
|
+
default: 'tipo_cambio',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
displayName: 'Monto UIT',
|
|
256
|
+
name: 'montoUit',
|
|
257
|
+
type: 'number',
|
|
258
|
+
default: 10,
|
|
259
|
+
displayOptions: { show: { resource: ['indicadores'], operation: ['uit_soles'] } },
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
displayName: 'Monto Soles',
|
|
263
|
+
name: 'montoSoles',
|
|
264
|
+
type: 'number',
|
|
265
|
+
default: 50000,
|
|
266
|
+
displayOptions: { show: { resource: ['indicadores'], operation: ['soles_uit'] } },
|
|
267
|
+
},
|
|
268
|
+
// Operaciones Factura
|
|
269
|
+
{
|
|
270
|
+
displayName: 'Operación',
|
|
271
|
+
name: 'operation',
|
|
272
|
+
type: 'options',
|
|
273
|
+
noDataExpression: true,
|
|
274
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
275
|
+
options: [
|
|
276
|
+
{ name: 'Factura', value: 'factura', description: 'Tipo 01', action: 'Emitir Factura' },
|
|
277
|
+
{ name: 'Boleta de Venta', value: 'boleta', description: 'Tipo 03', action: 'Emitir Boleta' },
|
|
278
|
+
{ name: 'Nota de Crédito', value: 'nota_credito', description: 'Tipo 07', action: 'Emitir Nota Credito' },
|
|
279
|
+
],
|
|
280
|
+
default: 'factura',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
displayName: 'Tipo Documento Cliente',
|
|
284
|
+
name: 'tipoDocCliente',
|
|
285
|
+
type: 'options',
|
|
286
|
+
options: [
|
|
287
|
+
{ name: 'RUC', value: '6' },
|
|
288
|
+
{ name: 'DNI', value: '1' },
|
|
289
|
+
{ name: 'Carnet Extranjería', value: '4' },
|
|
290
|
+
{ name: 'Pasaporte', value: '7' },
|
|
291
|
+
],
|
|
292
|
+
default: '6',
|
|
293
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
displayName: 'Número Documento',
|
|
297
|
+
name: 'numDocCliente',
|
|
298
|
+
type: 'string',
|
|
299
|
+
default: '',
|
|
300
|
+
placeholder: '20123456789',
|
|
301
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
displayName: 'Razón Social',
|
|
305
|
+
name: 'razonSocial',
|
|
306
|
+
type: 'string',
|
|
307
|
+
default: '',
|
|
308
|
+
placeholder: 'Empresa S.A.C.',
|
|
309
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
displayName: 'Subtotal',
|
|
313
|
+
name: 'subtotal',
|
|
314
|
+
type: 'number',
|
|
315
|
+
default: 0,
|
|
316
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
displayName: 'IGV (18%)',
|
|
320
|
+
name: 'igv',
|
|
321
|
+
type: 'number',
|
|
322
|
+
default: 0,
|
|
323
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
displayName: 'Total',
|
|
327
|
+
name: 'total',
|
|
328
|
+
type: 'number',
|
|
329
|
+
default: 0,
|
|
330
|
+
displayOptions: { show: { resource: ['factura'] } },
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
async execute() {
|
|
336
|
+
const items = this.getInputData();
|
|
337
|
+
const returnData = [];
|
|
338
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
339
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
340
|
+
for (let i = 0; i < items.length; i++) {
|
|
341
|
+
let result = {};
|
|
342
|
+
try {
|
|
343
|
+
// RUC/DNI
|
|
344
|
+
if (resource === 'ruc') {
|
|
345
|
+
if (operation === 'validar_ruc') {
|
|
346
|
+
const ruc = this.getNodeParameter('ruc', i);
|
|
347
|
+
result = validarRuc(ruc);
|
|
348
|
+
}
|
|
349
|
+
else if (operation === 'validar_dni') {
|
|
350
|
+
const dni = this.getNodeParameter('dni', i);
|
|
351
|
+
result = validarDni(dni);
|
|
352
|
+
}
|
|
353
|
+
else if (operation === 'formatear') {
|
|
354
|
+
const ruc = this.getNodeParameter('ruc', i);
|
|
355
|
+
result = { ruc: formatearRuc(ruc) };
|
|
356
|
+
}
|
|
357
|
+
else if (operation === 'consultar') {
|
|
358
|
+
const ruc = this.getNodeParameter('ruc', i);
|
|
359
|
+
result = await consultarRuc(this, ruc);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Indicadores
|
|
363
|
+
else if (resource === 'indicadores') {
|
|
364
|
+
if (operation === 'tipo_cambio') {
|
|
365
|
+
result = await obtenerTipoCambio(this);
|
|
366
|
+
}
|
|
367
|
+
else if (operation === 'uit') {
|
|
368
|
+
result = await obtenerUit(this);
|
|
369
|
+
}
|
|
370
|
+
else if (operation === 'uit_soles') {
|
|
371
|
+
const montoUit = this.getNodeParameter('montoUit', i);
|
|
372
|
+
const uit = await obtenerUit(this);
|
|
373
|
+
result = convertirUitSoles(montoUit, uit.valor);
|
|
374
|
+
}
|
|
375
|
+
else if (operation === 'soles_uit') {
|
|
376
|
+
const montoSoles = this.getNodeParameter('montoSoles', i);
|
|
377
|
+
const uit = await obtenerUit(this);
|
|
378
|
+
result = convertirSolesUit(montoSoles, uit.valor);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Factura
|
|
382
|
+
else if (resource === 'factura') {
|
|
383
|
+
const credentials = await this.getCredentials('sunatPeruApi').catch(() => null);
|
|
384
|
+
if (!credentials || credentials.provider === 'none') {
|
|
385
|
+
throw new Error('Configura credenciales de Nubefact o Efact para emitir facturas');
|
|
386
|
+
}
|
|
387
|
+
const tipoComp = operation === 'factura' ? '01' : operation === 'boleta' ? '03' : '07';
|
|
388
|
+
const datos = {
|
|
389
|
+
tipoDoc: this.getNodeParameter('tipoDocCliente', i),
|
|
390
|
+
numDoc: this.getNodeParameter('numDocCliente', i),
|
|
391
|
+
razonSocial: this.getNodeParameter('razonSocial', i),
|
|
392
|
+
subtotal: this.getNodeParameter('subtotal', i),
|
|
393
|
+
igv: this.getNodeParameter('igv', i),
|
|
394
|
+
total: this.getNodeParameter('total', i),
|
|
395
|
+
};
|
|
396
|
+
result = await emitirFactura(this, credentials.provider, credentials.nubefactApiKey, tipoComp, datos);
|
|
397
|
+
}
|
|
398
|
+
returnData.push({ json: result });
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
if (this.continueOnFail()) {
|
|
402
|
+
returnData.push({ json: { error: error.message } });
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return [returnData];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
exports.SunatPeru = SunatPeru;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
|
|
2
|
+
<rect width="60" height="60" rx="8" fill="#D91023"/>
|
|
3
|
+
<text x="30" y="24" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="white" text-anchor="middle">SUNAT</text>
|
|
4
|
+
<text x="30" y="40" font-family="Arial, sans-serif" font-size="7" fill="white" text-anchor="middle">PERÚ</text>
|
|
5
|
+
<rect x="10" y="48" width="13" height="6" fill="#D91023"/>
|
|
6
|
+
<rect x="23" y="48" width="14" height="6" fill="white"/>
|
|
7
|
+
<rect x="37" y="48" width="13" height="6" fill="#D91023"/>
|
|
8
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-sunat-peru",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Nodo n8n para integración con SUNAT Perú - Factura Electrónica, RUC/DNI, tipo cambio, UIT",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"sunat",
|
|
9
|
+
"peru",
|
|
10
|
+
"ruc",
|
|
11
|
+
"dni",
|
|
12
|
+
"facturacion",
|
|
13
|
+
"impuestos",
|
|
14
|
+
"uit",
|
|
15
|
+
"latam"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"homepage": "https://github.com/Manuelreyesbravo/n8n-nodes-sunat-peru",
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "Manuel Reyes Bravo",
|
|
21
|
+
"email": "manuelreyesbravo@gmail.com"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Manuelreyesbravo/n8n-nodes-sunat-peru.git"
|
|
26
|
+
},
|
|
27
|
+
"main": "index.js",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc && gulp build:icons",
|
|
30
|
+
"dev": "tsc --watch",
|
|
31
|
+
"format": "prettier nodes credentials --write",
|
|
32
|
+
"lint": "eslint nodes credentials package.json",
|
|
33
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
34
|
+
"prepublishOnly": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"n8n": {
|
|
40
|
+
"n8nNodesApiVersion": 1,
|
|
41
|
+
"credentials": [
|
|
42
|
+
"dist/credentials/SunatPeruApi.credentials.js"
|
|
43
|
+
],
|
|
44
|
+
"nodes": [
|
|
45
|
+
"dist/nodes/SunatPeru/SunatPeru.node.js"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^20.10.0",
|
|
50
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
51
|
+
"eslint": "^8.56.0",
|
|
52
|
+
"gulp": "^4.0.2",
|
|
53
|
+
"n8n-workflow": "*",
|
|
54
|
+
"prettier": "^3.1.0",
|
|
55
|
+
"typescript": "^5.3.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"n8n-workflow": "*"
|
|
59
|
+
}
|
|
60
|
+
}
|