v0.6.0 - Subida a producción
This commit is contained in:
parent
67dc91eced
commit
330240deaa
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "WEB: Vite (Chrome)",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/factuges-server",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --config tsup.config.ts",
|
||||
@ -37,8 +37,6 @@
|
||||
"@erp/core": "workspace:*",
|
||||
"@erp/customer-invoices": "workspace:*",
|
||||
"@erp/customers": "workspace:*",
|
||||
"@erp/factuges": "workspace:*",
|
||||
"@erp/suppliers": "workspace:*",
|
||||
"@repo/rdx-logger": "workspace:*",
|
||||
"@repo/rdx-utils": "workspace:*",
|
||||
"bcrypt": "^6.0.0",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import customerInvoicesAPIModule from "@erp/customer-invoices/api";
|
||||
import customersAPIModule from "@erp/customers/api";
|
||||
import factuGESAPIModule from "@erp/factuges/api";
|
||||
import suppliersAPIModule from "@erp/suppliers/api";
|
||||
|
||||
//import suppliersAPIModule from "@erp/suppliers/api";
|
||||
|
||||
import { registerModule } from "./lib";
|
||||
|
||||
@ -9,6 +9,5 @@ export const registerModules = () => {
|
||||
//registerModule(authAPIModule);
|
||||
registerModule(customersAPIModule);
|
||||
registerModule(customerInvoicesAPIModule);
|
||||
registerModule(factuGESAPIModule);
|
||||
registerModule(suppliersAPIModule);
|
||||
//registerModule(suppliersAPIModule);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/factuges-web",
|
||||
"private": true,
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host --clearScreen false",
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/auth",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/auth/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/core",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
export const formatDate = (value: string) => {
|
||||
return new Date(value).toLocaleDateString();
|
||||
};
|
||||
@ -1,3 +1,2 @@
|
||||
export * from "./date-func";
|
||||
export * from "./form-utils";
|
||||
export * from "./http-url-utils";
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/core/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customer-invoices",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import type { Maybe, Result } from "@repo/rdx-utils";
|
||||
import type { Result } from "@repo/rdx-utils";
|
||||
|
||||
import type { InvoiceNumber, InvoiceSerie } from "../../../domain";
|
||||
|
||||
@ -13,7 +13,7 @@ export interface IIssuedInvoiceNumberGenerator {
|
||||
*/
|
||||
getNextForCompany(
|
||||
companyId: UniqueID,
|
||||
series: Maybe<InvoiceSerie>,
|
||||
series: InvoiceSerie,
|
||||
transaction: unknown
|
||||
): Promise<Result<InvoiceNumber, Error>>;
|
||||
}
|
||||
|
||||
@ -98,15 +98,15 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
||||
companyId: proforma.companyId,
|
||||
status: InvoiceStatus.issued(),
|
||||
|
||||
series: proforma.series,
|
||||
proformaId: proforma.id,
|
||||
series: proforma.series.getOrUndefined()!,
|
||||
linkedProformaId: proforma.id,
|
||||
|
||||
invoiceNumber: proforma.invoiceNumber,
|
||||
|
||||
invoiceDate: UtcDate.today(), // La fecha de la factura es la fecha de emisión, no la de la proforma
|
||||
operationDate: proforma.operationDate,
|
||||
|
||||
description: proforma.description,
|
||||
description: proforma.description.getOrUndefined()!,
|
||||
|
||||
languageCode: proforma.languageCode,
|
||||
currencyCode: proforma.currencyCode,
|
||||
@ -116,7 +116,7 @@ export class ProformaToIssuedInvoiceConverter implements IProformaToIssuedInvoic
|
||||
paymentMethod: proforma.paymentMethod,
|
||||
|
||||
customerId: proforma.customerId,
|
||||
recipient: proforma.recipient,
|
||||
recipient: proforma.recipient.getOrUndefined()!,
|
||||
|
||||
items: issuedItems,
|
||||
|
||||
|
||||
@ -53,9 +53,7 @@ export class IssuedInvoiceFullSnapshotBuilder implements IIssuedInvoiceFullSnaps
|
||||
customer_id: invoice.customerId.toString(),
|
||||
recipient,
|
||||
|
||||
linked_proforma_id: maybeToNullable(invoice.linkedProformaId, (linkedId) =>
|
||||
linkedId.toString()
|
||||
),
|
||||
linked_proforma_id: invoice.linkedProformaId.toString(),
|
||||
|
||||
taxes,
|
||||
|
||||
|
||||
@ -1,2 +1 @@
|
||||
export * from "./proforma-summary-snapshot.interface";
|
||||
export * from "./proforma-summary-snapshot-builder";
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
import type { ISnapshotBuilder } from "@erp/core/api";
|
||||
import { maybeToEmptyString, maybeToNullable } from "@repo/rdx-ddd";
|
||||
|
||||
import type { ProformaSummaryDTO } from "../../../../../common";
|
||||
import type { ProformaSummary } from "../../models";
|
||||
|
||||
import type { IProformaSummarySnapshot } from "./proforma-summary-snapshot.interface";
|
||||
|
||||
export interface IProformaSummarySnapshotBuilder
|
||||
extends ISnapshotBuilder<ProformaSummary, IProformaSummarySnapshot> {}
|
||||
extends ISnapshotBuilder<ProformaSummary, ProformaSummaryDTO> {}
|
||||
|
||||
export class ProformaSummarySnapshotBuilder implements IProformaSummarySnapshotBuilder {
|
||||
toOutput(proforma: ProformaSummary): IProformaSummarySnapshot {
|
||||
toOutput(proforma: ProformaSummary): ProformaSummaryDTO {
|
||||
const recipient = proforma.recipient.toObjectString();
|
||||
|
||||
return {
|
||||
@ -18,7 +17,7 @@ export class ProformaSummarySnapshotBuilder implements IProformaSummarySnapshotB
|
||||
is_proforma: proforma.isProforma,
|
||||
|
||||
invoice_number: proforma.invoiceNumber.toString(),
|
||||
status: proforma.status.toPrimitive(),
|
||||
status: proforma.status.toPrimitive() as ProformaSummaryDTO["status"],
|
||||
series: maybeToNullable(proforma.series, (value) => value.toString()),
|
||||
|
||||
invoice_date: proforma.invoiceDate.toDateString(),
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Fijarse en ListProformasResponseDTO["items"]
|
||||
*/
|
||||
export interface IProformaSummarySnapshot {
|
||||
id: string;
|
||||
company_id: string;
|
||||
is_proforma: boolean;
|
||||
|
||||
invoice_number: string;
|
||||
status: string;
|
||||
series: string | null;
|
||||
|
||||
invoice_date: string;
|
||||
operation_date: string | null;
|
||||
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
|
||||
reference: string | null;
|
||||
description: string | null;
|
||||
|
||||
customer_id: string;
|
||||
recipient: {
|
||||
id: string;
|
||||
tin: string;
|
||||
name: string;
|
||||
street: string | null;
|
||||
street2: string | null;
|
||||
city: string | null;
|
||||
postal_code: string | null;
|
||||
province: string | null;
|
||||
country: string | null;
|
||||
};
|
||||
|
||||
subtotal_amount: { value: string; scale: string; currency_code: string };
|
||||
total_discount_amount: { value: string; scale: string; currency_code: string };
|
||||
taxable_amount: { value: string; scale: string; currency_code: string };
|
||||
taxes_amount: { value: string; scale: string; currency_code: string };
|
||||
total_amount: { value: string; scale: string; currency_code: string };
|
||||
|
||||
linked_invoice_id: string | null;
|
||||
}
|
||||
@ -32,7 +32,7 @@ export interface IIssuedInvoiceCreateProps {
|
||||
companyId: UniqueID;
|
||||
status: InvoiceStatus;
|
||||
|
||||
linkedProformaId: Maybe<UniqueID>; // <- id de la proforma padre en caso de issue
|
||||
linkedProformaId: UniqueID; // <- id de la proforma padre en caso de issue
|
||||
|
||||
series: InvoiceSerie;
|
||||
invoiceNumber: InvoiceNumber;
|
||||
@ -178,7 +178,7 @@ export class IssuedInvoice
|
||||
return this.props.customerId;
|
||||
}
|
||||
|
||||
public get linkedProformaId(): Maybe<UniqueID> {
|
||||
public get linkedProformaId(): UniqueID {
|
||||
return this.props.linkedProformaId;
|
||||
}
|
||||
|
||||
|
||||
@ -304,6 +304,97 @@ export class Proforma extends AggregateRoot<ProformaInternalProps> implements IP
|
||||
);
|
||||
}
|
||||
|
||||
if (this.series.isNone()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_SERIES",
|
||||
"series",
|
||||
"Series is required to issue the proforma"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.description.isNone()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_DESCRIPTION",
|
||||
"description",
|
||||
"Description is required to issue the proforma"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.recipient.isNone()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_RECIPIENT",
|
||||
"recipient",
|
||||
"Recipient is required to issue the proforma"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.items.size() === 0) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"NO_ITEMS",
|
||||
"items",
|
||||
"At least one item is required to issue the proforma"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const invalidItem = this.items.find((item) => !item.isValued());
|
||||
if (invalidItem) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"INVALID_ITEM",
|
||||
"items",
|
||||
`Item at position ${invalidItem.id} is not valid for invoicing`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.paymentMethod.isNone()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"MISSING_PAYMENT_METHOD",
|
||||
"paymentMethod",
|
||||
"Payment method is required to issue the proforma"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/*if (this.operationDate.isSome() && this.operationDate.unwrap() > new Date()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"INVALID_OPERATION_DATE",
|
||||
"operationDate",
|
||||
"Operation date cannot be in the future"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.operationDate.isSome() && this.operationDate.unwrap() < this.invoiceDate) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"INVALID_OPERATION_DATE",
|
||||
"operationDate",
|
||||
"Operation date cannot be before invoice date"
|
||||
)
|
||||
);
|
||||
}*/
|
||||
|
||||
if (this.linkedInvoiceId.isSome()) {
|
||||
return Result.fail(
|
||||
new DomainValidationError(
|
||||
"LINKED_INVOICE_NOT_ALLOWED",
|
||||
"linkedInvoiceId",
|
||||
"Proforma cannot be linked to an invoice"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.props.status = InvoiceStatus.issued();
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||
const customerId = extractOrPushError(UniqueID.create(raw.customer_id), "customer_id", errors);
|
||||
|
||||
const linkedProformaId = extractOrPushError(
|
||||
maybeFromNullableResult(raw.proforma_id, (v) => UniqueID.create(String(v))),
|
||||
UniqueID.create(String(raw.proforma_id)),
|
||||
"proforma_id",
|
||||
errors
|
||||
);
|
||||
@ -435,6 +435,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||
}
|
||||
|
||||
// 3) Cliente
|
||||
console.debug(source.recipient);
|
||||
const recipient = this._recipientMapper.mapToPersistence(source.recipient, {
|
||||
errors,
|
||||
parent: source,
|
||||
@ -450,6 +451,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||
|
||||
// 5) Si hubo errores de mapeo, devolvemos colección de validación
|
||||
if (errors.length > 0) {
|
||||
console.error("Errors mapping issued invoice to persistence:", errors);
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice mapping to persistence failed", errors)
|
||||
);
|
||||
@ -467,7 +469,7 @@ export class SequelizeIssuedInvoiceDomainMapper extends SequelizeDomainMapper<
|
||||
// Flags / estado / serie / número
|
||||
is_proforma: false,
|
||||
status: source.status.toPrimitive(),
|
||||
proforma_id: maybeToNullable(source.linkedProformaId, (v) => v.toPrimitive()),
|
||||
proforma_id: source.linkedProformaId.toPrimitive(),
|
||||
|
||||
series: source.series.toPrimitive(),
|
||||
invoice_number: source.invoiceNumber.toPrimitive(),
|
||||
|
||||
@ -15,11 +15,7 @@ import {
|
||||
} from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
|
||||
import {
|
||||
type IIssuedInvoiceCreateProps,
|
||||
InvoiceRecipient,
|
||||
type IssuedInvoice,
|
||||
} from "../../../../../../domain";
|
||||
import { type IIssuedInvoiceCreateProps, InvoiceRecipient } from "../../../../../../domain";
|
||||
import type { CustomerInvoiceModel } from "../../../../../common";
|
||||
|
||||
export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
||||
@ -118,27 +114,10 @@ export class SequelizeIssuedInvoiceRecipientDomainMapper {
|
||||
* En caso contrario, se agrega un error de validación.
|
||||
*/
|
||||
mapToPersistence(source: InvoiceRecipient, params?: MapperParamsType) {
|
||||
const { errors, parent } = params as {
|
||||
/*const { errors, parent } = params as {
|
||||
parent: IssuedInvoice;
|
||||
errors: ValidationErrorDetail[];
|
||||
};
|
||||
|
||||
const { hasRecipient } = parent;
|
||||
|
||||
// Validación: facturas emitidas deben tener destinatario.
|
||||
if (!hasRecipient) {
|
||||
errors.push({
|
||||
path: "recipient",
|
||||
message: "[InvoiceRecipientDomainMapper] Issued customer invoice without recipient data",
|
||||
});
|
||||
}
|
||||
|
||||
// Si hay errores previos, devolvemos fallo de validación inmediatamente.
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer invoice mapping to persistence failed", errors)
|
||||
);
|
||||
}
|
||||
};*/
|
||||
|
||||
const recipient = source;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { UniqueID } from "@repo/rdx-ddd";
|
||||
import { type Maybe, Result } from "@repo/rdx-utils";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { type Transaction, type WhereOptions, literal } from "sequelize";
|
||||
|
||||
import type { IIssuedInvoiceNumberGenerator } from "../../../../../application/issued-invoices";
|
||||
@ -12,7 +12,7 @@ import { CustomerInvoiceModel } from "../../../../common/persistence";
|
||||
export class SequelizeIssuedInvoiceNumberGenerator implements IIssuedInvoiceNumberGenerator {
|
||||
public async getNextForCompany(
|
||||
companyId: UniqueID,
|
||||
series: Maybe<InvoiceSerie>,
|
||||
series: InvoiceSerie,
|
||||
transaction: Transaction
|
||||
): Promise<Result<InvoiceNumber, Error>> {
|
||||
const where: WhereOptions = {
|
||||
@ -20,14 +20,7 @@ export class SequelizeIssuedInvoiceNumberGenerator implements IIssuedInvoiceNumb
|
||||
is_proforma: false,
|
||||
};
|
||||
|
||||
series.match(
|
||||
(serieVO) => {
|
||||
where.series = serieVO.toString();
|
||||
},
|
||||
() => {
|
||||
where.series = null;
|
||||
}
|
||||
);
|
||||
where.series = series.toString();
|
||||
|
||||
try {
|
||||
const lastInvoice = await CustomerInvoiceModel.findOne({
|
||||
|
||||
@ -1,46 +1,8 @@
|
||||
import {
|
||||
CurrencyCodeSchema,
|
||||
IsoDateSchema,
|
||||
LanguageCodeSchema,
|
||||
MoneySchema,
|
||||
PercentageSchema,
|
||||
createPaginatedListSchema,
|
||||
} from "@erp/core";
|
||||
import { z } from "zod/v4";
|
||||
import { createPaginatedListSchema } from "@erp/core";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import { ProformaRecipientSummarySchema, ProformaStatusSchema } from "../../shared/proforma";
|
||||
import { ProformaSummarySchema } from "../../shared/proforma";
|
||||
|
||||
export const ListProformasResponseSchema = createPaginatedListSchema(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
company_id: z.uuid(),
|
||||
is_proforma: z.boolean(),
|
||||
|
||||
invoice_number: z.string(),
|
||||
status: ProformaStatusSchema,
|
||||
series: z.string().nullable(),
|
||||
|
||||
invoice_date: IsoDateSchema,
|
||||
operation_date: IsoDateSchema.nullable(),
|
||||
|
||||
language_code: LanguageCodeSchema,
|
||||
currency_code: CurrencyCodeSchema,
|
||||
|
||||
reference: z.string().nullable(),
|
||||
description: z.string().nullable(),
|
||||
|
||||
customer_id: z.uuid(),
|
||||
recipient: ProformaRecipientSummarySchema,
|
||||
|
||||
subtotal_amount: MoneySchema,
|
||||
discount_percentage: PercentageSchema,
|
||||
discount_amount: MoneySchema,
|
||||
taxable_amount: MoneySchema,
|
||||
taxes_amount: MoneySchema,
|
||||
total_amount: MoneySchema,
|
||||
|
||||
linked_invoice_id: z.uuid().nullable(),
|
||||
})
|
||||
);
|
||||
export const ListProformasResponseSchema = createPaginatedListSchema(ProformaSummarySchema);
|
||||
|
||||
export type ListProformasResponseDTO = z.infer<typeof ListProformasResponseSchema>;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from "./proforma-item-detail.dto";
|
||||
export * from "./proforma-recipient-summary.dto";
|
||||
export * from "./proforma-status.dto";
|
||||
export * from "./proforma-summary.dto";
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import { CurrencyCodeSchema, IsoDateSchema, LanguageCodeSchema, MoneySchema } from "@erp/core";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { ProformaRecipientSummarySchema } from "./proforma-recipient-summary.dto";
|
||||
import { ProformaStatusSchema } from "./proforma-status.dto";
|
||||
|
||||
export const ProformaSummarySchema = z.object({
|
||||
id: z.uuid(),
|
||||
company_id: z.uuid(),
|
||||
is_proforma: z.boolean(),
|
||||
|
||||
invoice_number: z.string(),
|
||||
status: ProformaStatusSchema,
|
||||
series: z.string().nullable(),
|
||||
|
||||
invoice_date: IsoDateSchema,
|
||||
operation_date: IsoDateSchema.nullable(),
|
||||
|
||||
language_code: LanguageCodeSchema,
|
||||
currency_code: CurrencyCodeSchema,
|
||||
|
||||
reference: z.string().nullable(),
|
||||
description: z.string().nullable(),
|
||||
|
||||
customer_id: z.uuid(),
|
||||
recipient: ProformaRecipientSummarySchema,
|
||||
|
||||
subtotal_amount: MoneySchema,
|
||||
total_discount_amount: MoneySchema,
|
||||
taxable_amount: MoneySchema,
|
||||
taxes_amount: MoneySchema,
|
||||
total_amount: MoneySchema,
|
||||
|
||||
linked_invoice_id: z.uuid().nullable(),
|
||||
});
|
||||
|
||||
export type ProformaSummaryDTO = z.infer<typeof ProformaSummarySchema>;
|
||||
@ -1,4 +1,4 @@
|
||||
import { formatDate } from "@erp/core/client";
|
||||
import { DateHelper } from "@erp/core";
|
||||
import { ReactQRCode } from "@lglab/react-qr-code";
|
||||
import { DataTableColumnHeader } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
@ -205,7 +205,7 @@ export function useIssuedInvoicesGridColumns(
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium text-left tabular-nums">
|
||||
{formatDate(row.original.invoiceDate)}
|
||||
{DateHelper.format(row.original.invoiceDate)}
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
@ -227,7 +227,7 @@ export function useIssuedInvoicesGridColumns(
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium text-left tabular-nums">
|
||||
{formatDate(row.original.operationDate)}
|
||||
{DateHelper.format(row.original.operationDate)}
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
@ -284,6 +284,27 @@ export function useIssuedInvoicesGridColumns(
|
||||
},
|
||||
},
|
||||
|
||||
// Taxable amount
|
||||
{
|
||||
accessorKey: "taxableAmountFmt",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader
|
||||
className="text-right tabular-nums"
|
||||
column={column}
|
||||
title={t("pages.issued_invoices.list.grid_columns.taxable_amount", "Base imponible")}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium text-right tabular-nums">{row.original.taxableAmountFmt}</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
size: 120,
|
||||
minSize: 100,
|
||||
meta: {
|
||||
title: t("pages.issued_invoices.list.grid_columns.taxable_amount"),
|
||||
},
|
||||
},
|
||||
|
||||
// Taxes amount
|
||||
{
|
||||
accessorKey: "taxes_amount_fmt",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DateHelper } from "@erp/core";
|
||||
import {
|
||||
Button,
|
||||
Tooltip,
|
||||
@ -159,6 +160,10 @@ export function useProformasGridColumns(
|
||||
accessorKey: "series",
|
||||
header: "Serie",
|
||||
},
|
||||
{
|
||||
accessorKey: "reference",
|
||||
header: "Referencia",
|
||||
},
|
||||
{
|
||||
accessorKey: "invoiceDate",
|
||||
header: ({ column }) => {
|
||||
@ -173,6 +178,17 @@ export function useProformasGridColumns(
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium text-left tabular-nums">
|
||||
{DateHelper.format(row.original.invoiceDate)}
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
size: 140,
|
||||
minSize: 120,
|
||||
meta: {
|
||||
title: t("pages.issued_invoices.list.grid_columns.invoice_date"),
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "operationDate",
|
||||
@ -193,30 +209,43 @@ export function useProformasGridColumns(
|
||||
accessorKey: "subtotalAmountFmt",
|
||||
header: () => <div className="text-right">Subtotal</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right tabular-nums">{row.getValue("subtotalAmountFmt")}</div>
|
||||
<div className="text-right tabular-nums font-medium">
|
||||
{row.getValue("subtotalAmountFmt")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "discountAmountFmt",
|
||||
accessorKey: "totalDiscountAmountFmt",
|
||||
header: () => <div className="text-right">Descuentos</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right tabular-nums">{row.getValue("discountAmountFmt")}</div>
|
||||
<div className="text-right tabular-nums font-medium">
|
||||
{row.getValue("totalDiscountAmountFmt")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "taxableAmountFmt",
|
||||
header: () => <div className="text-right">Base imponible</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right tabular-nums font-medium">
|
||||
{row.getValue("taxableAmountFmt")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "taxesAmountFmt",
|
||||
header: () => <div className="text-right">Impuestos</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right tabular-nums">{row.getValue("taxesAmountFmt")}</div>
|
||||
<div className="text-right tabular-nums font-medium">
|
||||
{row.getValue("taxesAmountFmt")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "totalAmountFmt",
|
||||
header: () => <div className="text-right">Importe total</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right tabular-nums font-medium">
|
||||
{row.getValue("totalAmountFmt")}
|
||||
</div>
|
||||
<div className="text-right tabular-nums font-bold">{row.getValue("totalAmountFmt")}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,28 +1,52 @@
|
||||
import { MoneyDTOHelper, PercentageDTOHelper, formatCurrency } from "@erp/core";
|
||||
import { MoneyDTOHelper, formatCurrency } from "@erp/core";
|
||||
|
||||
import type { ListProformasResponseDTO } from "../../../../common";
|
||||
import type { ListProformasResult } from "../api";
|
||||
import type { ProformaList, ProformaListRow, ProformaStatus } from "../entities";
|
||||
|
||||
/**
|
||||
* Adaptador para transformar los datos de la API de ListProformasResponseDTO
|
||||
* a la entidad ProformaList utilizada en la aplicación.
|
||||
* Reglas de adaptación:
|
||||
* - page, per_page, total_pages, total_items se asignan directamente.
|
||||
* - items se transforma utilizando ProformaListRowAdapter para cada elemento.
|
||||
*
|
||||
* @param pageDto - lista de proformas desde la API.
|
||||
* @param context - Contexto adicional opcional para la adaptación.
|
||||
* @returns {ProformaList} Objeto adaptado a ProformaList.
|
||||
*/
|
||||
|
||||
export const ListProformasAdapter = {
|
||||
fromDto(dto: ListProformasResponseDTO): ProformaList {
|
||||
fromDto(dto: ListProformasResult, context?: unknown): ProformaList {
|
||||
return {
|
||||
items: dto.items.map(ProformaListRowAdapter.fromDto),
|
||||
page: dto.page,
|
||||
perPage: dto.per_page,
|
||||
totalPages: dto.total_pages,
|
||||
totalItems: dto.total_items,
|
||||
items: dto.items.map((rowDto) => ProformaListRowAdapter.fromDto(rowDto, context)),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
type ListProformasItemDTO = ListProformasResponseDTO["items"][number];
|
||||
/**
|
||||
* Adaptador para transformar los items de la API de ListProformasResult a la entidad ProformaListRow.
|
||||
* Reglas de adaptación:
|
||||
* - id, company_id, status, reference se asignan directamente.
|
||||
* - is_proforma se convierte a booleano (true si es "1", false si es "0").
|
||||
*
|
||||
* @param rowDto - item de proforma desde la API.
|
||||
* @param context - Contexto adicional opcional para la adaptación.
|
||||
* @returns {ProformaListRow} Objeto adaptado a ProformaListRow.
|
||||
*/
|
||||
|
||||
type ListProformasItemOutput = ListProformasResponseDTO["items"][number];
|
||||
|
||||
const ProformaListRowAdapter = {
|
||||
fromDto(dto: ListProformasItemDTO): ProformaListRow {
|
||||
fromDto(dto: ListProformasItemOutput, context?: unknown): ProformaListRow {
|
||||
return {
|
||||
id: dto.id,
|
||||
companyId: dto.company_id,
|
||||
isProforma: dto.is_proforma === "1",
|
||||
isProforma: dto.is_proforma,
|
||||
|
||||
invoiceNumber: dto.invoice_number,
|
||||
status: dto.status as ProformaStatus,
|
||||
@ -57,12 +81,9 @@ const ProformaListRowAdapter = {
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
discountPercentage: PercentageDTOHelper.toNumber(dto.discount_percentage),
|
||||
discountPercentageFmt: PercentageDTOHelper.toNumericString(dto.discount_percentage),
|
||||
|
||||
discountAmount: MoneyDTOHelper.toNumber(dto.discount_amount),
|
||||
discountAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.discount_amount),
|
||||
totalDiscountAmount: MoneyDTOHelper.toNumber(dto.total_discount_amount),
|
||||
totalDiscountAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.total_discount_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
|
||||
@ -15,27 +15,24 @@ export interface ProformaListRow {
|
||||
|
||||
invoiceNumber: string;
|
||||
status: ProformaStatus;
|
||||
series: string;
|
||||
series: string | null;
|
||||
|
||||
invoiceDate: string;
|
||||
operationDate: string;
|
||||
operationDate: string | null;
|
||||
|
||||
languageCode: string;
|
||||
currencyCode: string;
|
||||
|
||||
reference: string;
|
||||
description: string;
|
||||
reference: string | null;
|
||||
description: string | null;
|
||||
|
||||
recipient: ProformaRecipient;
|
||||
|
||||
subtotalAmount: number;
|
||||
subtotalAmountFmt: string;
|
||||
|
||||
discountPercentage: number;
|
||||
discountPercentageFmt: string;
|
||||
|
||||
discountAmount: number;
|
||||
discountAmountFmt: string;
|
||||
totalDiscountAmount: number;
|
||||
totalDiscountAmountFmt: string;
|
||||
|
||||
taxableAmount: number;
|
||||
taxableAmountFmt: string;
|
||||
@ -46,5 +43,5 @@ export interface ProformaListRow {
|
||||
totalAmount: number;
|
||||
totalAmountFmt: string;
|
||||
|
||||
linkedInvoiceId: string;
|
||||
linkedInvoiceId: string | null;
|
||||
}
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
|
||||
export interface ProformaRecipient {
|
||||
id: string;
|
||||
name: string;
|
||||
tin: string;
|
||||
name: string | null;
|
||||
tin: string | null;
|
||||
|
||||
street: string;
|
||||
street2: string;
|
||||
street: string | null;
|
||||
street2: string | null;
|
||||
|
||||
city: string;
|
||||
province: string;
|
||||
postalCode: string;
|
||||
country: string;
|
||||
city: string | null;
|
||||
province: string | null;
|
||||
postalCode: string | null;
|
||||
country: string | null;
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import type { CustomerSelectionOption } from "@erp/customers";
|
||||
import { FormSectionCard } from "@repo/rdx-ui/components";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import { FormSectionCard, SelectedRecipientSummary } from "../blocks";
|
||||
import { SelectedRecipientSummary } from "../blocks";
|
||||
|
||||
interface ProformaUpdateRecipientEditorProps {
|
||||
disabled?: boolean;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/customer-invoices/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/customers",
|
||||
"description": "Customers",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DateHelper } from "@erp/core";
|
||||
import { Badge, Button } from "@repo/shadcn-ui/components";
|
||||
import { FileTextIcon } from "lucide-react";
|
||||
|
||||
@ -69,7 +70,7 @@ export const CustomerProformasSection = ({
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">{pro.number}</p>
|
||||
<p className="text-xs text-muted-foreground">{formatDate(pro.date)}</p>
|
||||
<p className="text-xs text-muted-foreground">{DateHelper.format(pro.date)}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<span className="text-sm font-medium">{formatCurrency(pro.amount)}</span>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/customers/*": ["./src/*"]
|
||||
},
|
||||
@ -28,11 +27,6 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-view-dialog.tsx",
|
||||
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx",
|
||||
"../customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/selected-recipient-empty-card.tsx"
|
||||
],
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/factuges",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -4,10 +4,10 @@ import {
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import type { CreateProformaFromFactugesUseCase } from "@erp/factuges/api/application";
|
||||
import type { CreateProformaFromFactugesRequestDTO } from "@erp/factuges/common";
|
||||
|
||||
import type { CreateProformaFromFactugesRequestDTO } from "../../../../common/dto/request/create-proforma-from-factuges.request.dto.js";
|
||||
import type { CreateProformaFromFactugesUseCase } from "../../../application/use-cases/create-proforma-from-factuges.use-case.js";
|
||||
import { factugesApiErrorMapper } from "../factuges-api-error-mapper.js";
|
||||
import { factugesApiErrorMapper } from "../factuges-api-error-mapper";
|
||||
|
||||
export class CreateProformaFromFactugesController extends ExpressController {
|
||||
public constructor(private readonly useCase: CreateProformaFromFactugesUseCase) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/factuges/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/supplier-invoices",
|
||||
"description": "Supplier invoices",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/supplier-invoices/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/suppliers",
|
||||
"description": "Suppliers",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/suppliers/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-criteria",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-ddd",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ZodError } from "zod/v4";
|
||||
import { ValidationErrorCollection, ValidationErrorDetail } from "../errors";
|
||||
import type { ZodError } from "zod/v4";
|
||||
|
||||
import { ValidationErrorCollection, type ValidationErrorDetail } from "../errors";
|
||||
|
||||
export function translateZodValidationError<T>(
|
||||
message: string,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { translateZodValidationError } from "../helpers";
|
||||
|
||||
import { ValueObject } from "./value-object";
|
||||
|
||||
interface UtcDateProps {
|
||||
@ -101,4 +103,15 @@ export class UtcDate extends ValueObject<UtcDateProps> {
|
||||
equals(other: UtcDate): boolean {
|
||||
return this.toISOString() === other.toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determina si la fecha representada
|
||||
* es una fecha futura en comparación con la fecha actual.
|
||||
* @returns
|
||||
*/
|
||||
|
||||
isFuture(currentDate?: UtcDate): boolean {
|
||||
const now = currentDate ? currentDate : UtcDate.today();
|
||||
return this.date.getTime() > now.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-logger",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/react-library.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@repo/rdx-ui/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-utils",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/buildless.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src", "../../modules/core/src/api/application/mappers/patch-collector.ts"],
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
|
||||
@ -1,16 +1,21 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Root",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@erp/core/*": ["modules/core/src/*"],
|
||||
"@erp/auth/*": ["modules/auth/src/*"],
|
||||
"@erp/customers/*": ["modules/customers/src/*"],
|
||||
"@erp/customer-invoices/*": ["modules/customer-invoices/src/*"]
|
||||
"@erp/core/*": [
|
||||
"modules/core/src/*"
|
||||
],
|
||||
"@erp/auth/*": [
|
||||
"modules/auth/src/*"
|
||||
],
|
||||
"@erp/customers/*": [
|
||||
"modules/customers/src/*"
|
||||
],
|
||||
"@erp/customer-invoices/*": [
|
||||
"modules/customer-invoices/src/*"
|
||||
]
|
||||
},
|
||||
|
||||
"target": "ES2021",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
@ -20,4 +25,4 @@
|
||||
"skipLibCheck": true,
|
||||
"allowUnreachableCode": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,12 +65,6 @@ importers:
|
||||
'@erp/customers':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/customers
|
||||
'@erp/factuges':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/factuges
|
||||
'@erp/suppliers':
|
||||
specifier: workspace:*
|
||||
version: link:../../modules/supplier
|
||||
'@repo/rdx-logger':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/rdx-logger
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user