/**
* @module receipt
*/
import Item from "./Item.js"
import Receipt from "./Receipt.js"
import Sale from "./Sale.js"
import DocumentReference from "./DocumentReference.js"
import TagsGenerator from "./xml/TagsGenerator.js"
import Taxpayer from "../person/Taxpayer.js"
import Person from "../person/Person.js"
class Note extends Sale {
#description
#responseCode // type for this note
/**
* @deprecated
*/
#documentReference
/**
* @deprecated
*/
#documentReferenceTypeCode
/**
* Used in Credit note to hold just one invoice reference.
* @type {DocumentReference}
*/
#billingDocumentReference
/**
*
* @param {Taxpayer} taxpayer - Taxpayer with all their details ready.
* @param {Person} customer - Generic person.
* @param {boolean} isCredit - Must be boolean to choose between credit as true or false for debit.
*/
constructor(taxpayer, customer, isCredit) {
if (isCredit != undefined) {
super(taxpayer, customer, isCredit ? "CreditNote" : "DebitNote")
this.setTypeCode(isCredit ? 7 : 8)
}
else {
super(taxpayer, customer)
}
}
/**
* Set type again for converting note in credit note or debit note.
* You don't need it when you set it in constructor.
* @param {number} code - Number 7 for credit, number 8 for debit.
* @example <caption>Set as credit note</caption>
* note.setTypeCode(7)
*
* @example <caption>Set as debit note</caption>
* note.setTypeCode(8)
*/
setTypeCode(code) {
if (code == 7 || code == 8) {
super.setTypeCode(code)
this.setName(code == 7 ? "CreditNote" : "DebitNote")
}
}
setDescription(description) {
if (description.length > 250) {
this.#description = description.substring(0, 249)
return
}
this.#description = description
}
getDescription() {
return this.#description
}
setResponseCode(code) {
this.#responseCode = code
}
getResponseCode(withFormat = false) {
if (withFormat) {
return String(this.#responseCode).padStart(2, '0')
}
return this.#responseCode
}
/**
* Add a document reference and when it is billing reference, set it only once.
* @param {DocumentReference} documentReference
*/
addDocumentReference(documentReference) {
if (documentReference.getReferenceType() == DocumentReference.BILLING) {
this.#billingDocumentReference = documentReference
return
}
super.addDocumentReference(documentReference)
}
/**
* @returns {DocumentReference}
*/
get billingDocumentReference() {
return this.#billingDocumentReference
}
/**
* Set identifier of document reference like F000-12345678.
* @deprecated
* @param {string} reference
*/
setDocumentReference(reference) {
this.#documentReference = reference
}
/**
* @deprecated
* @returns {string}
*/
getDocumentReference() {
return this.#documentReference
}
/**
* @deprecated
* @param {number} code
*/
setDocumentReferenceTypeCode(code) {
this.#documentReferenceTypeCode = code
}
/**
* @deprecated
* @param {boolean} withFormat
* @returns {string}
*/
getDocumentReferenceTypeCode(withFormat = false) {
if (withFormat) {
return String(this.#documentReferenceTypeCode).padStart(2, '0')
}
return this.#documentReferenceTypeCode
}
validate(validateNumeration) {
super.validate(validateNumeration)
if (this.#documentReferenceTypeCode == 1) {
// Both must start with F
if (!(this.getSerie().startsWith("F") && this.#documentReference.startsWith("F"))) {
throw new Error("La serie para Nota de Factura debe empezar con F.")
}
} else if (this.#documentReferenceTypeCode == 3) {
// Both must start with B
if (!(this.getSerie().startsWith("B") && this.#documentReference.startsWith("B"))) {
throw new Error("La serie para Nota de Boleta de venta debe empezar con B.");
}
}
}
toXml() {
// All parts of XML in array of strings
const xmlParts = [
TagsGenerator.generateUpperWrapper(this),
TagsGenerator.generateUblExtensions(this),
TagsGenerator.generateHeader(this),
TagsGenerator.generateIdentity(this),
TagsGenerator.generateDates(this),
TagsGenerator.generateNotes(this),
TagsGenerator.generateCurrencyCode(this),
TagsGenerator.generateDiscrepancy(this),
TagsGenerator.generateReference(this),
TagsGenerator.generateDespatchDocumentReferences(this),
TagsGenerator.generateSignature(this),
TagsGenerator.generateSupplier(this),
TagsGenerator.generateCustomer(this),
TagsGenerator.generateTaxes(this),
TagsGenerator.generateTotal(this),
TagsGenerator.generateLines(this),
TagsGenerator.generateLowerWrapper(this)
]
// Together in string inmediately
this.xmlString = xmlParts.join("")
}
/**
* @return xmlDoc parsed
*/
fromXml(xmlContent) {
// All about sale
const xmlDoc = super.fromXml(xmlContent)
// Now about note
const [lineNodeName, quantityNodeName] = this.getTypeCode() == 7 ? ["CreditNoteLine", "CreditedQuantity"] : ["DebitNoteLine", "DebitedQuantity"]
const items = xmlDoc.getElementsByTagNameNS(Receipt.namespaces.cac, lineNodeName);
for (let i = 0; i < items.length; i++) {
const item = new Item(items[i].getElementsByTagNameNS(Receipt.namespaces.cbc, "Description")[0]?.textContent || "")
item.setQuantity(parseFloat(items[i].getElementsByTagNameNS(Receipt.namespaces.cbc, quantityNodeName)[0]?.textContent))
item.setUnitValue(
parseFloat(items[i].getElementsByTagNameNS(Receipt.namespaces.cac, "Price")[0]?.getElementsByTagNameNS(Receipt.namespaces.cbc, "PriceAmount")[0]?.textContent),
false
)
item.setUnitCode(items[i].getElementsByTagNameNS(Receipt.namespaces.cbc, quantityNodeName)[0]?.getAttribute("unitCode") || "")
// Warning because there are many tags with same name
item.setIgvPercentage(parseInt(items[i].getElementsByTagNameNS(Receipt.namespaces.cbc, "Percent")[0]?.textContent))
item.setExemptionReasonCode(parseInt(items[i].getElementsByTagNameNS(Receipt.namespaces.cbc, "TaxExemptionReasonCode")[0]?.textContent))
item.calcMounts();
this.addItem(item, true)
}
{// related document
const billingReference = xmlDoc.getElementsByTagNameNS(Receipt.namespaces.cac, "BillingReference")[0]
this.setDocumentReference(billingReference.getElementsByTagNameNS(Receipt.namespaces.cbc, "ID")[0].textContent)
this.setDocumentReferenceTypeCode(parseInt(billingReference.getElementsByTagNameNS(Receipt.namespaces.cbc, "DocumentTypeCode")[0].textContent))
const discrepancyResponse = xmlDoc.getElementsByTagNameNS(Receipt.namespaces.cac, "DiscrepancyResponse")[0]
this.setResponseCode(parseInt(discrepancyResponse.getElementsByTagNameNS(Receipt.namespaces.cbc, "ResponseCode")[0].textContent))
this.setDescription(discrepancyResponse.getElementsByTagNameNS(Receipt.namespaces.cbc, "Description")[0].textContent)
}
return xmlDoc
}
}
export default Note;