@facturacr/atv-sdk 0.0.26-beta → 1.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.
Files changed (67) hide show
  1. package/.github/workflows/ci.yml +2 -2
  2. package/.github/workflows/publish.yml +25 -0
  3. package/.github/workflows/release-please.yml +17 -0
  4. package/CHANGELOG.md +37 -0
  5. package/__tests__/stubs/commonExpectedXml.xml +13 -13
  6. package/__tests__/stubs/createDocument.data.ts +3 -3
  7. package/__tests__/tests/ATV/__snapshots__/index.test.ts.snap +1 -1
  8. package/__tests__/tests/ATV/index.test.ts +5 -5
  9. package/dist/src/ATV/core/CreateDocFactory.d.ts +2 -0
  10. package/dist/src/ATV/core/CreateDocFactory.js +1 -1
  11. package/dist/src/ATV/core/CreateDocFactory.js.map +1 -1
  12. package/dist/src/ATV/core/Person.d.ts +1 -1
  13. package/dist/src/ATV/core/ReceptorConsecutive.d.ts +13 -0
  14. package/dist/src/ATV/core/ReceptorConsecutive.js +21 -0
  15. package/dist/src/ATV/core/ReceptorConsecutive.js.map +1 -0
  16. package/dist/src/ATV/core/documentNames.types.d.ts +1 -1
  17. package/dist/src/ATV/core/types.d.ts +20 -0
  18. package/dist/src/ATV/core/types.js +10 -0
  19. package/dist/src/ATV/core/types.js.map +1 -0
  20. package/dist/src/ATV/index.d.ts +8 -0
  21. package/dist/src/ATV/index.js +5 -0
  22. package/dist/src/ATV/index.js.map +1 -1
  23. package/dist/src/ATV/mappers/billDocToAtv.d.ts +2 -0
  24. package/dist/src/ATV/mappers/billDocToAtv.js +23 -2
  25. package/dist/src/ATV/mappers/billDocToAtv.js.map +1 -1
  26. package/dist/src/ATV/useCases/createDocument/index.js.map +1 -1
  27. package/dist/src/ATV/useCases/createDocument/types.d.ts +16 -13
  28. package/dist/src/ATV/useCases/createReceptorMessage/index.d.ts +31 -0
  29. package/dist/src/ATV/useCases/createReceptorMessage/index.js +115 -0
  30. package/dist/src/ATV/useCases/createReceptorMessage/index.js.map +1 -0
  31. package/dist/src/confirmXML.js +1 -1
  32. package/dist/src/confirmXML.js.map +1 -1
  33. package/dist/src/electronicBill.js.map +1 -1
  34. package/dist/src/helpers/comprobantes.js.map +1 -1
  35. package/dist/src/index.d.ts +1 -0
  36. package/dist/src/index.js +3 -1
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/lib/genJSON/confirmXML.js +3 -3
  39. package/dist/src/lib/genJSON/confirmXML.js.map +1 -1
  40. package/dist/src/lib/genJSON/index.js.map +1 -1
  41. package/dist/src/lib/genXML/index.d.ts +2 -1
  42. package/dist/src/lib/genXML/index.js +3 -3
  43. package/dist/src/lib/genXML/index.js.map +1 -1
  44. package/dist/src/types/facturaInterfaces.d.ts +19 -2
  45. package/examples/README.md +10 -5
  46. package/examples/atvAccept.ts +76 -0
  47. package/examples/createAndSend.ts +6 -3
  48. package/package.json +1 -1
  49. package/src/ATV/core/CreateDocFactory.ts +3 -1
  50. package/src/ATV/core/Person.ts +1 -1
  51. package/src/ATV/core/ReceptorConsecutive.ts +27 -0
  52. package/src/ATV/core/documentNames.types.ts +2 -1
  53. package/src/ATV/core/types.ts +22 -0
  54. package/src/ATV/index.ts +6 -0
  55. package/src/ATV/mappers/billDocToAtv.ts +23 -1
  56. package/src/ATV/useCases/createDocument/index.ts +0 -2
  57. package/src/ATV/useCases/createDocument/types.ts +16 -13
  58. package/src/ATV/useCases/createReceptorMessage/index.ts +121 -0
  59. package/src/confirmXML.ts +2 -2
  60. package/src/electronicBill.ts +2 -1
  61. package/src/helpers/comprobantes.ts +1 -1
  62. package/src/index.ts +1 -1
  63. package/src/lib/genJSON/confirmXML.ts +5 -5
  64. package/src/lib/genJSON/index.ts +1 -1
  65. package/src/lib/genXML/index.ts +2 -1
  66. package/src/types/facturaInterfaces.ts +21 -2
  67. package/examples/confirmXML.ts +0 -76
