zatca-phase2
Version:
ZATCA Phase 2 e-invoicing integration for Node.js
98 lines (84 loc) • 3.06 kB
JavaScript
/**
* Credit Note Module
* @module zatca-phase2/invoice/credit-note
* @private
*/
const { v4: uuidv4 } = require('uuid');
const { ZatcaError, ErrorCodes } = require('../errors');
const logger = require('../utils/logger');
const xml = require('../xml');
const signing = require('../signing');
const api = require('../api');
/**
* Create a credit note for an invoice
* @function
* @param {Object} originalInvoice - Original invoice
* @param {string} reason - Reason for credit note
* @param {Object} certInfo - Certificate information
* @returns {Promise<Object>} Credit note and ZATCA response
*/
exports.createCreditNote = async function(originalInvoice, reason, certInfo) {
try {
logger.info('Creating credit note for invoice', {
originalInvoiceNumber: originalInvoice.invoiceNumber
});
// Create credit note object
const creditNote = {
uuid: uuidv4(),
invoiceNumber: `CN-${originalInvoice.invoiceNumber}`,
issueDate: new Date(),
supplyDate: new Date(),
supplierName: originalInvoice.supplierName,
supplierTaxNumber: originalInvoice.supplierTaxNumber,
customerName: originalInvoice.customerName,
customerTaxNumber: originalInvoice.customerTaxNumber,
totalAmount: -Math.abs(originalInvoice.totalAmount),
vatAmount: -Math.abs(originalInvoice.vatAmount),
items: originalInvoice.items.map(item => ({
...item,
quantity: -Math.abs(item.quantity),
totalAmount: -Math.abs(item.totalAmount),
taxAmount: -Math.abs(item.taxAmount)
}))
};
logger.debug('Credit note object created', {
creditNoteNumber: creditNote.invoiceNumber
});
// Generate XML for credit note
const creditNoteXml = xml.generateCreditNoteXml(creditNote, originalInvoice, reason);
logger.debug('Credit note XML generated');
// Calculate hash
const hash = signing.calculateInvoiceHash(creditNote, creditNoteXml);
logger.debug('Credit note hash calculated', { hash });
// Sign XML
const signedXml = await signing.signInvoice(creditNote, creditNoteXml, certInfo);
logger.debug('Credit note XML signed');
// Store signed XML in credit note object
creditNote.signedXml = signedXml;
// Submit to ZATCA (always reported, not cleared)
const response = await api.reportInvoice(creditNote, signedXml, certInfo);
// Update credit note status
creditNote.zatcaStatus = 'submitted';
creditNote.zatcaResponse = response;
logger.info('Credit note submitted successfully', {
creditNoteNumber: creditNote.invoiceNumber,
requestId: response.requestID
});
return {
creditNote,
response
};
} catch (error) {
logger.error('Failed to create credit note', {
originalInvoiceNumber: originalInvoice.invoiceNumber,
error: error.message
});
if (error.name === 'ZatcaError') {
throw error;
}
throw new ZatcaError(
`Failed to create credit note: ${error.message}`,
ErrorCodes.UNKNOWN_ERROR
);
}
};