@facturacr/atv-sdk 0.0.7-beta → 0.0.8-beta
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/.github/workflows/ci.yml +7 -4
- package/__tests__/stubs/commonExpectedXml.xml +94 -0
- package/__tests__/stubs/createDocument.stub.ts +80 -0
- package/__tests__/tests/ATV/__snapshots__/index.test.ts.snap +3 -0
- package/__tests__/tests/ATV/index.test.ts +50 -0
- package/doc/atv-structures/4.3/ANEXOS Y ESTRUCTURAS_V4.3.pdf +0 -0
- package/doc/atv-structures/4.3/API Ministerio de Hacienda /342/200/223 Dropbox Paper.pdf +0 -0
- package/doc/atv-structures/4.3/MensajeHacienda_V4.3.xsd.xml +179 -0
- package/doc/atv-structures/4.3/MensajeReceptor_V4.3.xsd.xml +163 -0
- package/doc/atv-structures/4.3/NotaCreditoElectronica_V4.3.xsd.xml +1804 -0
- package/doc/atv-structures/4.3/NotaDebitoElectronica_V4.3.xsd.xml +1804 -0
- package/examples/createAndSend.ts +27 -32
- package/examples/getToken.ts +3 -2
- package/jest-setup.js +5 -0
- package/jest.config.js +6 -5
- package/package.json +18 -14
- package/src/ATV/core/Clave.ts +41 -0
- package/src/ATV/core/Document.ts +123 -0
- package/src/ATV/core/DocumentType.ts +23 -0
- package/src/ATV/core/FullConsecutive.ts +29 -0
- package/src/ATV/core/OrderLine.ts +95 -0
- package/src/ATV/core/Person.ts +68 -0
- package/src/ATV/core/Summary.type.ts +20 -0
- package/src/ATV/core/documentNames.types.ts +5 -0
- package/src/ATV/index.ts +102 -0
- package/src/ATV/mappers/billDocToAtv.ts +103 -0
- package/src/ATV/types.ts +37 -0
- package/src/ATV/useCases/createDocument/index.ts +137 -0
- package/src/ATV/useCases/createDocument/types.ts +79 -0
- package/src/helpers/comprobantes.ts +1 -1
- package/src/lib/genJSON/index.ts +2 -2
- package/src/lib/genXML/xmlConfig.ts +2 -2
- package/src/services/getToken/GetToken.ts +1 -1
- package/src/types/facturaInterfaces.d.ts +27 -20
- package/__tests__/tests/electronicBill.test.ts +0 -28
- package/dist/__tests__/stubs/frontendRequest.stub.d.ts +0 -3
- package/dist/__tests__/stubs/frontendRequest.stub.js +0 -61
- package/dist/__tests__/stubs/frontendRequest.stub.js.map +0 -1
- package/dist/__tests__/stubs/token.stub.d.ts +0 -17
- package/dist/__tests__/stubs/token.stub.js +0 -21
- package/dist/__tests__/stubs/token.stub.js.map +0 -1
- package/dist/__tests__/tests/electronicBill.test.d.ts +0 -1
- package/dist/__tests__/tests/electronicBill.test.js +0 -38
- package/dist/__tests__/tests/electronicBill.test.js.map +0 -1
- package/dist/__tests__/tests/getClave.test.d.ts +0 -1
- package/dist/__tests__/tests/getClave.test.js +0 -15
- package/dist/__tests__/tests/getClave.test.js.map +0 -1
- package/dist/__tests__/tests/getToken.test.d.ts +0 -0
- package/dist/__tests__/tests/getToken.test.js +0 -28
- package/dist/__tests__/tests/getToken.test.js.map +0 -1
- package/dist/__tests__/tests/lib/getClave.test.d.ts +0 -1
- package/dist/__tests__/tests/lib/getClave.test.js +0 -15
- package/dist/__tests__/tests/lib/getClave.test.js.map +0 -1
- package/dist/__tests__/tests/services/getToken.test.d.ts +0 -1
- package/dist/__tests__/tests/services/getToken.test.js +0 -27
- package/dist/__tests__/tests/services/getToken.test.js.map +0 -1
- package/dist/examples/confirmXML.d.ts +0 -1
- package/dist/examples/confirmXML.js +0 -89
- package/dist/examples/confirmXML.js.map +0 -1
- package/dist/examples/consultXML.d.ts +0 -1
- package/dist/examples/consultXML.js +0 -55
- package/dist/examples/consultXML.js.map +0 -1
- package/dist/examples/createAndSend.d.ts +0 -1
- package/dist/examples/createAndSend.js +0 -77
- package/dist/examples/createAndSend.js.map +0 -1
- package/dist/examples/createCreditNote.d.ts +0 -1
- package/dist/examples/createCreditNote.js +0 -52
- package/dist/examples/createCreditNote.js.map +0 -1
- package/dist/examples/createDebitNote.d.ts +0 -1
- package/dist/examples/createDebitNote.js +0 -52
- package/dist/examples/createDebitNote.js.map +0 -1
- package/dist/examples/genBasicXML.d.ts +0 -1
- package/dist/examples/genBasicXML.js +0 -44
- package/dist/examples/genBasicXML.js.map +0 -1
- package/dist/examples/getClave.d.ts +0 -1
- package/dist/examples/getClave.js +0 -12
- package/dist/examples/getClave.js.map +0 -1
- package/dist/examples/getToken.d.ts +0 -1
- package/dist/examples/getToken.js +0 -16
- package/dist/examples/getToken.js.map +0 -1
- package/dist/src/ATV.d.ts +0 -17
- package/dist/src/ATV.js +0 -16
- package/dist/src/ATV.js.map +0 -1
- package/dist/src/confirmXML.d.ts +0 -8
- package/dist/src/confirmXML.js +0 -44
- package/dist/src/confirmXML.js.map +0 -1
- package/dist/src/creditNote.d.ts +0 -14
- package/dist/src/creditNote.js +0 -77
- package/dist/src/creditNote.js.map +0 -1
- package/dist/src/data/codigoMoneda.d.ts +0 -1
- package/dist/src/data/codigoMoneda.js +0 -184
- package/dist/src/data/codigoMoneda.js.map +0 -1
- package/dist/src/data/exoneracion.d.ts +0 -1
- package/dist/src/data/exoneracion.js +0 -15
- package/dist/src/data/exoneracion.js.map +0 -1
- package/dist/src/data/impuestos.d.ts +0 -25
- package/dist/src/data/impuestos.js +0 -51
- package/dist/src/data/impuestos.js.map +0 -1
- package/dist/src/data/tipoCedula.d.ts +0 -7
- package/dist/src/data/tipoCedula.js +0 -17
- package/dist/src/data/tipoCedula.js.map +0 -1
- package/dist/src/data/tipoDocumento.d.ts +0 -26
- package/dist/src/data/tipoDocumento.js +0 -30
- package/dist/src/data/tipoDocumento.js.map +0 -1
- package/dist/src/data/unidadesMedida.d.ts +0 -5
- package/dist/src/data/unidadesMedida.js +0 -141
- package/dist/src/data/unidadesMedida.js.map +0 -1
- package/dist/src/debitNote.d.ts +0 -14
- package/dist/src/debitNote.js +0 -77
- package/dist/src/debitNote.js.map +0 -1
- package/dist/src/electronicBill.d.ts +0 -3
- package/dist/src/electronicBill.js +0 -61
- package/dist/src/electronicBill.js.map +0 -1
- package/dist/src/helpers/comprobantes.d.ts +0 -7
- package/dist/src/helpers/comprobantes.js +0 -92
- package/dist/src/helpers/comprobantes.js.map +0 -1
- package/dist/src/index.d.ts +0 -8
- package/dist/src/index.js.map +0 -1
- package/dist/src/lib/genClave/index.d.ts +0 -9
- package/dist/src/lib/genClave/index.js +0 -119
- package/dist/src/lib/genClave/index.js.map +0 -1
- package/dist/src/lib/genJSON/confirmXML.d.ts +0 -7
- package/dist/src/lib/genJSON/confirmXML.js +0 -46
- package/dist/src/lib/genJSON/confirmXML.js.map +0 -1
- package/dist/src/lib/genJSON/index.d.ts +0 -3
- package/dist/src/lib/genJSON/index.js +0 -133
- package/dist/src/lib/genJSON/index.js.map +0 -1
- package/dist/src/lib/genXML/index.d.ts +0 -7
- package/dist/src/lib/genXML/index.js +0 -51
- package/dist/src/lib/genXML/index.js.map +0 -1
- package/dist/src/lib/genXML/sigXML/genKeysAndCert.d.ts +0 -6
- package/dist/src/lib/genXML/sigXML/genKeysAndCert.js +0 -72
- package/dist/src/lib/genXML/sigXML/genKeysAndCert.js.map +0 -1
- package/dist/src/lib/genXML/sigXML/index.d.ts +0 -1
- package/dist/src/lib/genXML/sigXML/index.js +0 -86
- package/dist/src/lib/genXML/sigXML/index.js.map +0 -1
- package/dist/src/lib/genXML/xmlConfig.d.ts +0 -55
- package/dist/src/lib/genXML/xmlConfig.js +0 -36
- package/dist/src/lib/genXML/xmlConfig.js.map +0 -1
- package/dist/src/services/getToken/GetToken.d.ts +0 -8
- package/dist/src/services/getToken/GetToken.js +0 -66
- package/dist/src/services/getToken/GetToken.js.map +0 -1
- package/dist/src/services/getToken/__tests__/GetToken.test.d.ts +0 -1
- package/dist/src/services/getToken/__tests__/GetToken.test.js +0 -86
- package/dist/src/services/getToken/__tests__/GetToken.test.js.map +0 -1
- package/dist/src/services/getToken/index.d.ts +0 -3
- package/dist/src/services/getToken/index.js +0 -22
- package/dist/src/services/getToken/index.js.map +0 -1
- package/dist/src/services/getToken/types.d.ts +0 -27
- package/dist/src/services/getToken/types.js +0 -3
- package/dist/src/services/getToken/types.js.map +0 -1
- package/dist/src/services/send/index.d.ts +0 -2
- package/dist/src/services/send/index.js +0 -40
- package/dist/src/services/send/index.js.map +0 -1
- package/dist/src/types/xml/notaDeCredito.d.ts +0 -21
- package/dist/src/types/xml/notaDeCredito.js +0 -3
- package/dist/src/types/xml/notaDeCredito.js.map +0 -1
- package/dist/src/types/xml/notaDeDebito.d.ts +0 -21
- package/dist/src/types/xml/notaDeDebito.js +0 -3
- package/dist/src/types/xml/notaDeDebito.js.map +0 -1
- package/src/ATV.ts +0 -28
- /package/{atv-structures → doc/atv-structures}/4.3/FacturaElectronicaExportacion_V4.3.xsd.xml +0 -0
- /package/{atv-structures → doc/atv-structures}/4.3/FacturaElectronica_V4.3.xsd.xml +0 -0
package/src/ATV/index.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
import { GetToken } from '../services/getToken/GetToken'
|
|
4
|
+
import { GetTokenDto, GetTokenResponse } from '../services/getToken/types'
|
|
5
|
+
import * as parser from 'fast-xml-parser'
|
|
6
|
+
import qs from 'querystring'
|
|
7
|
+
import { ConfirmationMessageRaw } from '@src/types/facturaInterfaces'
|
|
8
|
+
import { ATVOptions, ConfirmationMessage, Mode, SendConfirmationInput, SendResponse } from './types'
|
|
9
|
+
import { Command, CreateAndSendDocumentResponse, CreateDocumentInput } from './useCases/createDocument/types'
|
|
10
|
+
import { CreateDocumentCommand } from './useCases/createDocument'
|
|
11
|
+
|
|
12
|
+
export class ATV {
|
|
13
|
+
public readonly options: ATVOptions
|
|
14
|
+
constructor(options?: ATVOptions, public readonly mode: Mode = 'prod') {
|
|
15
|
+
this.options = options
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public getToken(params: GetTokenDto): Promise<GetTokenResponse> {
|
|
19
|
+
const tokenService = new GetToken(this)
|
|
20
|
+
return tokenService.execute(params)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public createDocumentCommand(input: CreateDocumentInput): Promise<CreateAndSendDocumentResponse> {
|
|
24
|
+
const createDocument = new CreateDocumentCommand(this)
|
|
25
|
+
return createDocument.execute(input)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async sendDocument(input: Command): Promise<SendResponse> {
|
|
29
|
+
try {
|
|
30
|
+
const response = await axios(input)
|
|
31
|
+
return {
|
|
32
|
+
status: response.status,
|
|
33
|
+
location: response.headers.location,
|
|
34
|
+
errorCause: null
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
const response = error.response || {}
|
|
38
|
+
const header = response.headers || {}
|
|
39
|
+
return {
|
|
40
|
+
status: error.response.status || 500,
|
|
41
|
+
errorCause: header['x-error-cause'],
|
|
42
|
+
location: null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
48
|
+
public async sendConfirmation(input: SendConfirmationInput) {
|
|
49
|
+
try {
|
|
50
|
+
const response = await axios(input)
|
|
51
|
+
console.log('response', response);
|
|
52
|
+
const xmlResponse = response.data['respuesta-xml']
|
|
53
|
+
if (!xmlResponse) {
|
|
54
|
+
const state = response.data['ind-estado']
|
|
55
|
+
return {
|
|
56
|
+
status: response.status,
|
|
57
|
+
state,
|
|
58
|
+
errorCause: null
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const xmlString = this.decodeBase64(xmlResponse)
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
status: response.status,
|
|
65
|
+
confirmation: this.parseConfirmationMessage(xmlString),
|
|
66
|
+
xml: xmlString,
|
|
67
|
+
errorCause: null
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const response = error.response || {}
|
|
71
|
+
return {
|
|
72
|
+
status: response.status || 500,
|
|
73
|
+
errorCause: response.statusText
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private parseConfirmationMessage(xml: string): ConfirmationMessage {
|
|
79
|
+
const parsedXml: ConfirmationMessageRaw = parser.parse(xml, {
|
|
80
|
+
ignoreAttributes: false,
|
|
81
|
+
tagValueProcessor: a => qs.unescape(a.replace(/(\r\n|\n|\r)/gm, '')),
|
|
82
|
+
ignoreNameSpace: false,
|
|
83
|
+
parseNodeValue: false
|
|
84
|
+
})
|
|
85
|
+
const msj = parsedXml.MensajeHacienda
|
|
86
|
+
return {
|
|
87
|
+
clave: msj.Clave,
|
|
88
|
+
message: msj.Mensaje,
|
|
89
|
+
details: msj.DetalleMensaje,
|
|
90
|
+
emitterFullName: msj.NombreEmisor,
|
|
91
|
+
emitterIdentifierId: msj.NumeroCedulaEmisor,
|
|
92
|
+
receiverIdentifierId: msj.NumeroCedulaReceptor,
|
|
93
|
+
taxesTotalAmount: msj.MontoTotalImpuesto,
|
|
94
|
+
totalInvoiceAmount: msj.TotalFactura
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private decodeBase64(encodedStr: string): string {
|
|
99
|
+
const buff = Buffer.from(encodedStr, 'base64')
|
|
100
|
+
return buff.toString('utf-8')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Document, FacturaElectronicaContenedor, DetalleServicio, Resumen, Persona } from '@src/types/facturaInterfaces'
|
|
2
|
+
import { Document as DomainDocument } from '../core/Document'
|
|
3
|
+
import { OrderLine } from '../core/OrderLine'
|
|
4
|
+
import { Person } from '../core/Person'
|
|
5
|
+
|
|
6
|
+
type AtvFormat = FacturaElectronicaContenedor
|
|
7
|
+
|
|
8
|
+
const mapOrderLinesToAtvFormat = (orderLines: OrderLine[]): DetalleServicio => {
|
|
9
|
+
const LineaDetalle = orderLines.map<DetalleServicio['LineaDetalle'][0]>((orderLine) => {
|
|
10
|
+
return {
|
|
11
|
+
NumeroLinea: orderLine.lineNumber,
|
|
12
|
+
Codigo: orderLine.code,
|
|
13
|
+
// CodigoComercial
|
|
14
|
+
Cantidad: orderLine.quantity,
|
|
15
|
+
UnidadMedida: orderLine.measureUnit,
|
|
16
|
+
// UnidadMedidaComercial
|
|
17
|
+
Detalle: orderLine.detail,
|
|
18
|
+
PrecioUnitario: orderLine.unitaryPrice,
|
|
19
|
+
MontoTotal: orderLine.totalAmount,
|
|
20
|
+
// Descuento
|
|
21
|
+
SubTotal: orderLine.subTotal,
|
|
22
|
+
// BaseImponible
|
|
23
|
+
Impuesto: {
|
|
24
|
+
Codigo: orderLine.tax.code,
|
|
25
|
+
CodigoTarifa: orderLine.tax.rateCode,
|
|
26
|
+
Tarifa: orderLine.tax.rate,
|
|
27
|
+
Monto: orderLine.tax.amount
|
|
28
|
+
},
|
|
29
|
+
// ImpuestoNeto
|
|
30
|
+
MontoTotalLinea: orderLine.totalOrderLineAmount
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
return { LineaDetalle }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mapSummaryInvoice = (summaryInvoice: DomainDocument['summaryInvoice']): Resumen => {
|
|
37
|
+
return {
|
|
38
|
+
CodigoTipoMoneda: {
|
|
39
|
+
CodigoMoneda: summaryInvoice.currency.code,
|
|
40
|
+
TipoCambio: summaryInvoice.currency.exchangeRate
|
|
41
|
+
},
|
|
42
|
+
TotalServGravados: summaryInvoice.totalEncumberedServices,
|
|
43
|
+
TotalServExentos: summaryInvoice.totalExemptServices,
|
|
44
|
+
TotalMercanciasGravadas: summaryInvoice.totalEncumberedMerchandise,
|
|
45
|
+
TotalMercanciasExentas: summaryInvoice.totalExemptMerchandise,
|
|
46
|
+
TotalGravado: summaryInvoice.totalEncumbered,
|
|
47
|
+
TotalExento: summaryInvoice.totalExempt,
|
|
48
|
+
TotalExonerado: summaryInvoice.totalExonerated,
|
|
49
|
+
TotalVenta: summaryInvoice.totalSale,
|
|
50
|
+
TotalDescuentos: summaryInvoice.totalDiscounts,
|
|
51
|
+
TotalVentaNeta: summaryInvoice.totalNetSale,
|
|
52
|
+
TotalImpuesto: summaryInvoice.totalTaxes,
|
|
53
|
+
TotalComprobante: summaryInvoice.totalVoucher
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const mapPerson = (person: Person): Persona => {
|
|
58
|
+
return {
|
|
59
|
+
Nombre: person.fullName,
|
|
60
|
+
Identificacion: {
|
|
61
|
+
Tipo: person.identifierType,
|
|
62
|
+
Numero: person.identifierId
|
|
63
|
+
},
|
|
64
|
+
NombreComercial: person.commercialName,
|
|
65
|
+
Ubicacion: {
|
|
66
|
+
Provincia: person.location?.province,
|
|
67
|
+
Canton: person.location?.canton.padStart(2, '0'),
|
|
68
|
+
Distrito: person.location?.district.padStart(2, '0'),
|
|
69
|
+
Barrio: person.location?.neighborhood.padStart(2, '0'),
|
|
70
|
+
OtrasSenas: person.location?.details
|
|
71
|
+
},
|
|
72
|
+
Telefono: {
|
|
73
|
+
CodigoPais: person.phone?.countryCode,
|
|
74
|
+
NumTelefono: person.phone?.number
|
|
75
|
+
},
|
|
76
|
+
Fax: {
|
|
77
|
+
CodigoPais: person.fax?.countryCode,
|
|
78
|
+
NumTelefono: person.fax?.number
|
|
79
|
+
},
|
|
80
|
+
CorreoElectronico: person.email
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const mapDocumentToAtvFormat = (docName: string, document: DomainDocument): AtvFormat => {
|
|
85
|
+
const key = docName
|
|
86
|
+
const doc: Document = {
|
|
87
|
+
Clave: document.clave,
|
|
88
|
+
CodigoActividad: document.activityCode.padStart(6, '0'),
|
|
89
|
+
NumeroConsecutivo: document.fullConsecutive,
|
|
90
|
+
FechaEmision: document.issueDate.toISOString(),
|
|
91
|
+
Emisor: mapPerson(document.emitter),
|
|
92
|
+
Receptor: mapPerson(document.receiver),
|
|
93
|
+
CondicionVenta: document.conditionSale,
|
|
94
|
+
PlazoCredito: document.deadlineCredit,
|
|
95
|
+
MedioPago: document.paymentMethod,
|
|
96
|
+
DetalleServicio: mapOrderLinesToAtvFormat(document.orderLines),
|
|
97
|
+
ResumenFactura: mapSummaryInvoice(document.summaryInvoice),
|
|
98
|
+
Otros: document.others
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
[key]: doc
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/ATV/types.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type Mode = 'prod' | 'stg' | 'custom';
|
|
2
|
+
|
|
3
|
+
export type ATVOptions = {
|
|
4
|
+
defaultOverwrites?: {
|
|
5
|
+
tokenServiceUrl: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type TokenServiceProps = {
|
|
10
|
+
serviceUrl: string;
|
|
11
|
+
clientId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type SendResponse = {
|
|
15
|
+
status: number;
|
|
16
|
+
location: string;
|
|
17
|
+
errorCause?: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type SendConfirmationInput = {
|
|
21
|
+
url: string;
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: string;
|
|
24
|
+
'Content-Type': string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ConfirmationMessage = {
|
|
29
|
+
clave: string;
|
|
30
|
+
message: string;
|
|
31
|
+
details: string;
|
|
32
|
+
emitterFullName: string;
|
|
33
|
+
emitterIdentifierId: string;
|
|
34
|
+
receiverIdentifierId: string;
|
|
35
|
+
taxesTotalAmount: string;
|
|
36
|
+
totalInvoiceAmount: string;
|
|
37
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Person } from '@src/ATV/core/Person'
|
|
2
|
+
import { OrderLine } from '@src/ATV/core/OrderLine'
|
|
3
|
+
import { FullConsecutive } from '@src/ATV/core/FullConsecutive'
|
|
4
|
+
import { Document } from '@src/ATV/core/Document'
|
|
5
|
+
import { Clave } from '@src/ATV/core/Clave'
|
|
6
|
+
import { mapDocumentToAtvFormat as mapBillToAtvFormat } from '@src/ATV/mappers/billDocToAtv'
|
|
7
|
+
import { genXML } from '@src/lib/genXML'
|
|
8
|
+
import { CreateAndSendDocumentResponse, CreateDocumentInput, Command } from './types'
|
|
9
|
+
import { ATV } from '@src/ATV'
|
|
10
|
+
import { DocumentType } from '@src/ATV/core/DocumentType'
|
|
11
|
+
export type { CreateDocumentInput as CreateBillInput } from './types'
|
|
12
|
+
|
|
13
|
+
const options: { [key: string]: { serviceUrl: string}} = {
|
|
14
|
+
prod: {
|
|
15
|
+
serviceUrl: 'https://api.comprobanteselectronicos.go.cr/v1/recepcion'
|
|
16
|
+
},
|
|
17
|
+
stg: {
|
|
18
|
+
serviceUrl: 'https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/recepcion'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class CreateDocumentCommand {
|
|
23
|
+
private readonly serviceUrl: string;
|
|
24
|
+
|
|
25
|
+
constructor(scope: ATV) {
|
|
26
|
+
this.serviceUrl = options[scope.mode].serviceUrl
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async execute(dto: CreateDocumentInput): Promise<CreateAndSendDocumentResponse> {
|
|
30
|
+
const documentName = dto.document.documentName || 'FacturaElectronica'
|
|
31
|
+
const document = this.createDocument(dto.document)
|
|
32
|
+
const atvDocument = mapBillToAtvFormat(documentName, document)
|
|
33
|
+
const xml = await genXML(documentName, atvDocument, dto.signatureOptions)
|
|
34
|
+
const command = await this.createDocumentCommand(document, xml, dto.token)
|
|
35
|
+
return {
|
|
36
|
+
command,
|
|
37
|
+
extraData: {
|
|
38
|
+
xml
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async createDocumentCommand(document: Document, xml: string, token: string): Promise<Command> {
|
|
44
|
+
return {
|
|
45
|
+
url: this.serviceUrl,
|
|
46
|
+
method: 'post',
|
|
47
|
+
data: {
|
|
48
|
+
clave: document.clave,
|
|
49
|
+
fecha: document.issueDate.toISOString(),
|
|
50
|
+
emisor: {
|
|
51
|
+
tipoIdentificacion: document.emitter.identifierType,
|
|
52
|
+
numeroIdentificacion: document.emitter.identifierId
|
|
53
|
+
},
|
|
54
|
+
receptor: {
|
|
55
|
+
tipoIdentificacion: document.receiver.identifierType,
|
|
56
|
+
numeroIdentificacion: document.receiver.identifierId
|
|
57
|
+
},
|
|
58
|
+
comprobanteXml: this.encodeXML(xml)
|
|
59
|
+
},
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: 'bearer ' + token,
|
|
62
|
+
'Content-Type': 'application/json'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private createDocument(document: CreateDocumentInput['document']): Document {
|
|
68
|
+
const documentType = DocumentType.create(document.documentName)
|
|
69
|
+
const emitter = this.createEmitter(document.emitter)
|
|
70
|
+
const receiver = this.createReceiver(document.receiver)
|
|
71
|
+
const clave = this.createClave(document, documentType)
|
|
72
|
+
const fullConsective = this.createFullConsecutive(document, documentType)
|
|
73
|
+
const orderLines = this.createOrderLines(document)
|
|
74
|
+
return Document.create({
|
|
75
|
+
clave: clave,
|
|
76
|
+
fullConsecutive: fullConsective,
|
|
77
|
+
orderLines: orderLines,
|
|
78
|
+
activityCode: document.activityCode,
|
|
79
|
+
issueDate: new Date(),
|
|
80
|
+
emitter: emitter,
|
|
81
|
+
receiver: receiver,
|
|
82
|
+
conditionSale: '01',
|
|
83
|
+
paymentMethod: '01'
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private createEmitter(emitterInput: CreateDocumentInput['document']['emitter']): Person {
|
|
88
|
+
return Person.create(emitterInput)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private createReceiver(receiverInput: CreateDocumentInput['document']['receiver']): Person {
|
|
92
|
+
return Person.create(receiverInput)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private createOrderLines(dto: CreateDocumentInput['document']): OrderLine[] {
|
|
96
|
+
return dto.orderLines.map((orderLine, index) => {
|
|
97
|
+
return OrderLine.create({
|
|
98
|
+
detail: orderLine.detail,
|
|
99
|
+
unitaryPrice: orderLine.unitaryPrice,
|
|
100
|
+
lineNumber: orderLine.lineNumber || (index + 1).toString(),
|
|
101
|
+
code: orderLine.code,
|
|
102
|
+
quantity: orderLine.quantity,
|
|
103
|
+
measureUnit: orderLine.measureUnit,
|
|
104
|
+
totalAmount: orderLine.totalAmount,
|
|
105
|
+
tax: orderLine.tax
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private createFullConsecutive(dto: CreateDocumentInput['document'], docType: DocumentType): FullConsecutive {
|
|
111
|
+
return FullConsecutive.create({
|
|
112
|
+
consecutiveIdentifier: dto.consecutiveIdentifier,
|
|
113
|
+
branch: dto.branch,
|
|
114
|
+
terminal: dto.terminal,
|
|
115
|
+
documentType: docType.value
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private createClave(dto: CreateDocumentInput['document'], docType: DocumentType): Clave {
|
|
120
|
+
return Clave.create({
|
|
121
|
+
branch: dto.branch,
|
|
122
|
+
ceSituation: dto.ceSituation,
|
|
123
|
+
consecutiveIdentifier: dto.consecutiveIdentifier,
|
|
124
|
+
countryCode: dto.countryCode,
|
|
125
|
+
docKeyType: docType.value,
|
|
126
|
+
emitterIdentifier: dto.emitter.identifier.id,
|
|
127
|
+
identifierType: dto.emitter.identifier.type, // TODO add default
|
|
128
|
+
securityCode: dto.securityCode,
|
|
129
|
+
terminal: dto.terminal
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private encodeXML(xmlStr: string): string {
|
|
134
|
+
const buffer = Buffer.from(xmlStr)
|
|
135
|
+
return buffer.toString('base64')
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PersonProps } from '@src/ATV/core/Person'
|
|
2
|
+
import { DocumentNames } from '@src/ATV/core/documentNames.types';
|
|
3
|
+
import { Method } from 'axios'
|
|
4
|
+
|
|
5
|
+
type PersonInput = PersonProps;
|
|
6
|
+
|
|
7
|
+
type TaxInput = {
|
|
8
|
+
code: string;
|
|
9
|
+
rateCode: string;
|
|
10
|
+
rate: number;
|
|
11
|
+
amount?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type OrderInput = {
|
|
15
|
+
detail: string;
|
|
16
|
+
unitaryPrice: number;
|
|
17
|
+
lineNumber?: string;
|
|
18
|
+
code?: string;
|
|
19
|
+
quantity?: number;
|
|
20
|
+
measureUnit?: string;
|
|
21
|
+
totalAmount?: number;
|
|
22
|
+
subTotal?: number;
|
|
23
|
+
tax?: TaxInput;
|
|
24
|
+
totalOrderLineAmount?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type DocumentTypes = 'FE'
|
|
28
|
+
|
|
29
|
+
type DocumentInput = {
|
|
30
|
+
emitter: PersonInput;
|
|
31
|
+
receiver: PersonInput;
|
|
32
|
+
branch: string; // sucursal
|
|
33
|
+
terminal: string; // terminal
|
|
34
|
+
// documentType: DocumentTypes; // @deprecated
|
|
35
|
+
documentName: DocumentNames;
|
|
36
|
+
countryCode: string; // codigoPais
|
|
37
|
+
securityCode: string; // codigoSeguridad
|
|
38
|
+
activityCode: string;
|
|
39
|
+
consecutiveIdentifier: string; // consecutivo
|
|
40
|
+
ceSituation: string; // situacionCE
|
|
41
|
+
orderLines: OrderInput[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type CreateDocumentInput = {
|
|
45
|
+
document: DocumentInput;
|
|
46
|
+
token: string;
|
|
47
|
+
signatureOptions: {
|
|
48
|
+
buffer: string;
|
|
49
|
+
password: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type Command = {
|
|
54
|
+
url: string;
|
|
55
|
+
method: Method;
|
|
56
|
+
data: {
|
|
57
|
+
clave: string;
|
|
58
|
+
fecha: string;
|
|
59
|
+
emisor: {
|
|
60
|
+
tipoIdentificacion: string;
|
|
61
|
+
numeroIdentificacion: string;
|
|
62
|
+
};
|
|
63
|
+
receptor: {
|
|
64
|
+
tipoIdentificacion: string;
|
|
65
|
+
numeroIdentificacion: string;
|
|
66
|
+
};
|
|
67
|
+
comprobanteXml: string;
|
|
68
|
+
};
|
|
69
|
+
headers: {
|
|
70
|
+
Authorization: string;
|
|
71
|
+
'Content-Type': string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export type CreateAndSendDocumentResponse = {
|
|
75
|
+
command: Command;
|
|
76
|
+
extraData: {
|
|
77
|
+
xml: string;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -39,7 +39,7 @@ export function getBillResum(frontEndRequest: ClientPayload): Resumen {
|
|
|
39
39
|
},
|
|
40
40
|
TotalServGravados: 0,
|
|
41
41
|
TotalServExentos: 0,
|
|
42
|
-
TotalServExonerado: 0,
|
|
42
|
+
// TotalServExonerado: 0,
|
|
43
43
|
TotalMercanciasGravadas: frontEndRequest.total,
|
|
44
44
|
TotalMercanciasExentas: 0,
|
|
45
45
|
TotalGravado: frontEndRequest.total,
|
package/src/lib/genJSON/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ function getBillResum(lines: LineaDetalle[]): Resumen {
|
|
|
30
30
|
},
|
|
31
31
|
TotalServGravados: 0,
|
|
32
32
|
TotalServExentos: 0,
|
|
33
|
-
TotalServExonerado: 0,
|
|
33
|
+
// TotalServExonerado: 0,
|
|
34
34
|
TotalMercanciasGravadas: sum.total,
|
|
35
35
|
TotalMercanciasExentas: 0,
|
|
36
36
|
TotalGravado: sum.total,
|
|
@@ -70,7 +70,7 @@ function setTaxObj(subtotal: number, taxObj: Impuesto): Impuesto {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function setLinesDefaults(lines: LineaDetalle[]): LineaDetalle[] {
|
|
73
|
+
export function setLinesDefaults(lines: LineaDetalle[]): LineaDetalle[] {
|
|
74
74
|
return lines.map((line, index) => {
|
|
75
75
|
const quantity = line.Cantidad || 1
|
|
76
76
|
const total = line.PrecioUnitario * quantity
|
|
@@ -57,8 +57,8 @@ const MR_XML_ATTRS = { // Mensaje Receptor
|
|
|
57
57
|
export const xmlExtructures = {
|
|
58
58
|
FacturaElectronica: FE_XML_ATTRS,
|
|
59
59
|
FacturaElectronicaExportacion: FEE_XML_ATTRS,
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
NotaCreditoElectronica: NC_XML_ATTRS,
|
|
61
|
+
NotaDebitoElectronica: ND_XML_ATTRS,
|
|
62
62
|
CCE: MR_XML_ATTRS,
|
|
63
63
|
CPCE: MR_XML_ATTRS,
|
|
64
64
|
RCE: MR_XML_ATTRS
|
|
@@ -7,7 +7,7 @@ const MAIN_DOMAIN = 'https://idp.comprobanteselectronicos.go.cr'
|
|
|
7
7
|
const options: { [key: string]: GetTokenInternalProps} = {
|
|
8
8
|
prod: {
|
|
9
9
|
serviceUrl: `${MAIN_DOMAIN}/auth/realms/rut/protocol/openid-connect/token`,
|
|
10
|
-
clientId: 'api'
|
|
10
|
+
clientId: 'api-prod'
|
|
11
11
|
},
|
|
12
12
|
stg: {
|
|
13
13
|
serviceUrl: `${MAIN_DOMAIN}/auth/realms/rut-stag/protocol/openid-connect/token`,
|
|
@@ -35,7 +35,7 @@ export interface Resumen {
|
|
|
35
35
|
};
|
|
36
36
|
TotalServGravados: number;
|
|
37
37
|
TotalServExentos: number;
|
|
38
|
-
TotalServExonerado: number;
|
|
38
|
+
// TotalServExonerado: number;
|
|
39
39
|
TotalMercanciasGravadas?: number;
|
|
40
40
|
TotalMercanciasExentas?: number;
|
|
41
41
|
TotalGravado?: number;
|
|
@@ -78,11 +78,11 @@ export interface Message {
|
|
|
78
78
|
DetalleMensaje: string;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export interface
|
|
81
|
+
export interface Document {
|
|
82
82
|
Clave: string;
|
|
83
83
|
CodigoActividad: string;
|
|
84
84
|
NumeroConsecutivo: string;
|
|
85
|
-
FechaEmision?:
|
|
85
|
+
FechaEmision?: string;
|
|
86
86
|
Emisor: Persona;
|
|
87
87
|
Receptor: Persona;
|
|
88
88
|
CondicionVenta?: string;
|
|
@@ -95,23 +95,30 @@ export interface FacturaElectronica {
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
export interface
|
|
99
|
-
|
|
100
|
-
CodigoActividad: string;
|
|
101
|
-
NumeroConsecutivo: string;
|
|
102
|
-
FechaEmision?: Date;
|
|
103
|
-
Emisor: Persona;
|
|
104
|
-
Receptor: Persona;
|
|
105
|
-
CondicionVenta?: string;
|
|
106
|
-
PlazoCredito?: string;
|
|
107
|
-
MedioPago?: string;
|
|
108
|
-
DetalleServicio?: DetalleServicio;
|
|
109
|
-
ResumenFactura: Resumen;
|
|
110
|
-
Otros?: {
|
|
111
|
-
OtroTexto: string;
|
|
112
|
-
};
|
|
98
|
+
export interface FacturaElectronicaContenedor {
|
|
99
|
+
[key: 'FacturaElectronica' | 'FacturaElectronicaExportacion']: Document | FacturaElectronicaExportacion;
|
|
113
100
|
}
|
|
114
101
|
|
|
115
|
-
export
|
|
116
|
-
|
|
102
|
+
export type ConfirmationMessageRaw = {
|
|
103
|
+
MensajeHacienda: {
|
|
104
|
+
Clave: string;
|
|
105
|
+
NombreEmisor: string;
|
|
106
|
+
TipoIdentificacionEmisor: string;
|
|
107
|
+
NumeroCedulaEmisor: string;
|
|
108
|
+
TipoIdentificacionReceptor: string;
|
|
109
|
+
NumeroCedulaReceptor: string;
|
|
110
|
+
Mensaje: string;
|
|
111
|
+
DetalleMensaje: string;
|
|
112
|
+
MontoTotalImpuesto: string;
|
|
113
|
+
TotalFactura: string;
|
|
114
|
+
'@_xmlns': string;
|
|
115
|
+
'ds:Signature': {
|
|
116
|
+
'@_xmlns:ds': string;
|
|
117
|
+
'@_Id': string;
|
|
118
|
+
'ds:SignedInfo': unknown;
|
|
119
|
+
'ds:SignatureValue': unknown;
|
|
120
|
+
'ds:KeyInfo': unknown;
|
|
121
|
+
'ds:Object': unknown;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
117
124
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { tokenStub } from '@test/stubs/token.stub'
|
|
2
|
-
import requestStub from '@test/stubs/frontendRequest.stub'
|
|
3
|
-
import eletronicBill from '@src/electronicBill'
|
|
4
|
-
import { send } from '@src/services/send/index'
|
|
5
|
-
import fs from 'fs'
|
|
6
|
-
import path from 'path'
|
|
7
|
-
const SOURCE_P12_URI = path.join(__dirname, '../stubs/dummyKeys/client-identity.p12')
|
|
8
|
-
|
|
9
|
-
jest.mock('@src/services/send/index')
|
|
10
|
-
|
|
11
|
-
const mockedSend = send as jest.MockedFunction<typeof send>
|
|
12
|
-
const pem = fs.readFileSync(SOURCE_P12_URI, 'binary')
|
|
13
|
-
|
|
14
|
-
describe('Get Token', () => {
|
|
15
|
-
it('should return a valid token', async () => {
|
|
16
|
-
mockedSend.mockImplementation(() => {
|
|
17
|
-
return Promise.resolve('')
|
|
18
|
-
})
|
|
19
|
-
const bill = await eletronicBill(tokenStub, requestStub, {
|
|
20
|
-
buffer: pem,
|
|
21
|
-
password: '1234'
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
console.log('bill', bill)
|
|
25
|
-
|
|
26
|
-
expect(bill).toEqual('')
|
|
27
|
-
})
|
|
28
|
-
})
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const impuestoStub = {
|
|
4
|
-
Codigo: '01',
|
|
5
|
-
CodigoTarifa: '08',
|
|
6
|
-
Tarifa: 13
|
|
7
|
-
};
|
|
8
|
-
const lineaDetalleStub = {
|
|
9
|
-
Codigo: '8311100000000',
|
|
10
|
-
Cantidad: 1,
|
|
11
|
-
UnidadMedida: 'Unid',
|
|
12
|
-
Detalle: 'detalle',
|
|
13
|
-
PrecioUnitario: 10,
|
|
14
|
-
Impuesto: impuestoStub
|
|
15
|
-
};
|
|
16
|
-
const transmitterStub = {
|
|
17
|
-
Nombre: 'SRL',
|
|
18
|
-
NombreComercial: 'CIENCIA DEL SABOR',
|
|
19
|
-
Identificacion: {
|
|
20
|
-
Tipo: '02',
|
|
21
|
-
Numero: '3102759157'
|
|
22
|
-
},
|
|
23
|
-
Ubicacion: {
|
|
24
|
-
Provincia: '2',
|
|
25
|
-
Canton: '06',
|
|
26
|
-
Distrito: '04',
|
|
27
|
-
Barrio: '06',
|
|
28
|
-
OtrasSenas: '25 norte 500 oeste restaurante El Faro'
|
|
29
|
-
},
|
|
30
|
-
Telefono: {
|
|
31
|
-
CodigoPais: '506',
|
|
32
|
-
NumTelefono: '80808080'
|
|
33
|
-
},
|
|
34
|
-
CorreoElectronico: 'cienciadelsabor@gmail.com',
|
|
35
|
-
Fax: {
|
|
36
|
-
CodigoPais: '506',
|
|
37
|
-
NumTelefono: '80808080'
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const receiverStub = {
|
|
41
|
-
Nombre: 'Nombre Receptor',
|
|
42
|
-
Identificacion: {
|
|
43
|
-
Numero: '206920142'
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
const requestStub = {
|
|
47
|
-
Emisor: transmitterStub,
|
|
48
|
-
Receptor: receiverStub,
|
|
49
|
-
sucursal: '001',
|
|
50
|
-
terminal: '00001',
|
|
51
|
-
tipoDocumento: 'FE',
|
|
52
|
-
codigoPais: '506',
|
|
53
|
-
consecutivo: '18',
|
|
54
|
-
codigoSeguridad: '00000001',
|
|
55
|
-
situationEC: '1',
|
|
56
|
-
actividad: '4',
|
|
57
|
-
LineasDetalle: [lineaDetalleStub],
|
|
58
|
-
facturaElectronicaType: 'FacturaElectronicaExportacion'
|
|
59
|
-
};
|
|
60
|
-
exports.default = requestStub;
|
|
61
|
-
//# sourceMappingURL=frontendRequest.stub.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"frontendRequest.stub.js","sourceRoot":"","sources":["../../../__tests__/stubs/frontendRequest.stub.ts"],"names":[],"mappings":";;AAGA,MAAM,YAAY,GAAa;IAC7B,MAAM,EAAE,IAAI;IACZ,YAAY,EAAE,IAAI;IAClB,MAAM,EAAE,EAAE;CACX,CAAA;AAED,MAAM,gBAAgB,GAAiB;IACrC,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC;IACX,YAAY,EAAE,MAAM;IACpB,OAAO,EAAE,SAAS;IAClB,cAAc,EAAE,EAAE;IAClB,QAAQ,EAAE,YAAY;CACvB,CAAA;AAED,MAAM,eAAe,GAAY;IAC/B,MAAM,EAAE,KAAK;IACb,eAAe,EAAE,mBAAmB;IACpC,cAAc,EAAE;QACd,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,YAAY;KACrB;IACD,SAAS,EAAE;QACT,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,wCAAwC;KACrD;IACD,QAAQ,EAAE;QACR,UAAU,EAAE,KAAK;QACjB,WAAW,EAAE,UAAU;KACxB;IACD,iBAAiB,EAAE,2BAA2B;IAC9C,GAAG,EAAE;QACH,UAAU,EAAE,KAAK;QACjB,WAAW,EAAE,UAAU;KACxB;CACF,CAAA;AAED,MAAM,YAAY,GAAY;IAC5B,MAAM,EAAE,iBAAiB;IACzB,cAAc,EAAE;QACd,MAAM,EAAE,WAAW;KACpB;CACF,CAAA;AAED,MAAM,WAAW,GAAkB;IACjC,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,OAAO;IACjB,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,KAAK;IACjB,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,UAAU;IAC3B,WAAW,EAAE,GAAG;IAChB,SAAS,EAAE,GAAG;IACd,aAAa,EAAE,CAAC,gBAAgB,CAAC;IACjC,sBAAsB,EAAE,+BAA+B;CACxD,CAAA;AAED,kBAAe,WAAW,CAAA"}
|