Compare commits
No commits in common. "5a6a81738fa34540c701050f227b42559a0c7590" and "c1399cd67d43e898c480eb160db47f8be10015fe" have entirely different histories.
5a6a81738f
...
c1399cd67d
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"bradlc.vscode-tailwindcss",
|
|
||||||
"biomejs.biome",
|
"biomejs.biome",
|
||||||
"cweijan.vscode-mysql-client2",
|
"cweijan.vscode-mysql-client2",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"yzhang.markdown-all-in-one",
|
||||||
"ms-vscode.vscode-json",
|
"ms-vscode.vscode-json",
|
||||||
"formulahendry.auto-rename-tag",
|
"formulahendry.auto-rename-tag"
|
||||||
"cweijan.dbclient-jdbc"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { TaxCatalogProvider } from "@erp/core";
|
import { TaxCatalogProvider } from "@erp/core";
|
||||||
import { Percentage, ValueObject } from "@repo/rdx-ddd";
|
import { Percentage, ValueObject } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export class IssuedInvoiceItemsFullPresenter extends Presenter {
|
|||||||
discount_amount: allAmounts.discountAmount.toObjectString(),
|
discount_amount: allAmounts.discountAmount.toObjectString(),
|
||||||
|
|
||||||
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
tax_codes: invoiceItem.taxes.getCodesArray(),
|
tax_codes: invoiceItem.taxes.getCodesToString().split(","),
|
||||||
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
|
||||||
total_amount: allAmounts.totalAmount.toObjectString(),
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export class ProformaItemsFullPresenter extends Presenter {
|
|||||||
discount_amount: allAmounts.discountAmount.toObjectString(),
|
discount_amount: allAmounts.discountAmount.toObjectString(),
|
||||||
|
|
||||||
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
taxable_amount: allAmounts.taxableAmount.toObjectString(),
|
||||||
tax_codes: proformaItem.taxes.getCodesArray(),
|
tax_codes: proformaItem.taxes.getCodesToString().split(","),
|
||||||
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
taxes_amount: allAmounts.taxesAmount.toObjectString(),
|
||||||
|
|
||||||
total_amount: allAmounts.totalAmount.toObjectString(),
|
total_amount: allAmounts.totalAmount.toObjectString(),
|
||||||
|
|||||||
@ -39,11 +39,7 @@ export class IssuedInvoiceListPresenter extends Presenter {
|
|||||||
language_code: invoice.languageCode.code,
|
language_code: invoice.languageCode.code,
|
||||||
currency_code: invoice.currencyCode.code,
|
currency_code: invoice.currencyCode.code,
|
||||||
|
|
||||||
taxes: invoice.taxes.map((t) => ({
|
taxes: invoice.taxes,
|
||||||
tax_code: t.tax_code,
|
|
||||||
taxable_amount: t.taxable_amount.toObjectString(),
|
|
||||||
taxes_amount: t.taxes_amount.toObjectString(),
|
|
||||||
})),
|
|
||||||
|
|
||||||
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
subtotal_amount: invoice.subtotalAmount.toObjectString(),
|
||||||
discount_percentage: invoice.discountPercentage.toObjectString(),
|
discount_percentage: invoice.discountPercentage.toObjectString(),
|
||||||
|
|||||||
@ -30,11 +30,7 @@ export class ProformaListPresenter extends Presenter {
|
|||||||
language_code: proforma.languageCode.code,
|
language_code: proforma.languageCode.code,
|
||||||
currency_code: proforma.currencyCode.code,
|
currency_code: proforma.currencyCode.code,
|
||||||
|
|
||||||
taxes: proforma.taxes.map((t) => ({
|
taxes: proforma.taxes,
|
||||||
tax_code: t.tax_code,
|
|
||||||
taxable_amount: t.taxable_amount.toObjectString(),
|
|
||||||
taxes_amount: t.taxes_amount.toObjectString(),
|
|
||||||
})),
|
|
||||||
|
|
||||||
subtotal_amount: proforma.subtotalAmount.toObjectString(),
|
subtotal_amount: proforma.subtotalAmount.toObjectString(),
|
||||||
discount_percentage: proforma.discountPercentage.toObjectString(),
|
discount_percentage: proforma.discountPercentage.toObjectString(),
|
||||||
|
|||||||
@ -18,14 +18,9 @@ export class IssuedInvoiceTaxesReportPresenter extends Presenter<IssuedInvoiceTa
|
|||||||
|
|
||||||
const taxCatalogItem = this._taxCatalog.findByCode(taxItem.tax_code);
|
const taxCatalogItem = this._taxCatalog.findByCode(taxItem.tax_code);
|
||||||
|
|
||||||
const taxName = taxCatalogItem.match(
|
|
||||||
(item) => item.name,
|
|
||||||
() => taxItem.tax_code // fallback
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tax_code: taxItem.tax_code,
|
tax_code: taxItem.tax_code,
|
||||||
tax_name: taxName,
|
tax_name: taxCatalogItem.unwrap().name,
|
||||||
taxable_amount: MoneyDTOHelper.format(taxItem.taxable_amount, this._locale, moneyOptions),
|
taxable_amount: MoneyDTOHelper.format(taxItem.taxable_amount, this._locale, moneyOptions),
|
||||||
taxes_amount: MoneyDTOHelper.format(taxItem.taxes_amount, this._locale, moneyOptions),
|
taxes_amount: MoneyDTOHelper.format(taxItem.taxes_amount, this._locale, moneyOptions),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,14 +18,9 @@ export class ProformaTaxesReportPresenter extends Presenter<ProformaTaxesDTO, un
|
|||||||
|
|
||||||
const taxCatalogItem = this._taxCatalog.findByCode(taxItem.tax_code);
|
const taxCatalogItem = this._taxCatalog.findByCode(taxItem.tax_code);
|
||||||
|
|
||||||
const taxName = taxCatalogItem.match(
|
|
||||||
(item) => item.name,
|
|
||||||
() => taxItem.tax_code // fallback
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tax_code: taxItem.tax_code,
|
tax_code: taxItem.tax_code,
|
||||||
tax_name: taxName,
|
tax_name: taxCatalogItem.unwrap().name,
|
||||||
taxable_amount: MoneyDTOHelper.format(taxItem.taxable_amount, this._locale, moneyOptions),
|
taxable_amount: MoneyDTOHelper.format(taxItem.taxable_amount, this._locale, moneyOptions),
|
||||||
taxes_amount: MoneyDTOHelper.format(taxItem.taxes_amount, this._locale, moneyOptions),
|
taxes_amount: MoneyDTOHelper.format(taxItem.taxes_amount, this._locale, moneyOptions),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,6 +23,9 @@ export class IssuedInvoiceReportHTMLPresenter extends TemplatePresenter {
|
|||||||
const invoiceDTO = dtoPresenter.toOutput(invoice);
|
const invoiceDTO = dtoPresenter.toOutput(invoice);
|
||||||
const prettyDTO = prePresenter.toOutput(invoiceDTO);
|
const prettyDTO = prePresenter.toOutput(invoiceDTO);
|
||||||
|
|
||||||
|
console.log(prettyDTO.verifactu);
|
||||||
|
|
||||||
|
|
||||||
// Obtener y compilar la plantilla HTML
|
// Obtener y compilar la plantilla HTML
|
||||||
const template = this.templateResolver.compileTemplate(
|
const template = this.templateResolver.compileTemplate(
|
||||||
"customer-invoices",
|
"customer-invoices",
|
||||||
|
|||||||
@ -23,6 +23,8 @@ export class IssuedInvoiceReportPDFPresenter extends Presenter<
|
|||||||
format: "HTML",
|
format: "HTML",
|
||||||
}) as IssuedInvoiceReportHTMLPresenter;
|
}) as IssuedInvoiceReportHTMLPresenter;
|
||||||
|
|
||||||
|
console.log(invoice);
|
||||||
|
|
||||||
const htmlData = htmlPresenter.toOutput(invoice, params);
|
const htmlData = htmlPresenter.toOutput(invoice, params);
|
||||||
|
|
||||||
// Generar el PDF con Puppeteer
|
// Generar el PDF con Puppeteer
|
||||||
|
|||||||
@ -236,26 +236,6 @@ export class CustomerInvoice
|
|||||||
return this.paymentMethod.isSome();
|
return this.paymentMethod.isSome();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CALCULOS INTERNOS */
|
|
||||||
|
|
||||||
private _getSubtotalAmount(): InvoiceAmount {
|
|
||||||
const itemsSubtotal = this.items.getSubtotalAmount().convertScale(2);
|
|
||||||
|
|
||||||
return InvoiceAmount.create({
|
|
||||||
value: itemsSubtotal.value,
|
|
||||||
currency_code: this.currencyCode.code,
|
|
||||||
}).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getItemsDiscountAmount(): InvoiceAmount {
|
|
||||||
const itemsDiscountAmount = this.items.getDiscountAmount().convertScale(2);
|
|
||||||
|
|
||||||
return InvoiceAmount.create({
|
|
||||||
value: itemsDiscountAmount.value,
|
|
||||||
currency_code: this.currencyCode.code,
|
|
||||||
}).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getHeaderDiscountAmount(
|
private _getHeaderDiscountAmount(
|
||||||
subtotalAmount: InvoiceAmount,
|
subtotalAmount: InvoiceAmount,
|
||||||
itemsDiscountAmount: InvoiceAmount
|
itemsDiscountAmount: InvoiceAmount
|
||||||
@ -271,43 +251,79 @@ export class CustomerInvoice
|
|||||||
return subtotalAmount.subtract(itemsDiscountAmount).subtract(headerDiscountAmount);
|
return subtotalAmount.subtract(itemsDiscountAmount).subtract(headerDiscountAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// total impuestos suma(iva + rec + retenciones)
|
private _getTaxesAmount(taxableAmount: InvoiceAmount): InvoiceAmount {
|
||||||
private _getTaxesAmount(): InvoiceAmount {
|
let amount = InvoiceAmount.zero(this.currencyCode.code);
|
||||||
const { iva, rec, retention } = this.items.getAggregatedTaxesByType();
|
|
||||||
const total = iva.add(rec).add(retention);
|
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
||||||
return InvoiceAmount.create({
|
|
||||||
value: total.convertScale(2).value,
|
for (const taxItem of itemTaxes) {
|
||||||
currency_code: this.currencyCode.code,
|
amount = amount.add(
|
||||||
}).data;
|
InvoiceAmount.create({
|
||||||
|
value: taxItem.taxesAmount.convertScale(2).value,
|
||||||
|
currency_code: this.currencyCode.code,
|
||||||
|
}).data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTotalAmount(taxableAmount: InvoiceAmount, taxesAmount: InvoiceAmount): InvoiceAmount {
|
private _getTotalAmount(taxableAmount: InvoiceAmount, taxesAmount: InvoiceAmount): InvoiceAmount {
|
||||||
return taxableAmount.add(taxesAmount);
|
return taxableAmount.add(taxesAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Totales expuestos */
|
public _getSubtotalAmount(): InvoiceAmount {
|
||||||
|
const itemsSubtotal = this.items.getSubtotalAmount().convertScale(2);
|
||||||
|
|
||||||
|
return InvoiceAmount.create({
|
||||||
|
value: itemsSubtotal.value,
|
||||||
|
currency_code: this.currencyCode.code,
|
||||||
|
}).data as InvoiceAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _getItemsDiscountAmount(): InvoiceAmount {
|
||||||
|
const itemsDiscountAmount = this.items.getDiscountAmount().convertScale(2);
|
||||||
|
|
||||||
|
return InvoiceAmount.create({
|
||||||
|
value: itemsDiscountAmount.value,
|
||||||
|
currency_code: this.currencyCode.code,
|
||||||
|
}).data as InvoiceAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public getHeaderDiscountAmount(): InvoiceAmount {
|
||||||
|
return this._getHeaderDiscountAmount(this.getSubtotalAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTaxableAmount(): InvoiceAmount {
|
||||||
|
return this._getTaxableAmount(this.getSubtotalAmount(), this.getHeaderDiscountAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTaxesAmount(): InvoiceAmount {
|
||||||
|
return this._getTaxesAmount(this.getTaxableAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTotalAmount(): InvoiceAmount {
|
||||||
|
const taxableAmount = this.getTaxableAmount();
|
||||||
|
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
||||||
|
|
||||||
|
return this._getTotalAmount(taxableAmount, taxesAmount);
|
||||||
|
}*/
|
||||||
|
|
||||||
public getTaxes(): InvoiceTaxTotal[] {
|
public getTaxes(): InvoiceTaxTotal[] {
|
||||||
const map = this.items.getAggregatedTaxesByCode();
|
const itemTaxes = this.items.getTaxesAmountByTaxes();
|
||||||
const currency = this.currencyCode.code;
|
|
||||||
|
|
||||||
const result: InvoiceTaxTotal[] = [];
|
return itemTaxes.map((item) => {
|
||||||
|
return {
|
||||||
for (const [tax_code, entry] of map.entries()) {
|
tax: item.tax,
|
||||||
result.push({
|
|
||||||
tax: entry.tax,
|
|
||||||
taxableAmount: InvoiceAmount.create({
|
taxableAmount: InvoiceAmount.create({
|
||||||
value: entry.taxable.convertScale(2).value,
|
value: item.taxableAmount.convertScale(2).value,
|
||||||
currency_code: currency,
|
currency_code: this.currencyCode.code,
|
||||||
}).data,
|
}).data,
|
||||||
taxesAmount: InvoiceAmount.create({
|
taxesAmount: InvoiceAmount.create({
|
||||||
value: entry.total.convertScale(2).value,
|
value: item.taxesAmount.convertScale(2).value,
|
||||||
currency_code: currency,
|
currency_code: this.currencyCode.code,
|
||||||
}).data,
|
}).data,
|
||||||
});
|
};
|
||||||
}
|
});
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAllAmounts() {
|
public getAllAmounts() {
|
||||||
@ -321,7 +337,7 @@ export class CustomerInvoice
|
|||||||
headerDiscountAmount
|
headerDiscountAmount
|
||||||
); //
|
); //
|
||||||
|
|
||||||
const taxesAmount = this._getTaxesAmount();
|
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
||||||
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
|
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { type CurrencyCode, DomainEntity, type LanguageCode, type UniqueID } from "@repo/rdx-ddd";
|
import {
|
||||||
|
type CurrencyCode,
|
||||||
|
DomainEntity,
|
||||||
|
type LanguageCode,
|
||||||
|
type Percentage,
|
||||||
|
type UniqueID,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
import { type Maybe, Result } from "@repo/rdx-utils";
|
import { type Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -7,7 +13,7 @@ import {
|
|||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
} from "../../value-objects";
|
} from "../../value-objects";
|
||||||
import type { ItemTaxGroup } from "../../value-objects/item-tax-group";
|
import type { ItemTaxTotal, ItemTaxes } from "../item-taxes";
|
||||||
|
|
||||||
export interface CustomerInvoiceItemProps {
|
export interface CustomerInvoiceItemProps {
|
||||||
description: Maybe<CustomerInvoiceItemDescription>;
|
description: Maybe<CustomerInvoiceItemDescription>;
|
||||||
@ -16,7 +22,7 @@ export interface CustomerInvoiceItemProps {
|
|||||||
|
|
||||||
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
||||||
|
|
||||||
taxes: ItemTaxGroup;
|
taxes: ItemTaxes;
|
||||||
|
|
||||||
languageCode: LanguageCode;
|
languageCode: LanguageCode;
|
||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
@ -32,7 +38,7 @@ export interface ICustomerInvoiceItem {
|
|||||||
|
|
||||||
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
||||||
|
|
||||||
taxes: ItemTaxGroup;
|
taxes: ItemTaxes;
|
||||||
|
|
||||||
languageCode: LanguageCode;
|
languageCode: LanguageCode;
|
||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
@ -74,25 +80,31 @@ export class CustomerInvoiceItem
|
|||||||
return this._isValued;
|
return this._isValued;
|
||||||
}
|
}
|
||||||
|
|
||||||
get description() {
|
get description(): Maybe<CustomerInvoiceItemDescription> {
|
||||||
return this.props.description;
|
return this.props.description;
|
||||||
}
|
}
|
||||||
get quantity() {
|
|
||||||
|
get quantity(): Maybe<ItemQuantity> {
|
||||||
return this.props.quantity;
|
return this.props.quantity;
|
||||||
}
|
}
|
||||||
get unitAmount() {
|
|
||||||
|
get unitAmount(): Maybe<ItemAmount> {
|
||||||
return this.props.unitAmount;
|
return this.props.unitAmount;
|
||||||
}
|
}
|
||||||
get discountPercentage() {
|
|
||||||
|
get discountPercentage(): Maybe<Percentage> {
|
||||||
return this.props.discountPercentage;
|
return this.props.discountPercentage;
|
||||||
}
|
}
|
||||||
get languageCode() {
|
|
||||||
|
get languageCode(): LanguageCode {
|
||||||
return this.props.languageCode;
|
return this.props.languageCode;
|
||||||
}
|
}
|
||||||
get currencyCode() {
|
|
||||||
|
get currencyCode(): CurrencyCode {
|
||||||
return this.props.currencyCode;
|
return this.props.currencyCode;
|
||||||
}
|
}
|
||||||
get taxes() {
|
|
||||||
|
get taxes(): ItemTaxes {
|
||||||
return this.props.taxes;
|
return this.props.taxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +124,7 @@ export class CustomerInvoiceItem
|
|||||||
*/
|
*/
|
||||||
private _getDiscountAmount(subtotalAmount: ItemAmount): ItemAmount {
|
private _getDiscountAmount(subtotalAmount: ItemAmount): ItemAmount {
|
||||||
const discount = this.discountPercentage.match(
|
const discount = this.discountPercentage.match(
|
||||||
(discount) => discount,
|
(percentage) => percentage,
|
||||||
() => ItemDiscount.zero()
|
() => ItemDiscount.zero()
|
||||||
);
|
);
|
||||||
return subtotalAmount.percentage(discount);
|
return subtotalAmount.percentage(discount);
|
||||||
@ -129,11 +141,6 @@ export class CustomerInvoiceItem
|
|||||||
return subtotalAmount.subtract(discountAmount);
|
return subtotalAmount.subtract(discountAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* importes individuales: iva / rec / ret */
|
|
||||||
private _getIndividualTaxAmounts(taxableAmount: ItemAmount) {
|
|
||||||
return this.props.taxes.calculateAmounts(taxableAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @summary Calcula el importe total de impuestos sobre la base imponible.
|
* @summary Calcula el importe total de impuestos sobre la base imponible.
|
||||||
@ -141,8 +148,7 @@ export class CustomerInvoiceItem
|
|||||||
* @returns El importe de impuestos calculado.
|
* @returns El importe de impuestos calculado.
|
||||||
*/
|
*/
|
||||||
private _getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
|
private _getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
|
||||||
const { ivaAmount, recAmount, retentionAmount } = this._getIndividualTaxAmounts(taxableAmount);
|
return this.props.taxes.getTaxesAmount(taxableAmount);
|
||||||
return ivaAmount.add(recAmount).add(retentionAmount); // retención ya es negativa
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,15 +169,17 @@ export class CustomerInvoiceItem
|
|||||||
* Si la cantidad o el importe unitario no están definidos, se asumen valores cero.
|
* Si la cantidad o el importe unitario no están definidos, se asumen valores cero.
|
||||||
*/
|
*/
|
||||||
public getSubtotalAmount(): ItemAmount {
|
public getSubtotalAmount(): ItemAmount {
|
||||||
const qty = this.quantity.match(
|
const curCode = this.currencyCode.code;
|
||||||
|
const quantity = this.quantity.match(
|
||||||
(quantity) => quantity,
|
(quantity) => quantity,
|
||||||
() => ItemQuantity.zero()
|
() => ItemQuantity.zero()
|
||||||
);
|
);
|
||||||
const unit = this.unitAmount.match(
|
const unitAmount = this.unitAmount.match(
|
||||||
(unitAmount) => unitAmount,
|
(unitAmount) => unitAmount,
|
||||||
() => ItemAmount.zero(this.currencyCode.code)
|
() => ItemAmount.zero(curCode)
|
||||||
);
|
);
|
||||||
return unit.multiply(qty);
|
|
||||||
|
return unitAmount.multiply(quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,11 +198,6 @@ export class CustomerInvoiceItem
|
|||||||
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
|
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* importes individuales: iva / rec / ret */
|
|
||||||
public getIndividualTaxAmounts() {
|
|
||||||
return this._getIndividualTaxAmounts(this.getTaxableAmount());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Calcula el importe total de impuestos aplicados al ítem.
|
* @summary Calcula el importe total de impuestos aplicados al ítem.
|
||||||
* @returns Un `ItemAmount` con el total de impuestos.
|
* @returns Un `ItemAmount` con el total de impuestos.
|
||||||
@ -214,6 +217,14 @@ export class CustomerInvoiceItem
|
|||||||
return this._getTotalAmount(taxableAmount, taxesAmount);
|
return this._getTotalAmount(taxableAmount, taxesAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Obtiene los importes de impuestos agrupados por tipo de impuesto.
|
||||||
|
* @returns Una colección con la base imponible y el importe de impuestos por cada tipo.
|
||||||
|
*/
|
||||||
|
public getTaxesAmountByTaxes(): ItemTaxTotal[] {
|
||||||
|
return this.taxes.getTaxesAmountByTaxes(this.getTaxableAmount());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Devuelve todos los importes calculados del ítem en un único objeto.
|
* @summary Devuelve todos los importes calculados del ítem en un único objeto.
|
||||||
* @returns Un objeto con las propiedades:
|
* @returns Un objeto con las propiedades:
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { CurrencyCode, LanguageCode } from "@repo/rdx-ddd";
|
|||||||
import { Collection } from "@repo/rdx-utils";
|
import { Collection } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import { ItemAmount } from "../../value-objects";
|
import { ItemAmount } from "../../value-objects";
|
||||||
|
import { ItemTaxes } from "../item-taxes";
|
||||||
|
|
||||||
import type { CustomerInvoiceItem } from "./customer-invoice-item";
|
import type { CustomerInvoiceItem } from "./customer-invoice-item";
|
||||||
|
|
||||||
@ -17,9 +18,10 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
|||||||
private _currencyCode!: CurrencyCode;
|
private _currencyCode!: CurrencyCode;
|
||||||
|
|
||||||
constructor(props: CustomerInvoiceItemsProps) {
|
constructor(props: CustomerInvoiceItemsProps) {
|
||||||
super(props.items ?? []);
|
const { items = [], languageCode, currencyCode } = props;
|
||||||
this._languageCode = props.languageCode;
|
super(items);
|
||||||
this._currencyCode = props.currencyCode;
|
this._languageCode = languageCode;
|
||||||
|
this._currencyCode = currencyCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(props: CustomerInvoiceItemsProps): CustomerInvoiceItems {
|
public static create(props: CustomerInvoiceItemsProps): CustomerInvoiceItems {
|
||||||
@ -103,83 +105,65 @@ export class CustomerInvoiceItems extends Collection<CustomerInvoiceItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* totales de iva/rec/ret a nivel factura */
|
/**
|
||||||
public getAggregatedTaxesByType() {
|
* @summary Agrupa los importes imponibles e impuestos por tipo de impuesto.
|
||||||
let iva = ItemAmount.zero(this._currencyCode.code);
|
* @returns Un array con objetos que contienen:
|
||||||
let rec = ItemAmount.zero(this._currencyCode.code);
|
* - `tax`: El tipo de impuesto.
|
||||||
let retention = ItemAmount.zero(this._currencyCode.code);
|
* - `taxableAmount`: El total de base imponible asociada a ese impuesto.
|
||||||
|
* - `taxesAmount`: El importe total de impuestos calculado.
|
||||||
|
* @remarks
|
||||||
|
* Los impuestos se agrupan por su `tax.code` (código fiscal).
|
||||||
|
* Si existen varios impuestos con el mismo código, sus importes se agregan.
|
||||||
|
* En caso de conflicto de datos en impuestos con mismo código, prevalece
|
||||||
|
* el primer `Tax` encontrado.
|
||||||
|
*/
|
||||||
|
public getTaxesAmountByTaxes(): Array<{
|
||||||
|
tax: Tax;
|
||||||
|
taxableAmount: ItemAmount;
|
||||||
|
taxesAmount: ItemAmount;
|
||||||
|
}> {
|
||||||
|
const getTaxCode = (tax: Tax): string => tax.code; // clave estable para Map
|
||||||
|
const currencyCode = this._currencyCode.code;
|
||||||
|
|
||||||
|
// Mapeamos por clave (tax code), pero también guardamos el Tax original
|
||||||
|
const resultMap = new Map<
|
||||||
|
string,
|
||||||
|
{ tax: Tax; taxableAmount: ItemAmount; taxesAmount: ItemAmount }
|
||||||
|
>();
|
||||||
|
|
||||||
for (const item of this.getAll()) {
|
for (const item of this.getAll()) {
|
||||||
const { ivaAmount, recAmount, retentionAmount } = item.getIndividualTaxAmounts();
|
for (const { taxableAmount, tax, taxesAmount } of item.getTaxesAmountByTaxes()) {
|
||||||
|
const key = getTaxCode(tax);
|
||||||
|
const current = resultMap.get(key) ?? {
|
||||||
|
tax,
|
||||||
|
taxableAmount: ItemAmount.zero(currencyCode),
|
||||||
|
taxesAmount: ItemAmount.zero(currencyCode),
|
||||||
|
};
|
||||||
|
|
||||||
iva = iva.add(ivaAmount);
|
resultMap.set(key, {
|
||||||
rec = rec.add(recAmount);
|
tax: current.tax,
|
||||||
retention = retention.add(retentionAmount);
|
taxableAmount: current.taxableAmount.add(taxableAmount),
|
||||||
|
taxesAmount: current.taxesAmount.add(taxesAmount),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { iva, rec, retention };
|
return Array.from(resultMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* agrupación por código fiscal → usado para customer_invoice_taxes */
|
/**
|
||||||
public getAggregatedTaxesByCode() {
|
* @summary Obtiene la lista de impuestos únicos aplicados en todos los ítems.
|
||||||
const map = new Map<string, { tax: Tax; taxable: ItemAmount; total: ItemAmount }>();
|
* @returns Un objeto `ItemTaxes` que contiene todos los impuestos distintos.
|
||||||
|
* @remarks
|
||||||
|
* Los impuestos se deduplican por su código (`tax.code`).
|
||||||
|
* Si existen varios impuestos con el mismo código, el último encontrado
|
||||||
|
* sobrescribirá los anteriores sin generar error.
|
||||||
|
*/
|
||||||
|
public getTaxes(): ItemTaxes {
|
||||||
|
const taxes = this.getAll()
|
||||||
|
.flatMap((item) => item.taxes.getAll())
|
||||||
|
.reduce((map, tax) => map.set(tax.code, tax), new Map<string, Tax>());
|
||||||
|
|
||||||
for (const item of this.getAll()) {
|
return ItemTaxes.create([...taxes.values()]);
|
||||||
const taxable = item.getTaxableAmount();
|
|
||||||
const { ivaAmount, recAmount, retentionAmount } = item.getIndividualTaxAmounts();
|
|
||||||
|
|
||||||
item.taxes.iva.match(
|
|
||||||
(iva) => {
|
|
||||||
const prev = map.get(iva.code) ?? {
|
|
||||||
taxable: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
total: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
};
|
|
||||||
map.set(iva.code, {
|
|
||||||
tax: iva,
|
|
||||||
taxable: prev.taxable.add(taxable),
|
|
||||||
total: prev.total.add(ivaAmount),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
item.taxes.rec.match(
|
|
||||||
(rec) => {
|
|
||||||
const prev = map.get(rec.code) ?? {
|
|
||||||
taxable: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
total: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
};
|
|
||||||
map.set(rec.code, {
|
|
||||||
tax: rec,
|
|
||||||
taxable: prev.taxable.add(taxable),
|
|
||||||
total: prev.total.add(recAmount),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
item.taxes.retention.match(
|
|
||||||
(retention) => {
|
|
||||||
const prev = map.get(retention.code) ?? {
|
|
||||||
taxable: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
total: ItemAmount.zero(taxable.currencyCode),
|
|
||||||
};
|
|
||||||
map.set(retention.code, {
|
|
||||||
tax: retention,
|
|
||||||
taxable: prev.taxable.add(taxable),
|
|
||||||
total: prev.total.add(retentionAmount),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export * from "./customer-invoice-items";
|
export * from "./customer-invoice-items";
|
||||||
export * from "./invoice-payment-method";
|
export * from "./invoice-payment-method";
|
||||||
export * from "./invoice-taxes";
|
export * from "./invoice-taxes";
|
||||||
|
export * from "./item-taxes";
|
||||||
export * from "./verifactu-record";
|
export * from "./verifactu-record";
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import type { Tax } from "@erp/core/api";
|
import { Tax } from "@erp/core/api";
|
||||||
import { DomainEntity, type UniqueID } from "@repo/rdx-ddd";
|
import { DomainEntity, UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { InvoiceAmount } from "../../value-objects/invoice-amount";
|
||||||
import type { InvoiceAmount } from "../../value-objects/invoice-amount";
|
|
||||||
|
|
||||||
export interface InvoiceTaxProps {
|
export interface InvoiceTaxProps {
|
||||||
tax: Tax;
|
tax: Tax;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { type Tax, Taxes } from "@erp/core/api";
|
import { Tax, Taxes } from "@erp/core/api";
|
||||||
|
|
||||||
import { InvoiceAmount } from "../../value-objects";
|
import { InvoiceAmount } from "../../value-objects";
|
||||||
|
|
||||||
export type InvoiceTaxTotal = {
|
export type InvoiceTaxTotal = {
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./item-taxes";
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { type Tax, Taxes } from "@erp/core/api";
|
||||||
|
|
||||||
|
import { ItemAmount } from "../../value-objects";
|
||||||
|
|
||||||
|
export type ItemTaxTotal = {
|
||||||
|
tax: Tax;
|
||||||
|
taxableAmount: ItemAmount;
|
||||||
|
taxesAmount: ItemAmount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ItemTaxes extends Taxes {
|
||||||
|
constructor(items: Tax[] = [], totalItems: number | null = null) {
|
||||||
|
super(items, totalItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
|
||||||
|
return this.getAll().reduce(
|
||||||
|
(total, tax) => total.add(taxableAmount.percentage(tax.percentage)),
|
||||||
|
ItemAmount.zero(taxableAmount.currencyCode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTaxesAmountByTaxCode(taxCode: string, taxableAmount: ItemAmount): ItemAmount {
|
||||||
|
const currencyCode = taxableAmount.currencyCode;
|
||||||
|
|
||||||
|
return this.filter((itemTax) => itemTax.code === taxCode).reduce((totalAmount, itemTax) => {
|
||||||
|
return taxableAmount.percentage(itemTax.percentage).add(totalAmount);
|
||||||
|
}, ItemAmount.zero(currencyCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTaxesAmountByTaxes(taxableAmount: ItemAmount): ItemTaxTotal[] {
|
||||||
|
return this.getAll().map((taxItem) => ({
|
||||||
|
taxableAmount,
|
||||||
|
tax: taxItem,
|
||||||
|
taxesAmount: this.getTaxesAmountByTaxCode(taxItem.code, taxableAmount),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCodesToString(): string {
|
||||||
|
return this.getAll()
|
||||||
|
.map((taxItem) => taxItem.code)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import type { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import type { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
import { CustomerInvoiceNumber, CustomerInvoiceSerie } from "../value-objects";
|
||||||
import type { CustomerInvoiceNumber, CustomerInvoiceSerie } from "../value-objects";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Servicio de dominio que define cómo se genera el siguiente número de factura.
|
* Servicio de dominio que define cómo se genera el siguiente número de factura.
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { CompositeSpecification } from "@repo/rdx-ddd";
|
import { CompositeSpecification } from "@repo/rdx-ddd";
|
||||||
|
import { CustomerInvoice } from "../aggregates";
|
||||||
import type { CustomerInvoice } from "../aggregates";
|
|
||||||
import { INVOICE_STATUS } from "../value-objects";
|
import { INVOICE_STATUS } from "../value-objects";
|
||||||
|
|
||||||
export class ProformaCanTranstionToIssuedSpecification extends CompositeSpecification<CustomerInvoice> {
|
export class ProformaCanTranstionToIssuedSpecification extends CompositeSpecification<CustomerInvoice> {
|
||||||
|
|||||||
@ -8,5 +8,4 @@ export * from "./invoice-recipient";
|
|||||||
export * from "./item-amount";
|
export * from "./item-amount";
|
||||||
export * from "./item-discount";
|
export * from "./item-discount";
|
||||||
export * from "./item-quantity";
|
export * from "./item-quantity";
|
||||||
export * from "./item-tax-group";
|
|
||||||
export * from "./verifactu-status";
|
export * from "./verifactu-status";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { MoneyValue, type MoneyValueProps, type Percentage, type Quantity } from "@repo/rdx-ddd";
|
import { MoneyValue, MoneyValueProps, Percentage, Quantity } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
type InvoiceAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
type InvoiceAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { MoneyValue, type MoneyValueProps, type Percentage, type Quantity } from "@repo/rdx-ddd";
|
import { MoneyValue, MoneyValueProps, Percentage, Quantity } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
type ItemAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
type ItemAmountProps = Pick<MoneyValueProps, "value" | "currency_code">;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Percentage, type PercentageProps } from "@repo/rdx-ddd";
|
import { Percentage, PercentageProps } from "@repo/rdx-ddd";
|
||||||
import type { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
type ItemDiscountProps = Pick<PercentageProps, "value">;
|
type ItemDiscountProps = Pick<PercentageProps, "value">;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Quantity, type QuantityProps } from "@repo/rdx-ddd";
|
import { Quantity, QuantityProps } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
type ItemQuantityProps = Pick<QuantityProps, "value">;
|
type ItemQuantityProps = Pick<QuantityProps, "value">;
|
||||||
|
|
||||||
|
|||||||
@ -1,87 +0,0 @@
|
|||||||
import type { Tax } from "@erp/core/api";
|
|
||||||
import { ValueObject } from "@repo/rdx-ddd";
|
|
||||||
import { type Maybe, Result } from "@repo/rdx-utils";
|
|
||||||
|
|
||||||
import { ItemAmount } from ".";
|
|
||||||
|
|
||||||
export interface ItemTaxGroupProps {
|
|
||||||
iva: Maybe<Tax>; // si existe
|
|
||||||
rec: Maybe<Tax>; // si existe
|
|
||||||
retention: Maybe<Tax>; // si existe
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ItemTaxGroup extends ValueObject<ItemTaxGroupProps> {
|
|
||||||
static create(props: ItemTaxGroupProps) {
|
|
||||||
return Result.ok(new ItemTaxGroup(props));
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateAmounts(taxableAmount: ItemAmount) {
|
|
||||||
const ivaAmount = this.props.iva.match(
|
|
||||||
(iva) => taxableAmount.percentage(iva.percentage),
|
|
||||||
() => ItemAmount.zero(taxableAmount.currencyCode)
|
|
||||||
);
|
|
||||||
|
|
||||||
const recAmount = this.props.rec.match(
|
|
||||||
(rec) => taxableAmount.percentage(rec.percentage),
|
|
||||||
() => ItemAmount.zero(taxableAmount.currencyCode)
|
|
||||||
);
|
|
||||||
|
|
||||||
const retentionAmount = this.props.retention.match(
|
|
||||||
(retention) => taxableAmount.percentage(retention.percentage).multiply(-1),
|
|
||||||
() => ItemAmount.zero(taxableAmount.currencyCode)
|
|
||||||
);
|
|
||||||
|
|
||||||
return { ivaAmount, recAmount, retentionAmount };
|
|
||||||
}
|
|
||||||
|
|
||||||
get iva(): Maybe<Tax> {
|
|
||||||
return this.props.iva;
|
|
||||||
}
|
|
||||||
|
|
||||||
get rec(): Maybe<Tax> {
|
|
||||||
return this.props.rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
get retention(): Maybe<Tax> {
|
|
||||||
return this.props.retention;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCodesArray(): string[] {
|
|
||||||
const codes: string[] = [];
|
|
||||||
|
|
||||||
this.props.iva.match(
|
|
||||||
(iva) => codes.push(iva.code),
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.props.rec.match(
|
|
||||||
(rec) => codes.push(rec.code),
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.props.retention.match(
|
|
||||||
(retention) => codes.push(retention.code),
|
|
||||||
() => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return codes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCodesToString(): string {
|
|
||||||
return this.getCodesArray().join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
getProps() {
|
|
||||||
return this.props;
|
|
||||||
}
|
|
||||||
|
|
||||||
toPrimitive() {
|
|
||||||
return this.getProps();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +1,7 @@
|
|||||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
|
||||||
import {
|
import {
|
||||||
type ISequelizeDomainMapper,
|
type ISequelizeDomainMapper,
|
||||||
type MapperParamsType,
|
type MapperParamsType,
|
||||||
SequelizeDomainMapper,
|
SequelizeDomainMapper,
|
||||||
Tax,
|
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
UniqueID,
|
UniqueID,
|
||||||
@ -24,13 +22,15 @@ import {
|
|||||||
ItemAmount,
|
ItemAmount,
|
||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
ItemTaxGroup,
|
ItemTaxes,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
import type {
|
import type {
|
||||||
CustomerInvoiceItemCreationAttributes,
|
CustomerInvoiceItemCreationAttributes,
|
||||||
CustomerInvoiceItemModel,
|
CustomerInvoiceItemModel,
|
||||||
} from "../../sequelize";
|
} from "../../sequelize";
|
||||||
|
|
||||||
|
import { ItemTaxesDomainMapper } from "./item-taxes.mapper";
|
||||||
|
|
||||||
export interface ICustomerInvoiceItemDomainMapper
|
export interface ICustomerInvoiceItemDomainMapper
|
||||||
extends ISequelizeDomainMapper<
|
extends ISequelizeDomainMapper<
|
||||||
CustomerInvoiceItemModel,
|
CustomerInvoiceItemModel,
|
||||||
@ -46,19 +46,11 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
>
|
>
|
||||||
implements ICustomerInvoiceItemDomainMapper
|
implements ICustomerInvoiceItemDomainMapper
|
||||||
{
|
{
|
||||||
private _taxCatalog!: JsonTaxCatalogProvider;
|
private _taxesMapper: ItemTaxesDomainMapper;
|
||||||
|
|
||||||
constructor(params: MapperParamsType) {
|
constructor(params: MapperParamsType) {
|
||||||
super();
|
super();
|
||||||
const { taxCatalog } = params as {
|
this._taxesMapper = new ItemTaxesDomainMapper(params);
|
||||||
taxCatalog: JsonTaxCatalogProvider;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!taxCatalog) {
|
|
||||||
throw new Error('taxCatalog not defined ("CustomerInvoiceItemDomainMapper")');
|
|
||||||
}
|
|
||||||
|
|
||||||
this._taxCatalog = taxCatalog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapAttributesToDomain(
|
private mapAttributesToDomain(
|
||||||
@ -105,26 +97,6 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
const iva = extractOrPushError(
|
|
||||||
maybeFromNullableVO(source.iva_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
|
|
||||||
`items[${index}].iva_code`,
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const rec = extractOrPushError(
|
|
||||||
maybeFromNullableVO(source.rec_code, (code) => Tax.createFromCode(code, this._taxCatalog)),
|
|
||||||
`items[${index}].rec_code`,
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
const retention = extractOrPushError(
|
|
||||||
maybeFromNullableVO(source.retention_code, (code) =>
|
|
||||||
Tax.createFromCode(code, this._taxCatalog)
|
|
||||||
),
|
|
||||||
`items[${index}].retention_code`,
|
|
||||||
errors
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
itemId,
|
itemId,
|
||||||
|
|
||||||
@ -134,12 +106,6 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
quantity,
|
quantity,
|
||||||
unitAmount,
|
unitAmount,
|
||||||
discountPercentage,
|
discountPercentage,
|
||||||
|
|
||||||
taxes: ItemTaxGroup.create({
|
|
||||||
iva: iva!,
|
|
||||||
rec: rec!,
|
|
||||||
retention: retention!,
|
|
||||||
}).data,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +122,23 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
// 1) Valores escalares (atributos generales)
|
// 1) Valores escalares (atributos generales)
|
||||||
const attributes = this.mapAttributesToDomain(source, params);
|
const attributes = this.mapAttributesToDomain(source, params);
|
||||||
|
|
||||||
|
// 2) Taxes (colección a nivel de item/línea)
|
||||||
|
const taxesResults = this._taxesMapper.mapToDomainCollection(
|
||||||
|
source.taxes,
|
||||||
|
source.taxes.length,
|
||||||
|
{
|
||||||
|
attributes,
|
||||||
|
...params,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (taxesResults.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "taxes",
|
||||||
|
message: taxesResults.error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Si hubo errores de mapeo, devolvemos colección de validación
|
// Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
@ -163,7 +146,9 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Construcción del elemento de dominio
|
const taxes = ItemTaxes.create(taxesResults.data.getAll());
|
||||||
|
|
||||||
|
// 3) Construcción del elemento de dominio
|
||||||
const createResult = CustomerInvoiceItem.create(
|
const createResult = CustomerInvoiceItem.create(
|
||||||
{
|
{
|
||||||
languageCode: attributes.languageCode!,
|
languageCode: attributes.languageCode!,
|
||||||
@ -172,7 +157,7 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
quantity: attributes.quantity!,
|
quantity: attributes.quantity!,
|
||||||
unitAmount: attributes.unitAmount!,
|
unitAmount: attributes.unitAmount!,
|
||||||
discountPercentage: attributes.discountPercentage!,
|
discountPercentage: attributes.discountPercentage!,
|
||||||
taxes: attributes.taxes!,
|
taxes,
|
||||||
},
|
},
|
||||||
attributes.itemId
|
attributes.itemId
|
||||||
);
|
);
|
||||||
@ -198,6 +183,18 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
errors: ValidationErrorDetail[];
|
errors: ValidationErrorDetail[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const taxesResults = this._taxesMapper.mapToPersistenceArray(source.taxes, {
|
||||||
|
...params,
|
||||||
|
parent: source,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (taxesResults.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "taxes",
|
||||||
|
message: taxesResults.error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const allAmounts = source.getAllAmounts();
|
const allAmounts = source.getAllAmounts();
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
@ -238,9 +235,7 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
total_amount_value: allAmounts.totalAmount.value,
|
total_amount_value: allAmounts.totalAmount.value,
|
||||||
total_amount_scale: allAmounts.totalAmount.scale,
|
total_amount_scale: allAmounts.totalAmount.scale,
|
||||||
|
|
||||||
iva_code: toNullable(source.taxes.iva, (t) => t.code),
|
taxes: taxesResults.data,
|
||||||
rec_code: toNullable(source.taxes.rec, (t) => t.code),
|
|
||||||
retention_code: toNullable(source.taxes.retention, (t) => t.code),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -220,6 +220,14 @@ export class CustomerInvoiceDomainMapper
|
|||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*if (recipientResult.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "recipient",
|
||||||
|
|
||||||
|
message: recipientResult.error.message,
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
// 3) Verifactu (snapshot en la factura o include)
|
// 3) Verifactu (snapshot en la factura o include)
|
||||||
const verifactuResult = this._verifactuMapper.mapToDomain(source.verifactu, {
|
const verifactuResult = this._verifactuMapper.mapToDomain(source.verifactu, {
|
||||||
errors,
|
errors,
|
||||||
@ -227,6 +235,14 @@ export class CustomerInvoiceDomainMapper
|
|||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*if (verifactuResult.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "verifactu",
|
||||||
|
|
||||||
|
message: verifactuResult.error.message,
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
// 4) Items (colección)
|
// 4) Items (colección)
|
||||||
const itemsResults = this._itemsMapper.mapToDomainCollection(
|
const itemsResults = this._itemsMapper.mapToDomainCollection(
|
||||||
source.items,
|
source.items,
|
||||||
@ -238,6 +254,16 @@ export class CustomerInvoiceDomainMapper
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*if (itemsResults.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "items",
|
||||||
|
message: itemsResults.error.message,
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Nota: los impuestos a nivel factura (tabla customer_invoice_taxes) se derivan de los items.
|
||||||
|
// El agregado expone un getter `taxes` (derivado). No se incluye en las props.
|
||||||
|
|
||||||
// 5) Si hubo errores de mapeo, devolvemos colección de validación
|
// 5) Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
@ -247,6 +273,9 @@ export class CustomerInvoiceDomainMapper
|
|||||||
|
|
||||||
// 6) Construcción del agregado (Dominio)
|
// 6) Construcción del agregado (Dominio)
|
||||||
|
|
||||||
|
const verifactu = verifactuResult.data;
|
||||||
|
const recipient = recipientResult.data;
|
||||||
|
|
||||||
const items = CustomerInvoiceItems.create({
|
const items = CustomerInvoiceItems.create({
|
||||||
languageCode: attributes.languageCode!,
|
languageCode: attributes.languageCode!,
|
||||||
currencyCode: attributes.currencyCode!,
|
currencyCode: attributes.currencyCode!,
|
||||||
@ -265,7 +294,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
operationDate: attributes.operationDate!,
|
operationDate: attributes.operationDate!,
|
||||||
|
|
||||||
customerId: attributes.customerId!,
|
customerId: attributes.customerId!,
|
||||||
recipient: recipientResult.data,
|
recipient: recipient,
|
||||||
|
|
||||||
reference: attributes.reference!,
|
reference: attributes.reference!,
|
||||||
description: attributes.description!,
|
description: attributes.description!,
|
||||||
@ -279,7 +308,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
paymentMethod: attributes.paymentMethod!,
|
paymentMethod: attributes.paymentMethod!,
|
||||||
|
|
||||||
items,
|
items,
|
||||||
verifactu: verifactuResult.data,
|
verifactu,
|
||||||
};
|
};
|
||||||
|
|
||||||
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
|
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
|
||||||
|
|||||||
@ -1,26 +1,11 @@
|
|||||||
import type { JsonTaxCatalogProvider } from "@erp/core";
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
import { type MapperParamsType, SequelizeDomainMapper, Tax } from "@erp/core/api";
|
import { MapperParamsType, SequelizeDomainMapper, Tax } from "@erp/core/api";
|
||||||
import { UniqueID, type ValidationErrorDetail } from "@repo/rdx-ddd";
|
import { UniqueID, ValidationErrorDetail } from "@repo/rdx-ddd";
|
||||||
|
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { CustomerInvoice, CustomerInvoiceItemProps, ItemAmount } from "../../../domain";
|
||||||
|
import { CustomerInvoiceTaxCreationAttributes, CustomerInvoiceTaxModel } from "../../sequelize";
|
||||||
|
|
||||||
import { type CustomerInvoice, type CustomerInvoiceItemProps, ItemAmount } from "../../../domain";
|
|
||||||
import type {
|
|
||||||
CustomerInvoiceTaxCreationAttributes,
|
|
||||||
CustomerInvoiceTaxModel,
|
|
||||||
} from "../../sequelize";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mapper para customer_invoice_taxes
|
|
||||||
*
|
|
||||||
* Domina estructuras:
|
|
||||||
* {
|
|
||||||
* tax: Tax
|
|
||||||
* taxableAmount: ItemAmount
|
|
||||||
* taxesAmount: ItemAmount
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Cada fila = un impuesto agregado en toda la factura.
|
|
||||||
*/
|
|
||||||
export class TaxesDomainMapper extends SequelizeDomainMapper<
|
export class TaxesDomainMapper extends SequelizeDomainMapper<
|
||||||
CustomerInvoiceTaxModel,
|
CustomerInvoiceTaxModel,
|
||||||
CustomerInvoiceTaxCreationAttributes,
|
CustomerInvoiceTaxCreationAttributes,
|
||||||
@ -56,17 +41,15 @@ export class TaxesDomainMapper extends SequelizeDomainMapper<
|
|||||||
attributes: Partial<CustomerInvoiceItemProps>;
|
attributes: Partial<CustomerInvoiceItemProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const currency_code = attributes.currencyCode!.code;
|
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
taxableAmount: ItemAmount.create({
|
taxableAmount: ItemAmount.create({
|
||||||
value: source.taxable_amount_value,
|
value: source.taxable_amount_value,
|
||||||
currency_code,
|
currency_code: attributes.currencyCode!.code,
|
||||||
}).data,
|
}).data,
|
||||||
tax: Tax.createFromCode(source.tax_code, this._taxCatalog).data,
|
tax: Tax.createFromCode(source.tax_code, this._taxCatalog).data,
|
||||||
taxesAmount: ItemAmount.create({
|
taxesAmount: ItemAmount.create({
|
||||||
value: source.taxes_amount_value,
|
value: source.taxes_amount_value,
|
||||||
currency_code,
|
currency_code: attributes.currencyCode!.code,
|
||||||
}).data,
|
}).data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -84,6 +67,8 @@ export class TaxesDomainMapper extends SequelizeDomainMapper<
|
|||||||
errors: ValidationErrorDetail[];
|
errors: ValidationErrorDetail[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
source;
|
||||||
|
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
tax_id: UniqueID.generateNewID().toPrimitive(),
|
tax_id: UniqueID.generateNewID().toPrimitive(),
|
||||||
invoice_id: parent.id.toPrimitive(),
|
invoice_id: parent.id.toPrimitive(),
|
||||||
|
|||||||
@ -0,0 +1,112 @@
|
|||||||
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
|
import {
|
||||||
|
ISequelizeDomainMapper,
|
||||||
|
MapperParamsType,
|
||||||
|
SequelizeDomainMapper,
|
||||||
|
Tax,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import {
|
||||||
|
extractOrPushError,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
ValidationErrorDetail,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
import { CustomerInvoiceItem } from "../../../domain";
|
||||||
|
import {
|
||||||
|
CustomerInvoiceItemTaxCreationAttributes,
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
} from "../../sequelize";
|
||||||
|
|
||||||
|
export interface IItemTaxesDomainMapper
|
||||||
|
extends ISequelizeDomainMapper<
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
CustomerInvoiceItemTaxCreationAttributes,
|
||||||
|
Tax
|
||||||
|
> {}
|
||||||
|
|
||||||
|
export class ItemTaxesDomainMapper
|
||||||
|
extends SequelizeDomainMapper<
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
CustomerInvoiceItemTaxCreationAttributes,
|
||||||
|
Tax
|
||||||
|
>
|
||||||
|
implements IItemTaxesDomainMapper
|
||||||
|
{
|
||||||
|
private _taxCatalog!: JsonTaxCatalogProvider;
|
||||||
|
|
||||||
|
constructor(params: MapperParamsType) {
|
||||||
|
super();
|
||||||
|
const { taxCatalog } = params as {
|
||||||
|
taxCatalog: JsonTaxCatalogProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!taxCatalog) {
|
||||||
|
throw new Error('taxCatalog not defined ("ItemTaxesMapper")');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._taxCatalog = taxCatalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapToDomain(
|
||||||
|
source: CustomerInvoiceItemTaxModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<Tax, Error> {
|
||||||
|
const { errors, index } = params as {
|
||||||
|
index: number;
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const tax = extractOrPushError(
|
||||||
|
Tax.createFromCode(source.tax_code, this._taxCatalog),
|
||||||
|
`taxes[${index}].tax_code`,
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
// Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Invoice item tax mapping failed [mapToDomain]", errors)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creación del objeto de dominio
|
||||||
|
const createResult = Tax.create(tax!);
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Invoice item tax creation failed", [
|
||||||
|
{ path: `taxes[${index}]`, message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public mapToPersistence(
|
||||||
|
source: Tax,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<CustomerInvoiceItemTaxCreationAttributes, Error> {
|
||||||
|
const { errors, parent } = params as {
|
||||||
|
parent: CustomerInvoiceItem;
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const taxableAmount = parent.getTaxableAmount();
|
||||||
|
const taxAmount = taxableAmount.percentage(source.percentage);
|
||||||
|
|
||||||
|
return Result.ok({
|
||||||
|
tax_id: UniqueID.generateNewID().toPrimitive(),
|
||||||
|
item_id: parent.id.toPrimitive(),
|
||||||
|
|
||||||
|
tax_code: source.code,
|
||||||
|
|
||||||
|
taxable_amount_value: taxableAmount.value,
|
||||||
|
taxable_amount_scale: taxableAmount.scale,
|
||||||
|
|
||||||
|
taxes_amount_value: taxAmount.value,
|
||||||
|
taxes_amount_scale: taxAmount.scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,6 @@ import {
|
|||||||
CustomerInvoiceStatus,
|
CustomerInvoiceStatus,
|
||||||
InvoiceAmount,
|
InvoiceAmount,
|
||||||
type InvoiceRecipient,
|
type InvoiceRecipient,
|
||||||
ItemAmount,
|
|
||||||
type VerifactuRecord,
|
type VerifactuRecord,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
import type { CustomerInvoiceModel } from "../../sequelize";
|
import type { CustomerInvoiceModel } from "../../sequelize";
|
||||||
@ -51,11 +50,7 @@ export type CustomerInvoiceListDTO = {
|
|||||||
languageCode: LanguageCode;
|
languageCode: LanguageCode;
|
||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
|
|
||||||
taxes: {
|
taxes: string;
|
||||||
tax_code: string;
|
|
||||||
taxable_amount: InvoiceAmount;
|
|
||||||
taxes_amount: InvoiceAmount;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
discountPercentage: Percentage;
|
discountPercentage: Percentage;
|
||||||
|
|
||||||
@ -108,23 +103,7 @@ export class CustomerInvoiceListMapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3) Taxes
|
// 3) Taxes
|
||||||
const taxes = raw.taxes.map((tax) => {
|
const taxes = raw.taxes.map((tax) => tax.tax_code).join(", ");
|
||||||
const taxableAmount = ItemAmount.create({
|
|
||||||
value: tax.taxable_amount_value || 0,
|
|
||||||
currency_code: attributes.currencyCode!.code,
|
|
||||||
}).data;
|
|
||||||
|
|
||||||
const taxesAmount = ItemAmount.create({
|
|
||||||
value: tax.taxes_amount_value || 0,
|
|
||||||
currency_code: attributes.currencyCode!.code,
|
|
||||||
}).data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
tax_code: tax.tax_code,
|
|
||||||
taxable_amount: taxableAmount,
|
|
||||||
taxes_amount: taxesAmount,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4) Verifactu record
|
// 4) Verifactu record
|
||||||
let verifactu: Maybe<VerifactuRecord> = Maybe.none();
|
let verifactu: Maybe<VerifactuRecord> = Maybe.none();
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import type {
|
|||||||
|
|
||||||
import { CustomerInvoiceModel } from "./models/customer-invoice.model";
|
import { CustomerInvoiceModel } from "./models/customer-invoice.model";
|
||||||
import { CustomerInvoiceItemModel } from "./models/customer-invoice-item.model";
|
import { CustomerInvoiceItemModel } from "./models/customer-invoice-item.model";
|
||||||
|
import { CustomerInvoiceItemTaxModel } from "./models/customer-invoice-item-tax.model";
|
||||||
import { CustomerInvoiceTaxModel } from "./models/customer-invoice-tax.model";
|
import { CustomerInvoiceTaxModel } from "./models/customer-invoice-tax.model";
|
||||||
import { VerifactuRecordModel } from "./models/verifactu-record.model";
|
import { VerifactuRecordModel } from "./models/verifactu-record.model";
|
||||||
|
|
||||||
@ -101,7 +102,12 @@ export class CustomerInvoiceRepository
|
|||||||
// 3. Inserta items + sus taxes
|
// 3. Inserta items + sus taxes
|
||||||
if (Array.isArray(items) && items.length > 0) {
|
if (Array.isArray(items) && items.length > 0) {
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
await CustomerInvoiceItemModel.create(item, { transaction });
|
const { taxes: itemTaxes, ...itemData } = item;
|
||||||
|
await CustomerInvoiceItemModel.create(itemData, { transaction });
|
||||||
|
|
||||||
|
if (Array.isArray(itemTaxes) && itemTaxes.length > 0) {
|
||||||
|
await CustomerInvoiceItemTaxModel.bulkCreate(itemTaxes, { transaction });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +171,12 @@ export class CustomerInvoiceRepository
|
|||||||
// 4. Inserta items + sus taxes
|
// 4. Inserta items + sus taxes
|
||||||
if (Array.isArray(items) && items.length > 0) {
|
if (Array.isArray(items) && items.length > 0) {
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
await CustomerInvoiceItemModel.create(item, { transaction });
|
const { taxes: itemTaxes, ...itemData } = item;
|
||||||
|
await CustomerInvoiceItemModel.create(itemData, { transaction });
|
||||||
|
|
||||||
|
if (Array.isArray(itemTaxes) && itemTaxes.length > 0) {
|
||||||
|
await CustomerInvoiceItemTaxModel.bulkCreate(itemTaxes, { transaction });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,6 +276,13 @@ export class CustomerInvoiceRepository
|
|||||||
model: CustomerInvoiceItemModel,
|
model: CustomerInvoiceItemModel,
|
||||||
as: "items",
|
as: "items",
|
||||||
required: false,
|
required: false,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: CustomerInvoiceItemTaxModel,
|
||||||
|
as: "taxes",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: CustomerInvoiceTaxModel,
|
model: CustomerInvoiceTaxModel,
|
||||||
@ -353,6 +371,13 @@ export class CustomerInvoiceRepository
|
|||||||
model: CustomerInvoiceItemModel,
|
model: CustomerInvoiceItemModel,
|
||||||
as: "items",
|
as: "items",
|
||||||
required: false,
|
required: false,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: CustomerInvoiceItemTaxModel,
|
||||||
|
as: "taxes",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: CustomerInvoiceTaxModel,
|
model: CustomerInvoiceTaxModel,
|
||||||
@ -459,6 +484,7 @@ export class CustomerInvoiceRepository
|
|||||||
as: "taxes",
|
as: "taxes",
|
||||||
required: false,
|
required: false,
|
||||||
separate: true, // => query aparte, devuelve siempre array
|
separate: true, // => query aparte, devuelve siempre array
|
||||||
|
attributes: ["tax_id", "tax_code"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -575,6 +601,7 @@ export class CustomerInvoiceRepository
|
|||||||
as: "taxes",
|
as: "taxes",
|
||||||
required: false,
|
required: false,
|
||||||
separate: true, // => query aparte, devuelve siempre array
|
separate: true, // => query aparte, devuelve siempre array
|
||||||
|
attributes: ["tax_id", "tax_code"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import customerInvoiceModelInit from "./models/customer-invoice.model";
|
import customerInvoiceModelInit from "./models/customer-invoice.model";
|
||||||
import customerInvoiceItemModelInit from "./models/customer-invoice-item.model";
|
import customerInvoiceItemModelInit from "./models/customer-invoice-item.model";
|
||||||
|
import customerInvoiceItemTaxesModelInit from "./models/customer-invoice-item-tax.model";
|
||||||
import customerInvoiceTaxesModelInit from "./models/customer-invoice-tax.model";
|
import customerInvoiceTaxesModelInit from "./models/customer-invoice-tax.model";
|
||||||
import verifactuRecordModelInit from "./models/verifactu-record.model";
|
import verifactuRecordModelInit from "./models/verifactu-record.model";
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ export const models = [
|
|||||||
customerInvoiceItemModelInit,
|
customerInvoiceItemModelInit,
|
||||||
|
|
||||||
customerInvoiceTaxesModelInit,
|
customerInvoiceTaxesModelInit,
|
||||||
|
customerInvoiceItemTaxesModelInit,
|
||||||
|
|
||||||
verifactuRecordModelInit,
|
verifactuRecordModelInit,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
DataTypes,
|
||||||
|
type InferAttributes,
|
||||||
|
type InferCreationAttributes,
|
||||||
|
Model,
|
||||||
|
type NonAttribute,
|
||||||
|
type Sequelize,
|
||||||
|
} from "sequelize";
|
||||||
|
|
||||||
|
import type { CustomerInvoiceItem } from "../../../domain";
|
||||||
|
|
||||||
|
export type CustomerInvoiceItemTaxCreationAttributes = InferCreationAttributes<
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
{ omit: "item" }
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class CustomerInvoiceItemTaxModel extends Model<
|
||||||
|
InferAttributes<CustomerInvoiceItemTaxModel>,
|
||||||
|
InferCreationAttributes<CustomerInvoiceItemTaxModel>
|
||||||
|
> {
|
||||||
|
declare tax_id: string;
|
||||||
|
declare item_id: string;
|
||||||
|
|
||||||
|
declare tax_code: string; //"iva_21"
|
||||||
|
|
||||||
|
// Taxable amount (base imponible) // 100,00 €
|
||||||
|
declare taxable_amount_value: number;
|
||||||
|
declare taxable_amount_scale: number;
|
||||||
|
|
||||||
|
// Total tax amount / taxes total // 21,00 €
|
||||||
|
declare taxes_amount_value: number;
|
||||||
|
declare taxes_amount_scale: number;
|
||||||
|
|
||||||
|
// Relaciones
|
||||||
|
declare item: NonAttribute<CustomerInvoiceItem>;
|
||||||
|
|
||||||
|
static associate(database: Sequelize) {
|
||||||
|
const models = database.models;
|
||||||
|
|
||||||
|
const requiredModels = ["CustomerInvoiceItemModel"];
|
||||||
|
|
||||||
|
// Comprobamos que los modelos existan
|
||||||
|
for (const name of requiredModels) {
|
||||||
|
if (!models[name]) {
|
||||||
|
throw new Error(`[CustomerInvoiceItemTaxModel.associate] Missing model: ${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { CustomerInvoiceItemModel } = models;
|
||||||
|
|
||||||
|
CustomerInvoiceItemTaxModel.belongsTo(CustomerInvoiceItemModel, {
|
||||||
|
as: "item",
|
||||||
|
targetKey: "item_id",
|
||||||
|
foreignKey: "item_id",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "CASCADE",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static hooks(_database: Sequelize) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (database: Sequelize) => {
|
||||||
|
CustomerInvoiceItemTaxModel.init(
|
||||||
|
{
|
||||||
|
tax_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
item_id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
tax_code: {
|
||||||
|
type: new DataTypes.STRING(),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
taxable_amount_value: {
|
||||||
|
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
taxable_amount_scale: {
|
||||||
|
type: new DataTypes.SMALLINT(),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 4,
|
||||||
|
},
|
||||||
|
|
||||||
|
taxes_amount_value: {
|
||||||
|
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
taxes_amount_scale: {
|
||||||
|
type: new DataTypes.SMALLINT(),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize: database,
|
||||||
|
modelName: "CustomerInvoiceItemTaxModel",
|
||||||
|
tableName: "customer_invoice_item_taxes",
|
||||||
|
|
||||||
|
underscored: true,
|
||||||
|
|
||||||
|
indexes: [],
|
||||||
|
|
||||||
|
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||||
|
|
||||||
|
defaultScope: {},
|
||||||
|
|
||||||
|
scopes: {},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return CustomerInvoiceItemTaxModel;
|
||||||
|
};
|
||||||
@ -9,15 +9,21 @@ import {
|
|||||||
} from "sequelize";
|
} from "sequelize";
|
||||||
|
|
||||||
import type { CustomerInvoiceModel } from "./customer-invoice.model";
|
import type { CustomerInvoiceModel } from "./customer-invoice.model";
|
||||||
|
import type {
|
||||||
|
CustomerInvoiceItemTaxCreationAttributes,
|
||||||
|
CustomerInvoiceItemTaxModel,
|
||||||
|
} from "./customer-invoice-item-tax.model";
|
||||||
|
|
||||||
export type CustomerInvoiceItemCreationAttributes = InferCreationAttributes<
|
export type CustomerInvoiceItemCreationAttributes = InferCreationAttributes<
|
||||||
CustomerInvoiceItemModel,
|
CustomerInvoiceItemModel,
|
||||||
{ omit: "invoice" }
|
{ omit: "invoice" | "taxes" }
|
||||||
>;
|
> & {
|
||||||
|
taxes?: CustomerInvoiceItemTaxCreationAttributes[];
|
||||||
|
};
|
||||||
|
|
||||||
export class CustomerInvoiceItemModel extends Model<
|
export class CustomerInvoiceItemModel extends Model<
|
||||||
InferAttributes<CustomerInvoiceItemModel>,
|
InferAttributes<CustomerInvoiceItemModel>,
|
||||||
InferCreationAttributes<CustomerInvoiceItemModel, { omit: "invoice" }>
|
InferCreationAttributes<CustomerInvoiceItemModel, { omit: "invoice" | "taxes" }>
|
||||||
> {
|
> {
|
||||||
declare item_id: string;
|
declare item_id: string;
|
||||||
declare invoice_id: string;
|
declare invoice_id: string;
|
||||||
@ -48,39 +54,6 @@ export class CustomerInvoiceItemModel extends Model<
|
|||||||
declare taxable_amount_value: CreationOptional<number | null>;
|
declare taxable_amount_value: CreationOptional<number | null>;
|
||||||
declare taxable_amount_scale: number;
|
declare taxable_amount_scale: number;
|
||||||
|
|
||||||
// Código de impuestos
|
|
||||||
|
|
||||||
// IVA percentage
|
|
||||||
declare iva_code: CreationOptional<string | null>;
|
|
||||||
|
|
||||||
declare iva_percentage_value: CreationOptional<number | null>;
|
|
||||||
declare iva_percentage_scale: number;
|
|
||||||
|
|
||||||
// IVA amount
|
|
||||||
|
|
||||||
declare iva_amount_value: CreationOptional<number | null>;
|
|
||||||
declare iva_amount_scale: number;
|
|
||||||
|
|
||||||
// Recargo de equivalencia percentage
|
|
||||||
declare rec_code: CreationOptional<string | null>;
|
|
||||||
|
|
||||||
declare rec_percentage_value: CreationOptional<number | null>;
|
|
||||||
declare rec_percentage_scale: number;
|
|
||||||
|
|
||||||
// Recargo de equivalencia amount
|
|
||||||
declare rec_amount_value: CreationOptional<number | null>;
|
|
||||||
declare rec_amount_scale: number;
|
|
||||||
|
|
||||||
// Retention percentage
|
|
||||||
declare retention_code: CreationOptional<string | null>;
|
|
||||||
|
|
||||||
declare retention_percentage_value: CreationOptional<number | null>;
|
|
||||||
declare retention_percentage_scale: number;
|
|
||||||
|
|
||||||
// Retention amount
|
|
||||||
declare retention_amount_value: CreationOptional<number | null>;
|
|
||||||
declare retention_amount_scale: number;
|
|
||||||
|
|
||||||
// Total taxes amount / taxes total
|
// Total taxes amount / taxes total
|
||||||
declare taxes_amount_value: CreationOptional<number | null>;
|
declare taxes_amount_value: CreationOptional<number | null>;
|
||||||
declare taxes_amount_scale: number;
|
declare taxes_amount_scale: number;
|
||||||
@ -91,10 +64,15 @@ export class CustomerInvoiceItemModel extends Model<
|
|||||||
|
|
||||||
// Relaciones
|
// Relaciones
|
||||||
declare invoice: NonAttribute<CustomerInvoiceModel>;
|
declare invoice: NonAttribute<CustomerInvoiceModel>;
|
||||||
|
declare taxes: NonAttribute<CustomerInvoiceItemTaxModel[]>;
|
||||||
|
|
||||||
static associate(database: Sequelize) {
|
static associate(database: Sequelize) {
|
||||||
const models = database.models;
|
const models = database.models;
|
||||||
const requiredModels = ["CustomerInvoiceModel", "CustomerInvoiceItemModel"];
|
const requiredModels = [
|
||||||
|
"CustomerInvoiceModel",
|
||||||
|
"CustomerInvoiceItemModel",
|
||||||
|
"CustomerInvoiceItemTaxModel",
|
||||||
|
];
|
||||||
|
|
||||||
// Comprobamos que los modelos existan
|
// Comprobamos que los modelos existan
|
||||||
for (const name of requiredModels) {
|
for (const name of requiredModels) {
|
||||||
@ -103,7 +81,7 @@ export class CustomerInvoiceItemModel extends Model<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { CustomerInvoiceModel, CustomerInvoiceItemModel } = models;
|
const { CustomerInvoiceModel, CustomerInvoiceItemModel, CustomerInvoiceItemTaxModel } = models;
|
||||||
|
|
||||||
CustomerInvoiceItemModel.belongsTo(CustomerInvoiceModel, {
|
CustomerInvoiceItemModel.belongsTo(CustomerInvoiceModel, {
|
||||||
as: "invoice",
|
as: "invoice",
|
||||||
@ -112,6 +90,15 @@ export class CustomerInvoiceItemModel extends Model<
|
|||||||
onDelete: "CASCADE",
|
onDelete: "CASCADE",
|
||||||
onUpdate: "CASCADE",
|
onUpdate: "CASCADE",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CustomerInvoiceItemModel.hasMany(CustomerInvoiceItemTaxModel, {
|
||||||
|
as: "taxes",
|
||||||
|
foreignKey: "item_id",
|
||||||
|
sourceKey: "item_id",
|
||||||
|
constraints: true,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "CASCADE",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,93 +199,6 @@ export default (database: Sequelize) => {
|
|||||||
defaultValue: 4,
|
defaultValue: 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
// IVA %
|
|
||||||
|
|
||||||
iva_code: {
|
|
||||||
type: DataTypes.STRING(40),
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
iva_percentage_value: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
iva_percentage_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
iva_amount_value: {
|
|
||||||
type: DataTypes.BIGINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
iva_amount_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
// REC %
|
|
||||||
rec_code: {
|
|
||||||
type: DataTypes.STRING(40),
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
rec_percentage_value: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
rec_percentage_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
rec_amount_value: {
|
|
||||||
type: DataTypes.BIGINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
rec_amount_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Retención %
|
|
||||||
retention_code: {
|
|
||||||
type: DataTypes.STRING(40),
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
retention_percentage_value: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
retention_percentage_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 2,
|
|
||||||
},
|
|
||||||
|
|
||||||
retention_amount_value: {
|
|
||||||
type: DataTypes.BIGINT,
|
|
||||||
allowNull: true,
|
|
||||||
defaultValue: null,
|
|
||||||
},
|
|
||||||
retention_amount_scale: {
|
|
||||||
type: DataTypes.SMALLINT,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: 4,
|
|
||||||
},
|
|
||||||
|
|
||||||
taxes_amount_value: {
|
taxes_amount_value: {
|
||||||
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
type: new DataTypes.BIGINT(), // importante: evita problemas de precisión con valores grandes
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
@ -330,7 +230,7 @@ export default (database: Sequelize) => {
|
|||||||
|
|
||||||
underscored: true,
|
underscored: true,
|
||||||
|
|
||||||
indexes: [{ fields: ["invoice_id"] }, { fields: ["invoice_id", "position"] }],
|
indexes: [],
|
||||||
|
|
||||||
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
whereMergeStrategy: "and", // <- cómo tratar el merge de un scope
|
||||||
|
|
||||||
|
|||||||
@ -49,10 +49,9 @@ export class CustomerInvoiceTaxModel extends Model<
|
|||||||
|
|
||||||
CustomerInvoiceTaxModel.belongsTo(CustomerInvoiceModel, {
|
CustomerInvoiceTaxModel.belongsTo(CustomerInvoiceModel, {
|
||||||
as: "invoice",
|
as: "invoice",
|
||||||
foreignKey: "invoice_id",
|
|
||||||
targetKey: "id",
|
targetKey: "id",
|
||||||
|
foreignKey: "invoice_id",
|
||||||
onDelete: "CASCADE",
|
onDelete: "CASCADE",
|
||||||
onUpdate: "CASCADE",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ export default (database: Sequelize) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
tax_code: {
|
tax_code: {
|
||||||
type: new DataTypes.STRING(40), // Sugerido por IA
|
type: new DataTypes.STRING(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -114,11 +113,7 @@ export default (database: Sequelize) => {
|
|||||||
{
|
{
|
||||||
name: "invoice_id_idx",
|
name: "invoice_id_idx",
|
||||||
fields: ["invoice_id"],
|
fields: ["invoice_id"],
|
||||||
},
|
unique: false,
|
||||||
{
|
|
||||||
name: "invoice_tax_code_unique",
|
|
||||||
fields: ["invoice_id", "tax_code"],
|
|
||||||
unique: true, // cada impuesto aparece como máximo una vez
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export * from "./customer-invoice.model";
|
export * from "./customer-invoice.model";
|
||||||
export * from "./customer-invoice-item.model";
|
export * from "./customer-invoice-item.model";
|
||||||
|
export * from "./customer-invoice-item-tax.model";
|
||||||
export * from "./customer-invoice-tax.model";
|
export * from "./customer-invoice-tax.model";
|
||||||
export * from "./verifactu-record.model";
|
export * from "./verifactu-record.model";
|
||||||
|
|||||||
@ -38,13 +38,7 @@ export const ListIssuedInvoicesResponseSchema = createPaginatedListSchema(
|
|||||||
country: z.string(),
|
country: z.string(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
taxes: z.array(
|
taxes: z.string(),
|
||||||
z.object({
|
|
||||||
tax_code: z.string(),
|
|
||||||
taxable_amount: MoneySchema,
|
|
||||||
taxes_amount: MoneySchema,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
|
|
||||||
subtotal_amount: MoneySchema,
|
subtotal_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
discount_percentage: PercentageSchema,
|
||||||
|
|||||||
@ -38,13 +38,7 @@ export const ListProformasResponseSchema = createPaginatedListSchema(
|
|||||||
country: z.string(),
|
country: z.string(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
taxes: z.array(
|
taxes: z.string(),
|
||||||
z.object({
|
|
||||||
tax_code: z.string(),
|
|
||||||
taxable_amount: MoneySchema,
|
|
||||||
taxes_amount: MoneySchema,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
|
|
||||||
subtotal_amount: MoneySchema,
|
subtotal_amount: MoneySchema,
|
||||||
discount_percentage: PercentageSchema,
|
discount_percentage: PercentageSchema,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import type {
|
|||||||
*/
|
*/
|
||||||
export const IssuedInvoiceSummaryDtoAdapter = {
|
export const IssuedInvoiceSummaryDtoAdapter = {
|
||||||
fromDto(pageDto: IssuedInvoiceSummaryPage, context?: unknown): IssuedInvoiceSummaryPageData {
|
fromDto(pageDto: IssuedInvoiceSummaryPage, context?: unknown): IssuedInvoiceSummaryPageData {
|
||||||
|
console.log(pageDto);
|
||||||
return {
|
return {
|
||||||
...pageDto,
|
...pageDto,
|
||||||
items: pageDto.items.map(
|
items: pageDto.items.map(
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export function useIssuedInvoicesGridColumns(
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const { verifactu } = row.original;
|
const { verifactu } = row.original;
|
||||||
const isPending = verifactu.status === "Pendiente";
|
const isPending = verifactu.status === "Pendiente";
|
||||||
|
console.log(verifactu.status);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
@ -331,11 +331,12 @@ export function useIssuedInvoicesGridColumns(
|
|||||||
return (
|
return (
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
{/* Descargar en PDF */}
|
{/* Descargar en PDF */}
|
||||||
|
{/* Descargar en PDF */}
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
className={"size-8"}
|
className="size-8"
|
||||||
disabled={isPDFLoading || !isCompleted}
|
disabled={isPDFLoading || !isCompleted}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
stop(e);
|
stop(e);
|
||||||
@ -345,9 +346,9 @@ export function useIssuedInvoicesGridColumns(
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{isPDFLoading ? (
|
{isPDFLoading ? (
|
||||||
<Spinner className="size-4 cursor-progress" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<FileDownIcon className="size-4 cursor-pointer" />
|
<FileDownIcon className="size-4" />
|
||||||
)}
|
)}
|
||||||
<span className="sr-only">Descargar PDF</span>
|
<span className="sr-only">Descargar PDF</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user