@@ -5,22 +5,25 @@ import { Method } from 'axios'
5
5
 
6
6
  export type DocumentTypes = 'FE'
7
7
 
8
+ type CommandData = {
9
+ clave: string;
10
+ fecha: string;
11
+ emisor: {
12
+ tipoIdentificacion: string;
13
+ numeroIdentificacion: string;
14
+ };
15
+ receptor: {
16
+ tipoIdentificacion: string;
17
+ numeroIdentificacion: string;
18
+ };
19
+ comprobanteXml: string;
20
+ consecutivoReceptor?: string;
21
+ };
22
+
8
23
  export type Command = {
9
24
  url: string;
10
25
  method: Method;
11
- data: {
12
- clave: string;
13
- fecha: string;
14
- emisor: {
15
- tipoIdentificacion: string;
16
- numeroIdentificacion: string;
17
- };
18
- receptor: {
19
- tipoIdentificacion: string;
20
- numeroIdentificacion: string;
21
- };
22
- comprobanteXml: string;
23
- };
26
+ data: CommandData;
24
27
  headers: {
25
28
  Authorization: string;
26
29
  'Content-Type': string;
@@ -0,0 +1,121 @@
1
+ import { AceptationStates, ReceptorMessageProps } from "@src/ATV/core/types";
2
+ import { mapReceptorMessageToAtvFormat } from "@src/ATV/mappers/billDocToAtv";
3
+ import { genXML, parseElectronicBillXML } from "@src/lib/genXML";
4
+ import { Command } from "../createDocument/types";
5
+ import { ATV } from "@src/ATV";
6
+ import { ReceptorConsecutive } from "@src/ATV/core/ReceptorConsecutive";
7
+
8
+ export type CreateReceptorMessageCommandInput = {
9
+ receivedDocument: string;
10
+ aceptationState: AceptationStates;
11
+ aceptationDetailMessage: string;
12
+ branch: string;
13
+ terminal: string;
14
+ token: string;
15
+ consecutive: string;
16
+ signatureOptions: {
17
+ buffer: string;
18
+ password: string;
19
+ };
20
+ }
21
+
22
+ const options: { [key: string]: { serviceUrl: string}} = {
23
+ prod: {
24
+ serviceUrl: 'https://api.comprobanteselectronicos.go.cr/v1/recepcion'
25
+ },
26
+ stg: {
27
+ serviceUrl: 'https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/recepcion'
28
+ }
29
+ }
30
+
31
+ export class CreateReceptorMessageCommand {
32
+ private readonly serviceUrl: string
33
+
34
+ constructor(scope: ATV) {
35
+ this.serviceUrl = options[scope.mode].serviceUrl
36
+ }
37
+
38
+ public async execute(input: CreateReceptorMessageCommandInput) {
39
+ const receptorMessageProps = this.processDocument(input)
40
+ const atvDocument = mapReceptorMessageToAtvFormat(receptorMessageProps)
41
+ const xml = await genXML('MensajeReceptor', atvDocument, input.signatureOptions)
42
+ const command = await this.createDocumentCommand(receptorMessageProps, xml, input.token)
43
+ return {
44
+ command,
45
+ extraData: {
46
+ xml,
47
+ document: atvDocument
48
+ }
49
+ }
50
+ }
51
+
52
+ private async createDocumentCommand(receptorMessageProps: ReceptorMessageProps, xml: string, token: string): Promise<Command> {
53
+ return {
54
+ url: this.serviceUrl,
55
+ method: 'post',
56
+ data: {
57
+ clave: receptorMessageProps.clave,
58
+ fecha: receptorMessageProps.documentIssueDate.toISOString(),
59
+ emisor: {
60
+ tipoIdentificacion: receptorMessageProps.emitterIdentifierType,
61
+ numeroIdentificacion: receptorMessageProps.emitterIdentifier,
62
+ },
63
+ receptor: {
64
+ tipoIdentificacion: receptorMessageProps.receptorIdentifierType,
65
+ numeroIdentificacion: receptorMessageProps.receptorIdentifier,
66
+ },
67
+ comprobanteXml: this.encodeXML(xml),
68
+ consecutivoReceptor: receptorMessageProps.receptorConcecutive
69
+ },
70
+ headers: {
71
+ Authorization: 'bearer ' + token,
72
+ 'Content-Type': 'application/json'
73
+ }
74
+ }
75
+ }
76
+
77
+ private encodeXML(xmlStr: string): string {
78
+ const buffer = Buffer.from(xmlStr)
79
+ return buffer.toString('base64')
80
+ }
81
+
82
+ private acceptationStateToDocumentType(aceptationState: AceptationStates) {
83
+ switch (aceptationState) {
84
+ case AceptationStates.ACCEPTED:
85
+ return '05'
86
+ case AceptationStates.PARTIALLY_ACCEPTED:
87
+ return '06'
88
+ default:
89
+ return '07'
90
+ }
91
+ }
92
+
93
+ private processDocument(input: CreateReceptorMessageCommandInput): ReceptorMessageProps {
94
+ try {
95
+ const electronillBillRaw = parseElectronicBillXML(input.receivedDocument)
96
+ const receptorConsecutive = ReceptorConsecutive.create({
97
+ branch: input.branch,
98
+ terminal: input.terminal,
99
+ documentType: this.acceptationStateToDocumentType(input.aceptationState),
100
+ consecutive: input.consecutive,
101
+ })
102
+ return {
103
+ clave: electronillBillRaw.Clave,
104
+ emitterIdentifier: electronillBillRaw.Emisor.Identificacion.Numero,
105
+ emitterIdentifierType: electronillBillRaw.Emisor.Identificacion.Tipo,
106
+ receptorIdentifier: electronillBillRaw.Receptor.Identificacion.Numero,
107
+ receptorIdentifierType: electronillBillRaw.Receptor.Identificacion.Tipo,
108
+ documentIssueDate: new Date(electronillBillRaw.FechaEmision),
109
+ activityCode: electronillBillRaw.CodigoActividad,
110
+ taxCondition: electronillBillRaw.CondicionVenta,
111
+ totalTaxes: electronillBillRaw.ResumenFactura.TotalImpuesto,
112
+ totalSale: electronillBillRaw.ResumenFactura.TotalVenta,
113
+ aceptationState: input.aceptationState,
114
+ aceptationDetailMessage: input.aceptationDetailMessage,
115
+ receptorConcecutive: receptorConsecutive.value
116
+ }
117
+ } catch (err) {
118
+ throw new Error('Error parsing the document')
119
+ }
120
+ }
121
+ }
package/src/confirmXML.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { consecutivoStr } from '@src/lib/genClave'
2
- import { xmlToJson, genXML } from '@src/lib/genXML/index'
2
+ import { parseElectronicBillXML, genXML } from '@src/lib/genXML/index'
3
3
  import { send } from '@src/services/send/index'
4
4
  import { getFinalMessage, getMsjObj } from '@src/lib/genJSON/confirmXML'
5
5
 
@@ -21,7 +21,7 @@ export default async (opts: {
21
21
  }): Promise<any> => {
22
22
  const { token, pemOpt, tipoDocKey } = opts
23
23
  const consecutivo = getConsecutivoStr(opts)
24
- const fullInvoice = xmlToJson(opts.xmlStr)
24
+ const fullInvoice = parseElectronicBillXML(opts.xmlStr)
25
25
  const msjObj = getMsjObj({ fullInvoice, consecutivo, tipoDocKey })
26
26
  const xmlBase64 = await genXML(tipoDocKey, msjObj, {
27
27
  buffer: pemOpt.buffer,
@@ -2,9 +2,10 @@ import { ClientPayload, FinalMessagePerson } from '@src/types/globalInterfaces'
2
2
  import { genClaveObj, genString, parseOptions } from '@src/lib/genClave/index'
3
3
  import genJSON from '@src/lib/genJSON/index'
4
4
  import { send } from '@src/services/send/index'
5
+ import { Persona } from './types/facturaInterfaces'
5
6
 
6
7
  const DEFAULT_VALUES = {
7
- tipoIdentificacion: '01'
8
+ tipoIdentificacion: '01' as Persona['Identificacion']['Tipo']
8
9
  }
9
10
 
10
11
  const encodeXML = (xmlStr: string): string => {
@@ -6,7 +6,7 @@ const DEFAULT_VALUES = {
6
6
  message: 'Default msj',
7
7
  detailsMessage: 'Default details msj',
8
8
  taxes: 100,
9
- tipoIdentificacion: '01'
9
+ tipoIdentificacion: '01' as Persona['Identificacion']['Tipo']
10
10
  }
11
11
 
12
12
  export function getSimpleSender(frontEndRequest: ClientPayload): FinalMessagePerson {
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
-
1
+ export { parseElectronicBillXML as xmlToJson } from './lib/genXML'
2
2
  export { Document, InvoiceDocumentContainer, DetalleServicio, Resumen, Persona } from './types/facturaInterfaces'
3
3
  export { CreateDocumentInput } from './ATV/core/CreateDocFactory'
4
4
  export { ATV } from './ATV'
@@ -31,17 +31,17 @@ export function getMsjObj(opts: {
31
31
  export function getFinalMessage(fullInvoice: any, consecutivoReceptor: string, xmlBase64: any): any {
32
32
  const date = new Date()
33
33
  return {
34
- clave: fullInvoice.Clave,
35
- fecha: date.toISOString(),
36
- emisor: {
34
+ clave: fullInvoice.Clave, // in command
35
+ fecha: date.toISOString(), // in command
36
+ emisor: { // in command
37
37
  tipoIdentificacion: fullInvoice.Receptor.Identificacion.Tipo,
38
38
  numeroIdentificacion: fullInvoice.Receptor.Identificacion.Numero
39
39
  },
40
- receptor: {
40
+ receptor: { // in command
41
41
  tipoIdentificacion: fullInvoice.Emisor.Identificacion.Tipo,
42
42
  numeroIdentificacion: fullInvoice.Emisor.Identificacion.Numero
43
43
  },
44
44
  consecutivoReceptor,
45
- comprobanteXml: xmlBase64
45
+ comprobanteXml: xmlBase64 // in command
46
46
  }
47
47
  }
@@ -7,7 +7,7 @@ const DEFAULT_VALUES = {
7
7
  key: 0,
8
8
  message: 'Default msj',
9
9
  detailsMessage: 'Default details msj',
10
- tipoIdentificacion: '01'
10
+ tipoIdentificacion: '01' as Persona['Identificacion']['Tipo']
11
11
  }
12
12
 
13
13
  function sumLines(lines: LineaDetalle[]): any {
@@ -1,6 +1,7 @@
1
1
  import { j2xParser, parse } from 'fast-xml-parser'
2
2
  import { declaration, defaultOptions, xmlExtructures } from '@src/lib/genXML/xmlConfig'
3
3
  import sigXML from '@src/lib/genXML/sigXML/index'
4
+ import { XMLRawDocument } from '@src/types/facturaInterfaces'
4
5
 
5
6
  export const objToXML = (xmlStructure: string, obj: object): string => {
6
7
  const parser = new j2xParser(defaultOptions) // eslint-disable-line new-cap
@@ -21,7 +22,7 @@ export async function genXML(xmlStructure: string, obj: object, options?: {
21
22
  return signedXML
22
23
  }
23
24
 
24
- export const xmlToJson = (xml: string): any => {
25
+ export const parseElectronicBillXML = (xml: string): XMLRawDocument => {
25
26
  try {
26
27
  const json = parse(xml, {
27
28
  ignoreAttributes: false,
@@ -51,7 +51,7 @@ export interface Resumen {
51
51
  export interface Persona {
52
52
  Nombre: string;
53
53
  Identificacion: {
54
- Tipo?: string;
54
+ Tipo?: '01' | '02';
55
55
  Numero: string;
56
56
  };
57
57
  NombreComercial?: string;
@@ -104,8 +104,27 @@ export interface Document {
104
104
  InformacionReferencia?: InformacionReferencia;
105
105
  }
106
106
 
107
+ export interface XMLRawDocument extends Document {
108
+
109
+ }
110
+
111
+ interface MensajeReceptor {
112
+ Clave: string;
113
+ NumeroCedulaEmisor: string;
114
+ FechaEmisionDoc: string;
115
+ Mensaje: string;
116
+ DetalleMensaje: string;
117
+ MontoTotalImpuesto: number;
118
+ CodigoActividad: string;
119
+ CondicionImpuesto: string;
120
+ MontoTotalDeGastoAplicable: number;
121
+ TotalFactura: number;
122
+ NumeroCedulaReceptor: string;
123
+ NumeroConsecutivoReceptor: string;
124
+ }
125
+
107
126
  export interface InvoiceDocumentContainer {
108
- [key: string]: Document;
127
+ [key: string]: Document | MensajeReceptor;
109
128
  }
110
129
 
111
130
  export type ConfirmationMessageRaw = {
@@ -1,76 +0,0 @@
1
- import confirmXML from '@src/confirmXML'
2
- import { sendToCustomURL } from '@src/services/send/index'
3
- import getToken from '@src/services/getToken'
4
- import fs from 'fs'
5
-
6
- const USERNAME_TEST = process.env.USERNAME_TEST
7
- const PASSWORD_TEST = process.env.PASSWORD_TEST
8
-
9
- const SOURCE_P12_URI = process.env.SOURCE_P12_URI
10
- const SOURCE_P12_PASSPORT = process.env.SOURCE_P12_PASSPORT
11
- const XML_TO_CONFIRM = process.env.XML_TO_CONFIRM
12
-
13
- function decodeBase64(encodedStr: string): string {
14
- const buff = Buffer.from(encodedStr, 'base64')
15
- return buff.toString('ascii')
16
- }
17
-
18
- function getConfimation(token: string, data: any, ms: number): Promise<any> {
19
- return new Promise((resolve, reject): any => {
20
- setTimeout(() => {
21
- const location = data.headers.location
22
- console.log('location', location)
23
- sendToCustomURL(token, location)
24
- .then(data => resolve(data))
25
- .catch(err => reject(err))
26
- }, ms)
27
- })
28
- }
29
-
30
- async function main(): Promise<void> {
31
- try {
32
- const pem = fs.readFileSync(SOURCE_P12_URI, 'binary')
33
- const xml = fs.readFileSync(XML_TO_CONFIRM, 'utf-8')
34
- const token = await getToken({
35
- client_id: 'api-stag', // eslint-disable-line
36
- client_secret: '', // eslint-disable-line
37
- grant_type: 'password', // eslint-disable-line
38
- username: USERNAME_TEST,
39
- password: PASSWORD_TEST
40
- })
41
- const consecutivo = {
42
- consecutivo: '0000000013'
43
- }
44
- const data = await confirmXML({
45
- token: token.data.access_token,
46
- consecutivo,
47
- xmlStr: xml,
48
- tipoDocKey: 'CCE',
49
- pemOpt: {
50
- buffer: pem,
51
- password: SOURCE_P12_PASSPORT
52
- }
53
- })
54
- if (data) {
55
- const secondResponse = await getConfimation(token.data.access_token, data, 10000)
56
- .catch(err => {
57
- const response = err.response || {}
58
- console.log('response', response)
59
- })
60
- const XMLResponse = secondResponse.data['respuesta-xml']
61
- if (!XMLResponse) {
62
- const state = secondResponse.data['ind-estado']
63
- console.log('state', state)
64
- return
65
- }
66
- const text = decodeBase64(XMLResponse)
67
- console.log('secondResponse', text)
68
- } else {
69
- console.log('no data', data)
70
- }
71
- } catch (error) {
72
- console.warn(error.code)
73
- }
74
- }
75
-
76
- main()