@facturacr/atv-sdk
Version:
Librería (SDK) de Javascript/NodeJS para acceder al API de Administración Tributaria Virtual (ATV) del Ministerio de Hacienda.
129 lines (120 loc) • 4.7 kB
text/typescript
import { AceptationStates, ReceptorMessageProps } from "@src/ATV/core/types";
import { mapReceptorMessageToAtvFormat } from "@src/ATV/mappers/billDocToAtv";
import { genXML, parseElectronicBillXML } from "@src/lib/genXML";
import { Command } from "../createDocument/types";
import { ATV } from "@src/ATV";
import { ReceptorConsecutive } from "@src/ATV/core/ReceptorConsecutive";
export type CreateReceptorMessageCommandInput = {
clave: string;
emitterIdentifier: string;
emitterIdentifierType: string;
receptorIdentifier: string;
receptorIdentifierType: string;
documentIssueDate: Date;
activityCode: string;
taxCondition: string;
totalTaxes: number;
totalSale: number;
aceptationState: AceptationStates;
aceptationDetailMessage: string;
branch: string;
terminal: string;
token: string;
consecutive: string;
signatureOptions: {
buffer: string;
password: string;
};
}
const options: { [key: string]: { serviceUrl: string}} = {
prod: {
serviceUrl: 'https://api.comprobanteselectronicos.go.cr/recepcion/v1/recepcion'
},
stg: {
serviceUrl: 'https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/recepcion'
}
}
export class CreateReceptorMessageCommand {
private readonly serviceUrl: string
constructor(scope: ATV) {
this.serviceUrl = options[scope.mode].serviceUrl
}
public async execute(input: CreateReceptorMessageCommandInput) {
const receptorMessageProps = this.processDocument(input)
const atvDocument = mapReceptorMessageToAtvFormat(receptorMessageProps)
const xml = await genXML('MensajeReceptor', atvDocument, input.signatureOptions)
const command = await this.createDocumentCommand(receptorMessageProps, xml, input.token)
return {
command,
extraData: {
xml,
document: atvDocument
}
}
}
private async createDocumentCommand(receptorMessageProps: ReceptorMessageProps, xml: string, token: string): Promise<Command> {
return {
url: this.serviceUrl,
method: 'post',
data: {
clave: receptorMessageProps.clave,
fecha: receptorMessageProps.documentIssueDate.toISOString(),
emisor: {
tipoIdentificacion: receptorMessageProps.emitterIdentifierType,
numeroIdentificacion: receptorMessageProps.emitterIdentifier,
},
receptor: {
tipoIdentificacion: receptorMessageProps.receptorIdentifierType,
numeroIdentificacion: receptorMessageProps.receptorIdentifier,
},
comprobanteXml: this.encodeXML(xml),
consecutivoReceptor: receptorMessageProps.receptorConcecutive
},
headers: {
Authorization: 'bearer ' + token,
'Content-Type': 'application/json'
}
}
}
private encodeXML(xmlStr: string): string {
const buffer = Buffer.from(xmlStr)
return buffer.toString('base64')
}
private acceptationStateToDocumentType(aceptationState: AceptationStates) {
switch (aceptationState) {
case AceptationStates.ACCEPTED:
return '05'
case AceptationStates.PARTIALLY_ACCEPTED:
return '06'
default:
return '07'
}
}
private processDocument(input: CreateReceptorMessageCommandInput): ReceptorMessageProps {
try {
const receptorConsecutive = ReceptorConsecutive.create({
branch: input.branch,
terminal: input.terminal,
documentType: this.acceptationStateToDocumentType(input.aceptationState),
consecutive: input.consecutive,
})
return {
clave: input.clave,
emitterIdentifier: input.emitterIdentifier,
emitterIdentifierType: input.emitterIdentifierType,
receptorIdentifier: input.receptorIdentifier,
receptorIdentifierType: input.receptorIdentifierType,
documentIssueDate: input.documentIssueDate,
activityCode: input.activityCode,
taxCondition: input.taxCondition,
totalTaxes: input.totalTaxes,
totalSale: input.totalSale,
aceptationState: input.aceptationState,
aceptationDetailMessage: input.aceptationDetailMessage,
receptorConcecutive: receptorConsecutive.value
}
} catch (err) {
throw new Error(`Error parsing the document ${err}`)
}
}
}