receipt/Item.js

/**
 * @module receipt
 */

/**
 * Represents a line in the document.
 * When you finish setting all the item's properties, call calcMounts() to calculate the mounts.
 */
class Item {
	/** @type {string} */
	#description

	/** @type {string} */
	#code

	/** @type {number} */
	#quantity

	/** @type {number} */
	#unitValue

	/** @type {string} */
	#unitCode

	/** @type {string} */
	#classificationCode

	/** @type {number} */
	#igvPercentage = 0

	/** @type {number} */
	#iscPercentage = 0

	/** @type {number} */
	#taxableIgvAmount

	/** @type {number} */
	#igvAmount

	/** @type {number} */
	#iscAmount

	/** @type {number} */
	#taxTotalAmount

	/** @type {number} */
	#lineExtensionAmount

	/** @type {number} */
	#pricingReferenceAmount

	/** @type {number} */
	#exemptionReasonCode

	/**
	 * @param {string} description - Item description as product or service.
	 */
	constructor(description) {
		this.setDescription(description)
	}

	/**
	 * @returns {string} - Item description.
	 */
	getDescription() {
		return this.#description
	}

	/**
	 * Item description as product or service.
	 * @param {string} d - Item description.
	 */
	setDescription(d) {
		if ((typeof d === "string") && d.length > 0) {
			this.#description = d
			return
		}
	}

	/**
	 * @param {string} c - Item code.
	 */
	setCode(c) {
		if ((typeof c === "string") && c.length > 0) {
			this.#code = c
		}
	}

	/**
	 * @returns {string} - Item code.
	 */
	getCode() {
		return this.#code
	}

	/**
	 * @returns {number} - Item quantity.
	 */
	getQuantity() {
		return this.#quantity
	}

	/**
	 * How many units of the item are being sold.
	 * @param {number} q - Item quantity.
	 */
	setQuantity(q) {
		this.#quantity = q
		if (isNaN(this.#quantity)) {
			throw new Error("Cantidad no es un número.")
		}
		if (this.#quantity <= 0) {
			throw new Error("No puede haber 0 como cantidad.")
		}
	}

	/**
	 * @returns {number} - Line extension amount.
	 */
	getLineExtensionAmount() {
		return this.#lineExtensionAmount
	}

	/**
	 * According roll 03.
	 * @param {string} uc - Unit code.
	 */
	setUnitCode(uc) {
		this.#unitCode = uc
	}

	/**
	 * @returns {string} - Unit code.
	 */
	getUnitCode() {
		return this.#unitCode
	}

	/**
	 * According roll 25.
	 * @param {string} cc - Classification code.
	 */
	setClassificationCode(cc) {
		this.#classificationCode = cc
	}

	/**
	 * @returns {string} - Classification code.
	 */
	getClassificationCode() {
		return this.#classificationCode
	}

	/**
	 * @returns {number} - ISC percentage.
	 */
	getIscPercentage() {
		return this.#iscPercentage
	}

	/**
	 * @param {number} ip - ISC percentage.
	 * @throws {Error} - If ISC percentage is inconsistent.
	 */
	setIscPercentage(ip) {
		if (ip >= 0 || ip <= 100) {
			this.#iscPercentage = ip
			return
		}
		throw new Error("Porcentaje ISC inconsistente.")
	}

	/**
	 * @returns {number} - ISC amount.
	 */
	getIscAmount() {
		return this.#iscAmount
	}

	/**
	 * Percentage from 0 to 100.
	 * @param {number} ip - IGV percentage.
	 * @throws {Error} - If IGV percentage is inconsistent.
	 */
	setIgvPercentage(ip) {
		if (ip >= 0 || ip <= 100) {
			this.#igvPercentage = ip
			return
		}
		throw new Error("Porcentaje IGV inconsistente.")
	}

	/**
	 * @returns {number} - IGV percentage.
	 */
	getIgvPercentage() {
		return this.#igvPercentage
	}

	/**
	 * @returns {number} - IGV amount.
	 */
	getIgvAmount() {
		return this.#igvAmount
	}

	/**
	 * @returns {number} - Unit value.
	 */
	getUnitValue() {
		return this.#unitValue
	}

	/**
	 * It's the value of one item without taxes.
	 * @param {number} uv - Unit value.
	 * @param {boolean} withoutIgv - Whether the unit does not include IGV.
	 */
	setUnitValue(uv, withoutIgv) {
		if (!withoutIgv) {
			this.#unitValue = uv
			return
		}

		if (isNaN(this.#igvPercentage)) {
			throw new Error("Se requiere previamente porcentaje del IGV.")
		}
		this.#unitValue = uv / (1 + this.#igvPercentage / 100)
	}

	/**
	 * @returns {number} - Taxable IGV amount.
	 */
	getTaxableIgvAmount() {
		return this.#taxableIgvAmount
	}

	/**
	 * @returns {number} - Pricing reference amount.
	 */
	getPricingReferenceAmount() {
		return this.#pricingReferenceAmount
	}

	/**
	 * @returns {number} - Tax total amount.
	 */
	getTaxTotalAmount() {
		return this.#taxTotalAmount
	}

	/**
	 * Indicate how taxes affect them or not.
	 * @param {number} xrc - Exemption reason code.
	 */
	setExemptionReasonCode(xrc) {
		this.#exemptionReasonCode = xrc
	}

	/**
	 * @returns {number} - Exemption reason code.
	 */
	getExemptionReasonCode() {
		return this.#exemptionReasonCode
	}

	/**
	 * Calculates the mounts of the item using all the item's properties so call it after setting all the item's properties.
	 */
	calcMounts() {
		//Todo esto asumiendo que el valorUnitario no tiene incluido el IGV.
		// (auxiliar) valorVenta = cantidad * valorUnitario
		this.#lineExtensionAmount = this.#quantity * this.#unitValue

		let decimalIscPercentage = 0
		let decimalIgvPercentage = 0

		// Only apply when we are including taxes
		if (this.#exemptionReasonCode < 20) {
			decimalIscPercentage = this.#iscPercentage / 100 // eg: 0.17
			decimalIgvPercentage = this.#igvPercentage / 100 // eg: 0.18
		}

		this.#pricingReferenceAmount = this.#unitValue * (1 + decimalIscPercentage) * (1 + decimalIgvPercentage)

		// valorISC = (porcentajeISC/100)×valorVenta
		this.#iscAmount = decimalIscPercentage * this.#lineExtensionAmount

		// valorIGV=(porcentajeIGV/100)×(valorVenta + valorISC)
		this.#taxableIgvAmount = this.#iscAmount + this.#lineExtensionAmount
		this.#igvAmount = this.#taxableIgvAmount * decimalIgvPercentage

		this.#taxTotalAmount = this.#iscAmount + this.#igvAmount
	}
}

export default Item;