import { getDiscount } from "./helpers"; export type PriceOptions = { ico: number; iva: number; base: number; subtotal: number; total: number; measurementTotal: number; externalId?: number; maxQuantity?: number; discountedExternalId?: number; expireDate?: Date | null; quantity?: number; discount?: number | null; }; export class Price { public readonly ico: number; public readonly iva: number; public readonly base: number; public readonly subtotal: number; public readonly total: number; public readonly measurementTotal: number; public readonly externalId?: number; public readonly maxQuantity?: number; public readonly discountedExternalId?: number; public readonly expireDate?: Date | null; private _quantity = 0; public readonly discount?: number | null; constructor({ ico, iva, base, subtotal, total, measurementTotal, externalId, maxQuantity, discountedExternalId, expireDate, quantity, discount, }: PriceOptions) { this.ico = ico; this.iva = iva; this.base = base; this.subtotal = subtotal; this.total = total; this.measurementTotal = measurementTotal; this.externalId = externalId; this.maxQuantity = maxQuantity; this.discountedExternalId = discountedExternalId; this.expireDate = expireDate; this._quantity = quantity ?? 0; this.discount = discount; } get isExpired(): boolean { return !!this.expireDate && new Date() > this.expireDate; } /** * Set the price quantity until maxQuantity and returns the remaining quantity * @param quantity the quantity * @returns the remaining quantity */ set quantity(quantity: number) { if (this.isExpired && quantity > 0) { throw new Error("Price is expired"); } if ( this.maxQuantity !== undefined && this.maxQuantity !== null && quantity > this.maxQuantity ) { throw new Error( `Price can't be higher than maxQuantity(${this.maxQuantity})` ); } this._quantity = quantity; } get quantity(): number { return this._quantity; } /** * Calculates the total based on quantity * * @returns the total * quantity */ get extendedTotal() { return this.total * this.quantity; } /** * Calculates the subtotal based on quantity * * @returns the total * subtotal */ get extendedSubtotal() { return this.subtotal * this.quantity; } static fromPricing(json: any): Price[] { const prices = []; for (const item of json) { const { values, ico, iva, scheduleEndDate, discountedMaximumQuantity: maxQuantity, } = item; for (const value of values) { const { externalId, discountedExternalId } = value; if (value.discountedTotal && value.discountedTotal < value.total) { const { discountedPrice: base, discountedSubtotal: subtotal, discountedTotal: total, discountedTotalPerUnit, total: regularTotal, } = value; prices.push( new Price({ ico, iva, base, subtotal, total, externalId, maxQuantity, discountedExternalId, measurementTotal: discountedTotalPerUnit, expireDate: scheduleEndDate ? new Date(scheduleEndDate) : undefined, discount: getDiscount(regularTotal, total), }) ); } const { price: base, subtotal, total, totalPerUnit } = value; prices.push( new Price({ ico, iva, base, subtotal, total, measurementTotal: totalPerUnit, externalId, }) ); } } return prices; } static fromCatalogue(json: any): Price[] { const prices: Price[] = []; for (const item of json.prices) { if (item.discountedTotal) { const ico = item.discountedSubtotal - item.discountedBase; const iva = (item.discountedTotal - ico) / item.discountedBase - 1; prices.push( new Price({ ico, iva: Math.round(iva * 100) / 100, base: item.discountedBase, subtotal: item.discountedSubtotal, total: item.discountedTotal, measurementTotal: item.unitPrice, discount: item.discount, maxQuantity: json.discountedMaximumQuantity, expireDate: json.scheduleEndDate ? new Date(json.scheduleEndDate) : undefined, }) ); } const ico = item.managerSubtotal - item.managerPrice; const iva = (item.managerTotal - ico) / item.managerPrice - 1; prices.push( new Price({ ico, iva: Math.round(iva * 100) / 100, base: item.managerPrice, subtotal: item.managerSubtotal, total: item.managerTotal, measurementTotal: item.unitPrice, maxQuantity: json.maximumQuantity, }) ); } return prices; } toJSON(customerTotal: number) { return { customerTotal, // deprecated ico: this.ico, iva: this.iva, base: this.base, subtotal: this.subtotal, managerSubtotal: this.subtotal, // deprecated total: this.total, measurementTotal: this.measurementTotal, externalId: this.externalId, maxQuantity: this.maxQuantity, discountedExternalId: this.discountedExternalId ? this.discountedExternalId: null, expireDate: this.expireDate, quantity: this.quantity, discount: this.discount ? Math.round(this.discount * 100) / 100 : null, }; } }