Repaso de proformas
This commit is contained in:
parent
ba67750e17
commit
6ea2e2df5b
@ -63,6 +63,7 @@ export class ReportProformaUseCase {
|
||||
if (documentResult.isFailure) {
|
||||
return Result.fail(documentResult.error);
|
||||
}
|
||||
11;
|
||||
|
||||
// 5. Devolver artefacto firmado
|
||||
return Result.ok({
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
import {
|
||||
CreateProformaRequestSchema,
|
||||
GetProformaByIdRequestSchema,
|
||||
IssueProformaByIdParamsRequestSchema,
|
||||
IssueProformaByIdResponseSchema,
|
||||
ListProformasRequestSchema,
|
||||
ReportProformaByIdParamsRequestSchema,
|
||||
ReportProformaByIdQueryRequestSchema,
|
||||
@ -148,7 +148,7 @@ export const proformasRouter = (params: StartParams) => {
|
||||
"/:proforma_id/issue",
|
||||
//checkTabContext,
|
||||
|
||||
validateRequest(IssueProformaByIdParamsRequestSchema, "params"),
|
||||
validateRequest(IssueProformaByIdResponseSchema, "params"),
|
||||
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const useCase = deps.useCases.issueProforma(publicServices);
|
||||
|
||||
@ -3,3 +3,7 @@ import { z } from "zod/v4";
|
||||
export const IssueProformaByIdParamsRequestSchema = z.object({
|
||||
proforma_id: z.string(),
|
||||
});
|
||||
|
||||
export type IssueProformaByIdParamsRequestDTO = z.infer<
|
||||
typeof IssueProformaByIdParamsRequestSchema
|
||||
>;
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
import { MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core";
|
||||
import { NumericStringSchema, PercentageSchema } from "@erp/core";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const UpdateProformaItemRequestSchema = z.object({
|
||||
id: z.uuid(),
|
||||
position: z.string(),
|
||||
description: z.string().optional(),
|
||||
quantity: NumericStringSchema.optional(),
|
||||
unit_amount: NumericStringSchema.optional(),
|
||||
item_discount_percentage: PercentageSchema.optional(),
|
||||
taxes: z.string().optional(),
|
||||
});
|
||||
|
||||
export const UpdateProformaByIdParamsRequestSchema = z.object({
|
||||
proforma_id: z.string(),
|
||||
});
|
||||
@ -20,22 +30,7 @@ export const UpdateProformaByIdRequestSchema = z.object({
|
||||
language_code: z.string().optional(),
|
||||
currency_code: z.string().optional(),
|
||||
|
||||
items: z
|
||||
.array(
|
||||
z.object({
|
||||
is_valued: z.string().optional(),
|
||||
|
||||
description: z.string().optional(),
|
||||
quantity: QuantitySchema.optional(),
|
||||
unit_amount: MoneySchema.optional(),
|
||||
|
||||
discount_percentage: PercentageSchema.optional(),
|
||||
|
||||
tax_codes: z.array(z.string()).default([]),
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.default([]),
|
||||
items: z.array(UpdateProformaItemRequestSchema).default([]),
|
||||
});
|
||||
|
||||
export type UpdateProformaByIdRequestDTO = Partial<z.infer<typeof UpdateProformaByIdRequestSchema>>;
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
export * from "./create-proforma.response.dto";
|
||||
export * from "./get-proforma-by-id.response.dto";
|
||||
export * from "./issue-proforma-by-id.response.dto";
|
||||
export * from "./list-proformas.response.dto";
|
||||
export * from "./report-proforma-by-id.response.dto";
|
||||
export * from "./update-proformas-by-id.response.dto";
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const IssueProformaByIdResponseSchema = z.object({
|
||||
issuedinvoice_id: z.string(),
|
||||
proforma_id: z.string(),
|
||||
customer_id: z.string(),
|
||||
});
|
||||
|
||||
export type IssueProformaByIdResponseDTO = z.infer<typeof IssueProformaByIdResponseSchema>;
|
||||
@ -10,7 +10,7 @@ export const ListProformasResponseSchema = createPaginatedListSchema(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
company_id: z.uuid(),
|
||||
is_proforma: z.boolean(),
|
||||
is_proforma: z.string(),
|
||||
|
||||
customer_id: z.string(),
|
||||
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export type ReportProformaByIdResponseDTO = Blob;
|
||||
@ -0,0 +1,7 @@
|
||||
import {
|
||||
type GetProformaByIdResponseDTO,
|
||||
GetProformaByIdResponseSchema,
|
||||
} from "./get-proforma-by-id.response.dto";
|
||||
|
||||
export const UpdateProformaByIdResponseSchema = GetProformaByIdResponseSchema;
|
||||
export type UpdateProformaByIdResponseDTO = GetProformaByIdResponseDTO;
|
||||
@ -3,7 +3,7 @@ import { lazy } from "react";
|
||||
import { Outlet, type RouteObject } from "react-router-dom";
|
||||
|
||||
const ProformaLayout = lazy(() =>
|
||||
import("./proformas/shared").then((m) => ({ default: m.ProformaLayout }))
|
||||
import("./proformas/shared2").then((m) => ({ default: m.ProformaLayout }))
|
||||
);
|
||||
|
||||
const ProformasListPage = lazy(() =>
|
||||
|
||||
@ -38,8 +38,8 @@ import { useFieldArray, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { useTranslation } from "../../i18n";
|
||||
import { CustomerInvoicePricesCard } from "../../proformas/shared/ui/components";
|
||||
import { CustomerInvoiceItemsCardEditor } from "../../proformas/shared/ui/components/items";
|
||||
import { CustomerInvoicePricesCard } from "../../proformas/shared2/ui/components";
|
||||
import { CustomerInvoiceItemsCardEditor } from "../../proformas/shared2/ui/components/items";
|
||||
|
||||
import type { CustomerInvoiceData } from "./customer-invoice.schema";
|
||||
import { formatCurrency } from "./utils";
|
||||
|
||||
@ -2,9 +2,9 @@ import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||
import * as React from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import type { ProformaListRow } from "../../shared";
|
||||
import type { PROFORMA_STATUS } from "../../shared/entities";
|
||||
import { useChangeProformaStatusMutation } from "../../shared/hooks";
|
||||
import type { ProformaListRow } from "../../shared2";
|
||||
import type { PROFORMA_STATUS } from "../../shared2/entities";
|
||||
import { useChangeProformaStatusMutation } from "../../shared2/hooks";
|
||||
|
||||
interface ChangeStatusDialogState {
|
||||
open: boolean;
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
XCircleIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import type { ProformaStatus } from "../../shared";
|
||||
import type { ProformaStatus } from "../../shared2";
|
||||
|
||||
export const getProformaStatusButtonVariant = (
|
||||
status: ProformaStatus
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
import { useState } from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS, type ProformaListRow } from "../../shared";
|
||||
import { PROFORMA_STATUS, PROFORMA_STATUS_TRANSITIONS, type ProformaListRow } from "../../shared2";
|
||||
import { getProformaStatusIcon } from "../helpers";
|
||||
|
||||
import { StatusNode, TimelineConnector } from "./components";
|
||||
|
||||
@ -4,7 +4,7 @@ import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { CheckCircle2, type LucideIcon } from "lucide-react";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import type { PROFORMA_STATUS } from "../../../shared";
|
||||
import type { PROFORMA_STATUS } from "../../../shared2";
|
||||
|
||||
interface StatusNodeProps {
|
||||
status: PROFORMA_STATUS;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export * from "./proforma-create-form.entity";
|
||||
export * from "./proforma-create-form.schema";
|
||||
export * from "./proforma-create-form-default";
|
||||
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Valor por defecto para el formulario de creación de proformas.
|
||||
*
|
||||
* Reglas:
|
||||
* - debe ser un objeto que cumpla con la interfaz ProformaCreateForm
|
||||
* - debe tener valores por defecto razonables para cada campo, evitando campos vacíos o nulos cuando sea posible
|
||||
* - el shape del objeto debe coincidir exactamente con el de ProformaCreateForm, sin campos adicionales ni transformaciones
|
||||
* - orientado a la UI, no a la API ni al dominio, es decir, debe ser un objeto que se pueda usar directamente para inicializar un formulario en la interfaz de usuario
|
||||
*/
|
||||
|
||||
export const defaultProformaCreateForm: ProformaCreateForm = {
|
||||
invoiceNumber: "",
|
||||
series: "",
|
||||
|
||||
invoiceDate: "",
|
||||
operationDate: "",
|
||||
|
||||
customerId: "",
|
||||
|
||||
reference: "",
|
||||
isCompany: true,
|
||||
name: "",
|
||||
tradeName: "",
|
||||
tin: "",
|
||||
|
||||
defaultTaxes: [],
|
||||
|
||||
street: "",
|
||||
street2: "",
|
||||
city: "",
|
||||
province: "",
|
||||
postalCode: "",
|
||||
country: "es",
|
||||
|
||||
primaryEmail: "",
|
||||
secondaryEmail: "",
|
||||
primaryPhone: "",
|
||||
secondaryPhone: "",
|
||||
primaryMobile: "",
|
||||
secondaryMobile: "",
|
||||
|
||||
fax: "",
|
||||
website: "",
|
||||
|
||||
legalRecord: "",
|
||||
|
||||
languageCode: "es",
|
||||
currencyCode: "EUR",
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* ProformaCreateForm representa el shape de datos del formulario de creación de proforma.
|
||||
* Es decir, los campos que se muestran en el formulario y que el usuario puede editar.
|
||||
*
|
||||
* Este shape es específico para la UI y no tiene por qué coincidir
|
||||
* con el shape del dominio ni con el de la API.
|
||||
*
|
||||
* Debe cumplir las siguientes reglas:
|
||||
* - nombres en camelCase
|
||||
* - tipos orientados a UI/form
|
||||
* - sin campos de solo lectura que no se editen
|
||||
* - sin shape DTO
|
||||
* - sin detalles impuestos por el widget
|
||||
*/
|
||||
|
||||
import type { ProformaItemForm } from "../../shared/entities";
|
||||
|
||||
export interface ProformaCreateForm {
|
||||
series: string;
|
||||
|
||||
invoiceDate: string;
|
||||
operationDate: string;
|
||||
|
||||
customerId: string;
|
||||
|
||||
reference: string;
|
||||
notes: string;
|
||||
|
||||
languageCode: string;
|
||||
currencyCode: string;
|
||||
|
||||
globalDiscountPercentage: number;
|
||||
|
||||
paymentMethod: string;
|
||||
|
||||
items: ProformaItemForm[];
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Este esquema es para validar los datos del formulario de creación de cliente.
|
||||
* No tiene por qué coincidir con el shape de la entidad ni con el de la API.
|
||||
* Solo define los campos que se muestran en el formulario y sus validaciones.
|
||||
*
|
||||
* Reglas:
|
||||
* - no meter transformaciones silenciosas raras en el esquema (ej: .toUpperCase())
|
||||
* - nombres en camelCase
|
||||
* - tipos orientados a UI/form
|
||||
* - sin campos de solo lectura que no se editen
|
||||
* - sin shape DTO
|
||||
* - sin detalles impuestos por el widget
|
||||
*/
|
||||
|
||||
export const CustomerCreateFormSchema = z.object({
|
||||
reference: z.string(),
|
||||
isCompany: z.boolean(),
|
||||
name: z.string().min(1, "El nombre es obligatorio"),
|
||||
tradeName: z.string(),
|
||||
tin: z.string(),
|
||||
|
||||
defaultTaxes: z.array(z.string()),
|
||||
|
||||
street: z.string(),
|
||||
street2: z.string(),
|
||||
city: z.string(),
|
||||
province: z.string(),
|
||||
postalCode: z.string(),
|
||||
country: z.string().min(1, "El país es obligatorio"),
|
||||
|
||||
primaryEmail: z.email("Email inválido"),
|
||||
secondaryEmail: z.email("Email inválido"),
|
||||
|
||||
primaryPhone: z.string(),
|
||||
secondaryPhone: z.string(),
|
||||
primaryMobile: z.string(),
|
||||
secondaryMobile: z.string(),
|
||||
|
||||
fax: z.string(),
|
||||
website: z.url("URL inválida"),
|
||||
|
||||
legalRecord: z.string(),
|
||||
|
||||
languageCode: z.string().min(1, "El idioma es obligatorio"),
|
||||
currencyCode: z.string().min(1, "La moneda es obligatoria"),
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
export interface ProformaItemForm {
|
||||
id: string;
|
||||
position: string;
|
||||
description: string;
|
||||
quantity: number;
|
||||
unitAmount: number;
|
||||
discountPercentage: number;
|
||||
taxes: string;
|
||||
}
|
||||
@ -2,7 +2,7 @@ import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers";
|
||||
import React from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import { type ProformaListRow, useDeleteProformaMutation } from "../../shared";
|
||||
import { type ProformaListRow, useDeleteProformaMutation } from "../../shared2";
|
||||
|
||||
interface DeleteProformaDialogState {
|
||||
open: boolean;
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from "@repo/shadcn-ui/components";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import type { ProformaListRow } from "../../../shared";
|
||||
import type { ProformaListRow } from "../../../shared2";
|
||||
|
||||
interface DeleteProformaDialogProps {
|
||||
open: boolean;
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from "./issue-proforma-invoice.api";
|
||||
@ -1,18 +0,0 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
export interface IssueProformaInvoiceResponse {
|
||||
invoiceId: string;
|
||||
proformaId: string;
|
||||
customerId: string;
|
||||
}
|
||||
|
||||
export async function issueProformaInvoiceApi(
|
||||
dataSource: IDataSource,
|
||||
proformaId: string
|
||||
): Promise<IssueProformaInvoiceResponse> {
|
||||
return dataSource.custom<IssueProformaInvoiceResponse>({
|
||||
path: `proformas/${proformaId}/issue`,
|
||||
method: "put",
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
@ -3,11 +3,11 @@ import React from "react";
|
||||
import { useChangeProformaStatusDialogController } from "../../change-status";
|
||||
import { useDeleteProformaDialogController } from "../../delete";
|
||||
import { useProformaIssueDialogController } from "../../issue-proforma";
|
||||
import type { ProformaListRow } from "../../shared";
|
||||
import type { ProformaListRow } from "../../shared2";
|
||||
import {
|
||||
type PROFORMA_STATUS,
|
||||
PROFORMA_STATUS_TRANSITIONS,
|
||||
} from "../../shared/entities/proforma-status.entity";
|
||||
} from "../../shared2/entities/proforma-status.entity";
|
||||
|
||||
import { useListProformasController } from "./use-list-proformas.controller";
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import type { CriteriaDTO } from "@erp/core";
|
||||
import { useDebounce } from "@repo/rdx-ui/components";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import { useListProformasQuery } from "../../shared";
|
||||
import { useListProformasQuery } from "../../shared2";
|
||||
|
||||
export const useListProformasController = () => {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
|
||||
@ -3,7 +3,7 @@ import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useTranslation } from "../../../../../i18n";
|
||||
import type { ProformaList, ProformaListRow } from "../../../../shared";
|
||||
import type { ProformaList, ProformaListRow } from "../../../../shared2";
|
||||
|
||||
interface ProformasGridProps {
|
||||
data?: ProformaList;
|
||||
|
||||
@ -21,7 +21,7 @@ import {
|
||||
PROFORMA_STATUS_TRANSITIONS,
|
||||
type ProformaListRow,
|
||||
type ProformaStatus,
|
||||
} from "../../../../shared";
|
||||
} from "../../../../shared2";
|
||||
import { ProformaStatusBadge } from "../../components";
|
||||
|
||||
type GridActionHandlers = {
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
getProformaStatusColor,
|
||||
getProformaStatusIcon,
|
||||
} from "../../../change-status/helpers";
|
||||
import type { ProformaStatus } from "../../../shared";
|
||||
import type { ProformaStatus } from "../../../shared2";
|
||||
|
||||
export type ProformaStatusBadgeProps = {
|
||||
status: string | ProformaStatus; // permitir cualquier valor
|
||||
|
||||
@ -8,7 +8,7 @@ import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import { ProformaDtoAdapter } from "../../adapters";
|
||||
import { useUpdateProforma } from "../../shared/hooks/use-proforma-update-mutation";
|
||||
import { useUpdateProforma } from "../../shared2/hooks/use-proforma-update-mutation";
|
||||
import {
|
||||
type Proforma,
|
||||
type ProformaFormData,
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core";
|
||||
|
||||
import type { GetProformaByIdResponseDTO } from "../../../../common";
|
||||
import type {
|
||||
Proforma,
|
||||
ProformaItem,
|
||||
ProformaRecipient,
|
||||
ProformaStatus,
|
||||
ProformaTaxSummary,
|
||||
} from "../entities";
|
||||
|
||||
export const GetProformaByIdAdapter = {
|
||||
fromDto(dto: GetProformaByIdResponseDTO): Proforma {
|
||||
return {
|
||||
id: dto.id,
|
||||
companyId: dto.company_id,
|
||||
isProforma: dto.is_proforma === "1",
|
||||
|
||||
invoiceNumber: dto.invoice_number,
|
||||
status: dto.status as ProformaStatus,
|
||||
series: dto.series,
|
||||
|
||||
invoiceDate: dto.invoice_date,
|
||||
operationDate: dto.operation_date,
|
||||
|
||||
reference: dto.reference,
|
||||
description: dto.description,
|
||||
notes: dto.notes,
|
||||
|
||||
languageCode: dto.language_code,
|
||||
currencyCode: dto.currency_code,
|
||||
|
||||
customerId: dto.customer_id,
|
||||
recipient: mapRecipient(dto.recipient),
|
||||
taxes: dto.taxes.map(mapTaxSummary),
|
||||
|
||||
paymentMethod: dto.payment_method?.payment_id ?? "",
|
||||
|
||||
subtotalAmount: MoneyDTOHelper.toNumber(dto.subtotal_amount),
|
||||
|
||||
itemsDiscountAmount: MoneyDTOHelper.toNumber(dto.items_discount_amount),
|
||||
globalDiscountPercentage: PercentageDTOHelper.toNumber(dto.discount_percentage),
|
||||
discountAmount: MoneyDTOHelper.toNumber(dto.discount_amount),
|
||||
|
||||
taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount),
|
||||
ivaAmount: MoneyDTOHelper.toNumber(dto.iva_amount),
|
||||
recAmount: MoneyDTOHelper.toNumber(dto.rec_amount),
|
||||
retentionAmount: MoneyDTOHelper.toNumber(dto.retention_amount),
|
||||
taxesAmount: MoneyDTOHelper.toNumber(dto.taxes_amount),
|
||||
|
||||
totalAmount: MoneyDTOHelper.toNumber(dto.total_amount),
|
||||
|
||||
items: dto.items.map(mapItem),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const mapItem = (dto: GetProformaByIdResponseDTO["items"][number]): ProformaItem => {
|
||||
return {
|
||||
id: dto.id,
|
||||
position: dto.position,
|
||||
description: dto.description,
|
||||
|
||||
quantity: QuantityDTOHelper.toNumber(dto.quantity),
|
||||
unitAmount: MoneyDTOHelper.toNumber(dto.unit_amount),
|
||||
|
||||
subtotalAmount: MoneyDTOHelper.toNumber(dto.subtotal_amount),
|
||||
|
||||
itemDiscountPercentage: PercentageDTOHelper.toNumber(dto.item_discount_percentage),
|
||||
itemDiscountAmount: MoneyDTOHelper.toNumber(dto.item_discount_amount),
|
||||
|
||||
globalDiscountPercentage: PercentageDTOHelper.toNumber(dto.global_discount_percentage),
|
||||
globalDiscountAmount: MoneyDTOHelper.toNumber(dto.global_discount_amount),
|
||||
|
||||
taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount),
|
||||
|
||||
ivaCode: dto.iva_code,
|
||||
ivaPercentage: PercentageDTOHelper.toNumber(dto.iva_percentage),
|
||||
ivaAmount: MoneyDTOHelper.toNumber(dto.iva_amount),
|
||||
|
||||
recCode: dto.rec_code,
|
||||
recPercentage: PercentageDTOHelper.toNumber(dto.rec_percentage),
|
||||
recAmount: MoneyDTOHelper.toNumber(dto.rec_amount),
|
||||
|
||||
retentionCode: dto.retention_code,
|
||||
retentionPercentage: PercentageDTOHelper.toNumber(dto.retention_percentage),
|
||||
retentionAmount: MoneyDTOHelper.toNumber(dto.retention_amount),
|
||||
|
||||
taxesAmount: MoneyDTOHelper.toNumber(dto.taxes_amount),
|
||||
totalAmount: MoneyDTOHelper.toNumber(dto.total_amount),
|
||||
|
||||
// Nota: el DTO de detalle actual no incluye `taxes` por línea.
|
||||
// Dejamos fallback explícito para no inventar parseos.
|
||||
taxes: "",
|
||||
};
|
||||
};
|
||||
const mapRecipient = (dto: GetProformaByIdResponseDTO["recipient"]): ProformaRecipient => {
|
||||
return {
|
||||
id: dto.id,
|
||||
name: dto.name,
|
||||
tin: dto.tin,
|
||||
street: dto.street,
|
||||
street2: dto.street2,
|
||||
city: dto.city,
|
||||
province: dto.province,
|
||||
postalCode: dto.postal_code,
|
||||
country: dto.country,
|
||||
};
|
||||
};
|
||||
|
||||
const mapTaxSummary = (dto: GetProformaByIdResponseDTO["taxes"][number]): ProformaTaxSummary => {
|
||||
return {
|
||||
taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount),
|
||||
|
||||
ivaCode: dto.iva_code,
|
||||
ivaPercentage: PercentageDTOHelper.toNumber(dto.iva_percentage),
|
||||
ivaAmount: MoneyDTOHelper.toNumber(dto.iva_amount),
|
||||
|
||||
recCode: dto.rec_code,
|
||||
recPercentage: PercentageDTOHelper.toNumber(dto.rec_percentage),
|
||||
recAmount: MoneyDTOHelper.toNumber(dto.rec_amount),
|
||||
|
||||
retentionCode: dto.retention_code,
|
||||
retentionPercentage: PercentageDTOHelper.toNumber(dto.retention_percentage),
|
||||
retentionAmount: MoneyDTOHelper.toNumber(dto.retention_amount),
|
||||
|
||||
taxesAmount: MoneyDTOHelper.toNumber(dto.taxes_amount),
|
||||
};
|
||||
};
|
||||
@ -1 +0,0 @@
|
||||
export * from "./list-proformas.adapter";
|
||||
@ -1,101 +1,98 @@
|
||||
import { MoneyDTOHelper, PercentageDTOHelper, formatCurrency } from "@erp/core";
|
||||
|
||||
import type { ListProformasResponseDTO } from "../../../../common";
|
||||
import type { ProformaList, ProformaListRow } from "../entities";
|
||||
import type { ProformaList, ProformaListRow, ProformaStatus } from "../entities";
|
||||
|
||||
export const ListProformasAdapter = {
|
||||
fromDto(dto: ListProformasResponseDTO): ProformaList {
|
||||
return {
|
||||
items: dto.items.map(ProformaListRowAdapter.fromDto),
|
||||
page: dto.page,
|
||||
per_page: dto.per_page,
|
||||
total_pages: dto.total_pages,
|
||||
total_items: dto.total_items,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
type ListProformasItemDTO = ListProformasResponseDTO["items"][number];
|
||||
|
||||
export const ListProformasAdapter = {
|
||||
fromDTO(pageDto: ListProformasResponseDTO, context?: unknown): ProformaList {
|
||||
const ProformaListRowAdapter = {
|
||||
fromDto(dto: ListProformasItemDTO): ProformaListRow {
|
||||
return {
|
||||
//...pageDto,
|
||||
page: pageDto.page,
|
||||
per_page: pageDto.per_page,
|
||||
total_pages: pageDto.total_pages,
|
||||
total_items: pageDto.total_items,
|
||||
items: pageDto.items.map((row) => ListProformasRowAdapter.fromDTO(row, context)),
|
||||
};
|
||||
},
|
||||
};
|
||||
id: dto.id,
|
||||
companyId: dto.company_id,
|
||||
isProforma: dto.is_proforma === "1",
|
||||
|
||||
export const ListProformasRowAdapter = {
|
||||
fromDTO(rowDto: ListProformasItemDTO, context?: unknown): ProformaListRow {
|
||||
return {
|
||||
//...rowDto,
|
||||
id: rowDto.id,
|
||||
company_id: rowDto.company_id,
|
||||
invoiceNumber: dto.invoice_number,
|
||||
status: dto.status as ProformaStatus,
|
||||
series: dto.series,
|
||||
|
||||
customer_id: rowDto.company_id,
|
||||
invoiceDate: dto.invoice_date,
|
||||
operationDate: dto.operation_date,
|
||||
|
||||
invoice_number: rowDto.invoice_number,
|
||||
status: rowDto.status,
|
||||
series: rowDto.series,
|
||||
|
||||
invoice_date: rowDto.invoice_date,
|
||||
operation_date: rowDto.operation_date,
|
||||
|
||||
language_code: rowDto.language_code,
|
||||
currency_code: rowDto.currency_code,
|
||||
|
||||
reference: rowDto.reference,
|
||||
description: rowDto.description,
|
||||
languageCode: dto.language_code,
|
||||
currencyCode: dto.currency_code,
|
||||
|
||||
reference: dto.reference,
|
||||
description: dto.description,
|
||||
recipient: {
|
||||
tin: rowDto.recipient.tin,
|
||||
name: rowDto.recipient.name,
|
||||
id: dto.customer_id,
|
||||
tin: dto.recipient.tin,
|
||||
name: dto.recipient.name,
|
||||
|
||||
street: rowDto.recipient.street,
|
||||
street2: rowDto.recipient.street2,
|
||||
city: rowDto.recipient.city,
|
||||
province: rowDto.recipient.province,
|
||||
postal_code: rowDto.recipient.postal_code,
|
||||
country: rowDto.recipient.country,
|
||||
street: dto.recipient.street,
|
||||
street2: dto.recipient.street2,
|
||||
city: dto.recipient.city,
|
||||
province: dto.recipient.province,
|
||||
postalCode: dto.recipient.postal_code,
|
||||
country: dto.recipient.country,
|
||||
},
|
||||
|
||||
subtotal_amount: MoneyDTOHelper.toNumber(rowDto.subtotal_amount),
|
||||
subtotal_amount_fmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(rowDto.subtotal_amount),
|
||||
Number(rowDto.total_amount.scale || 2),
|
||||
rowDto.currency_code,
|
||||
rowDto.language_code
|
||||
subtotalAmount: MoneyDTOHelper.toNumber(dto.subtotal_amount),
|
||||
subtotalAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.subtotal_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
discount_percentage: PercentageDTOHelper.toNumber(rowDto.discount_percentage),
|
||||
discount_percentage_fmt: PercentageDTOHelper.toNumericString(rowDto.discount_percentage),
|
||||
discountPercentage: PercentageDTOHelper.toNumber(dto.discount_percentage),
|
||||
discountPercentageFmt: PercentageDTOHelper.toNumericString(dto.discount_percentage),
|
||||
|
||||
discount_amount: MoneyDTOHelper.toNumber(rowDto.discount_amount),
|
||||
discount_amount_fmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(rowDto.discount_amount),
|
||||
Number(rowDto.total_amount.scale || 2),
|
||||
rowDto.currency_code,
|
||||
rowDto.language_code
|
||||
discountAmount: MoneyDTOHelper.toNumber(dto.discount_amount),
|
||||
discountAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.discount_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
taxable_amount: MoneyDTOHelper.toNumber(rowDto.taxable_amount),
|
||||
taxable_amount_fmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(rowDto.taxable_amount),
|
||||
Number(rowDto.total_amount.scale || 2),
|
||||
rowDto.currency_code,
|
||||
rowDto.language_code
|
||||
taxableAmount: MoneyDTOHelper.toNumber(dto.taxable_amount),
|
||||
taxableAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.taxable_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
taxes_amount: MoneyDTOHelper.toNumber(rowDto.taxes_amount),
|
||||
taxes_amount_fmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(rowDto.taxes_amount),
|
||||
Number(rowDto.total_amount.scale || 2),
|
||||
rowDto.currency_code,
|
||||
rowDto.language_code
|
||||
taxesAmount: MoneyDTOHelper.toNumber(dto.taxes_amount),
|
||||
taxesAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.taxes_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
total_amount: MoneyDTOHelper.toNumber(rowDto.total_amount),
|
||||
total_amount_fmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(rowDto.total_amount),
|
||||
Number(rowDto.total_amount.scale || 2),
|
||||
rowDto.currency_code,
|
||||
rowDto.language_code
|
||||
totalAmount: MoneyDTOHelper.toNumber(dto.total_amount),
|
||||
totalAmountFmt: formatCurrency(
|
||||
MoneyDTOHelper.toNumber(dto.total_amount),
|
||||
Number(dto.total_amount.scale || 2),
|
||||
dto.currency_code,
|
||||
dto.language_code
|
||||
),
|
||||
|
||||
linked_invoice_id: rowDto.linked_invoice_id,
|
||||
linkedInvoiceId: dto.linked_invoice_id,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
import type { Proforma, ProformaListRow } from "../entities";
|
||||
|
||||
/**
|
||||
* Utilizado por el gestor de caché para actualizar algunos campos del objeto ProfromaListRow.
|
||||
* Adaptador para transformar un objeto Proforma a un objeto ProformaListRowPatch,
|
||||
* que es una versión parcial de ProformaListRow.
|
||||
|
||||
* Reglas de adaptación:
|
||||
* - los campos se asignan directamente
|
||||
* - los campos son opcionales excepto el id.
|
||||
*
|
||||
* @param proforma - objeto Proforma a adaptar.
|
||||
* @returns {ProformaListRowPatch} Objeto adaptado a ProformaListRowPatch.
|
||||
*/
|
||||
|
||||
export type ProformaListRowPatch = Partial<ProformaListRow> & Pick<ProformaListRow, "id">;
|
||||
|
||||
export const ProformaToListRowPatchAdapter = {
|
||||
fromProforma(proforma: Proforma): ProformaListRowPatch {
|
||||
return {
|
||||
id: proforma.id,
|
||||
customerId: proforma.customerId,
|
||||
invoiceNumber: proforma.invoiceNumber,
|
||||
status: proforma.status,
|
||||
series: proforma.series,
|
||||
invoiceDate: proforma.invoiceDate,
|
||||
operationDate: proforma.operationDate,
|
||||
languageCode: proforma.languageCode,
|
||||
currencyCode: proforma.currencyCode,
|
||||
reference: proforma.reference,
|
||||
description: proforma.description,
|
||||
recipientName: proforma.recipient.name,
|
||||
recipientTin: proforma.recipient.tin,
|
||||
subtotalAmount: proforma.subtotalAmount,
|
||||
discountPercentage: proforma.discountPercentage,
|
||||
discountAmount: proforma.discountAmount,
|
||||
taxableAmount: proforma.taxableAmount,
|
||||
taxesAmount: proforma.taxesAmount,
|
||||
totalAmount: proforma.totalAmount,
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -1,17 +1,25 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
export interface ChangeStatusResponse {
|
||||
success: boolean;
|
||||
import type { ChangeStatusProformaByIdRequestDTO } from "../../../../common";
|
||||
|
||||
export interface ChangeProformaStatusByIdParams {
|
||||
id: string;
|
||||
data: ChangeStatusProformaByIdRequestDTO;
|
||||
}
|
||||
|
||||
export type ChangeProformaStatusByIdResult = void;
|
||||
|
||||
export async function changeProformaStatusById(
|
||||
dataSource: IDataSource,
|
||||
proformaId: string,
|
||||
newStatus: string
|
||||
): Promise<ChangeStatusResponse> {
|
||||
return dataSource.custom<ChangeStatusResponse>({
|
||||
path: `proformas/${proformaId}/status`,
|
||||
params: ChangeProformaStatusByIdParams
|
||||
): Promise<ChangeProformaStatusByIdResult> {
|
||||
const { id, data } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.custom<ChangeStatusProformaByIdRequestDTO, ChangeProformaStatusByIdResult>({
|
||||
path: `proformas/${id}/status`,
|
||||
method: "patch",
|
||||
data: { new_status: newStatus },
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type { CreateProformaRequestDTO, CreateProformaResponseDTO } from "../../../../common";
|
||||
|
||||
/**
|
||||
* Crea una nueva proforma en el sistema utilizando la fuente de datos proporcionada.
|
||||
*
|
||||
* @param dataSource - La fuente de datos para interactuar con la API.
|
||||
* @param params - Los parámetros necesarios para crear la proforma.
|
||||
* @returns Una promesa que resuelve con los detalles de la proforma creada.
|
||||
* @throws Error si el ID de la proforma no es proporcionado o si la creación falla.
|
||||
*/
|
||||
|
||||
export interface CreateProformaParams {
|
||||
id: string;
|
||||
data: CreateProformaRequestDTO;
|
||||
}
|
||||
|
||||
export type CreateProformaResult = CreateProformaResponseDTO;
|
||||
|
||||
export const createProforma = (
|
||||
dataSource: IDataSource,
|
||||
params: CreateProformaParams
|
||||
): Promise<CreateProformaResult> => {
|
||||
const { id, data } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.createOne<CreateProformaRequestDTO, CreateProformaResponseDTO>(
|
||||
"proformas",
|
||||
data
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
/**
|
||||
* Elimina una proforma existente en el sistema utilizando la fuente de datos proporcionada.
|
||||
*
|
||||
* @param dataSource - La fuente de datos para interactuar con la API.
|
||||
* @param params - Los parámetros necesarios para eliminar la proforma.
|
||||
* @returns Una promesa que se resuelve cuando la proforma ha sido eliminada exitosamente.
|
||||
* @throws Error si el ID de la proforma no es proporcionado o si la eliminación falla.
|
||||
*/
|
||||
|
||||
export interface DeleteProformaByIdParams {
|
||||
id: string;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export type DeleteProformaByIdResult = void;
|
||||
|
||||
export const deleteProformaById = (
|
||||
dataSource: IDataSource,
|
||||
params: DeleteProformaByIdParams
|
||||
): Promise<DeleteProformaByIdResult> => {
|
||||
const { id, signal } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.deleteOne("proformas", id, { signal });
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
export async function deleteProformaById(
|
||||
dataSource: IDataSource,
|
||||
proformaId: string
|
||||
): Promise<void> {
|
||||
await dataSource.deleteOne("proformas", proformaId);
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type { GetProformaByIdResponseDTO } from "../../../../common";
|
||||
|
||||
/**
|
||||
* Recupera los detalles de una proforma específica utilizando su ID a través de la fuente de datos proporcionada.
|
||||
*
|
||||
* @param dataSource - La fuente de datos para interactuar con la API.
|
||||
* @param params - Los parámetros necesarios para obtener la proforma, incluyendo su ID.
|
||||
* @returns Una promesa que resuelve con los detalles de la proforma solicitada.
|
||||
* @throws Error si el ID de la proforma no es proporcionado o si la recuperación falla.
|
||||
*/
|
||||
|
||||
export interface GetProformaByIdParams {
|
||||
id: string;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export type GetProformaByIdResult = GetProformaByIdResponseDTO;
|
||||
|
||||
export function getProformaById(
|
||||
dataSource: IDataSource,
|
||||
params: GetProformaByIdParams
|
||||
): Promise<GetProformaByIdResult> {
|
||||
const { id, signal } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.getOne<GetProformaByIdResponseDTO>("proformas", id, { signal });
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type { Proforma } from "../entities";
|
||||
|
||||
export async function getProformaById(dataSource: IDataSource, signal: AbortSignal, id?: string) {
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
const response = dataSource.getOne<Proforma>("proformas", id, { signal });
|
||||
return response;
|
||||
}
|
||||
@ -1,4 +1,7 @@
|
||||
export * from "./change-proforma-status-by-id.api";
|
||||
export * from "./delete-proforma-by-ip.api";
|
||||
export * from "./get-proforma-by-ip.api";
|
||||
export * from "./list-proformas.api";
|
||||
export * from "./create-proforma.api";
|
||||
export * from "./delete-proforma-by-id.api";
|
||||
export * from "./get-proforma-by-id.api";
|
||||
export * from "./issue-proforma-by-id.api";
|
||||
export * from "./list-proformas-by-criteria.api";
|
||||
export * from "./update-proforma-by-id.api";
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
import type { IssueProformaByIdResponseDTO } from "@erp/customer-invoices/common";
|
||||
|
||||
export interface IssueProformaByIdParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type IssueProformaByIdResult = IssueProformaByIdResponseDTO;
|
||||
|
||||
export const issueProformaById = (
|
||||
dataSource: IDataSource,
|
||||
params: IssueProformaByIdParams
|
||||
): Promise<IssueProformaByIdResult> => {
|
||||
const { id } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.custom<Record<string, never>, IssueProformaByIdResponseDTO>({
|
||||
path: `proformas/${id}/issue`,
|
||||
method: "put",
|
||||
data: {},
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import type { CriteriaDTO } from "@erp/core";
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type { ListProformasResponseDTO } from "../../../../common";
|
||||
|
||||
/**
|
||||
* Recupera una lista de proformas del sistema utilizando la
|
||||
* fuente de datos proporcionada y los criterios de búsqueda especificados.
|
||||
*
|
||||
* @param dataSource - La fuente de datos para interactuar con la API.
|
||||
* @param params - Los parámetros necesarios para listar las proformas, incluyendo los criterios de búsqueda.
|
||||
* @returns Una promesa que resuelve con una lista de proformas que cumplen con los criterios especificados.
|
||||
* @throws Error si la recuperación de la lista de proformas falla.
|
||||
*/
|
||||
|
||||
export type ListProformasByCriteriaParams = {
|
||||
criteria?: CriteriaDTO;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type ListProformasResult = ListProformasResponseDTO;
|
||||
|
||||
export function getListProformasByCriteria(
|
||||
dataSource: IDataSource,
|
||||
params: ListProformasByCriteriaParams
|
||||
): Promise<ListProformasResult> {
|
||||
const { criteria, signal } = params || { criteria: undefined, signal: undefined };
|
||||
return dataSource.getList<ListProformasResponseDTO>("proformas", {
|
||||
signal,
|
||||
...criteria,
|
||||
});
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import type { CriteriaDTO } from "@erp/core";
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type { ListProformasResponseDTO } from "../../../../common";
|
||||
|
||||
export async function getListProformas(
|
||||
dataSource: IDataSource,
|
||||
signal: AbortSignal,
|
||||
criteria: CriteriaDTO
|
||||
) {
|
||||
const response = dataSource.getList<ListProformasResponseDTO>("proformas", {
|
||||
signal,
|
||||
...criteria,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type {
|
||||
ReportProformaByIdQueryRequestDTO,
|
||||
ReportProformaByIdResponseDTO,
|
||||
} from "../../../../common";
|
||||
|
||||
export type ProformaReportFormat = "PDF" | "HTML" | "JSON";
|
||||
|
||||
export interface ReportProformaByIdParams {
|
||||
id: string;
|
||||
data: ReportProformaByIdQueryRequestDTO;
|
||||
}
|
||||
|
||||
export type ReportProformaByIdResult = ReportProformaByIdResponseDTO;
|
||||
|
||||
export const reportProformaById = (
|
||||
dataSource: IDataSource,
|
||||
params: ReportProformaByIdParams
|
||||
): Promise<ReportProformaByIdResult> => {
|
||||
const {
|
||||
id,
|
||||
data: { format = "PDF" },
|
||||
} = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.custom<ReportProformaByIdQueryRequestDTO, ReportProformaByIdResponseDTO>({
|
||||
path: `proformas/${id}/report`,
|
||||
method: "get",
|
||||
data: {
|
||||
format,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
import type {
|
||||
UpdateProformaByIdRequestDTO,
|
||||
UpdateProformaByIdResponseDTO,
|
||||
} from "../../../../common";
|
||||
|
||||
/**
|
||||
* Actualiza una proforma existente en el sistema utilizando la fuente de datos proporcionada.
|
||||
*
|
||||
* @param dataSource - La fuente de datos para interactuar con la API.
|
||||
* @param params - Los parámetros necesarios para actualizar la proforma.
|
||||
* @returns Una promesa que resuelve con los detalles de la proforma actualizada.
|
||||
* @throws Error si el ID de la proforma no es proporcionado o si la actualización falla.
|
||||
*/
|
||||
|
||||
export interface UpdateProformaByIdParams {
|
||||
id: string;
|
||||
data: UpdateProformaByIdRequestDTO;
|
||||
}
|
||||
|
||||
export type UpdateProformaByIdResult = UpdateProformaByIdResponseDTO;
|
||||
|
||||
export const updateProformaById = (
|
||||
dataSource: IDataSource,
|
||||
params: UpdateProformaByIdParams
|
||||
): Promise<UpdateProformaByIdResult> => {
|
||||
const { id, data } = params;
|
||||
|
||||
if (!id) throw new Error("proformaId is required");
|
||||
|
||||
return dataSource.updateOne<UpdateProformaByIdRequestDTO, UpdateProformaByIdResponseDTO>(
|
||||
"proformas",
|
||||
id,
|
||||
data
|
||||
);
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export * from "./payment-method-options.constants";
|
||||
@ -0,0 +1,5 @@
|
||||
export const PAYMENT_METHOD_OPTIONS = [
|
||||
{ label: "Transferencia", value: "6" },
|
||||
{ label: "Transferencia bancaria", value: "15" },
|
||||
{ label: "Domiciliación bancaria", value: "14" },
|
||||
];
|
||||
@ -0,0 +1 @@
|
||||
export * from "./proforma-item-form.entity";
|
||||
@ -0,0 +1,9 @@
|
||||
export interface ProformaItemForm {
|
||||
id: string;
|
||||
position: string;
|
||||
description: string;
|
||||
quantity: number; // scale = 2
|
||||
unitAmount: number; // pendiente confirmar escala exacta de importe
|
||||
discountPercentage: number; // 0..100
|
||||
taxes: string;
|
||||
}
|
||||
@ -1,3 +1,8 @@
|
||||
export * from "./forms/proforma-item-form.entity";
|
||||
export * from "./proforma.entity";
|
||||
export * from "./proforma-item.entity";
|
||||
export * from "./proforma-list.entity";
|
||||
export * from "./proforma-list-row.entity";
|
||||
export * from "./proforma-recipient.entity";
|
||||
export * from "./proforma-status.entity";
|
||||
export * from "./proforma-tax-summary.entity";
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Interface que representa una línea de proforma en el sistema,
|
||||
* adaptada desde la respuesta de la API.
|
||||
* Contiene todos los campos detallados de la línea.
|
||||
*/
|
||||
|
||||
export interface ProformaItem {
|
||||
id: string;
|
||||
position: string;
|
||||
description: string;
|
||||
|
||||
quantity: number;
|
||||
unitAmount: number;
|
||||
|
||||
subtotalAmount: number;
|
||||
|
||||
itemDiscountPercentage: number;
|
||||
itemDiscountAmount: number;
|
||||
|
||||
globalDiscountPercentage: number;
|
||||
globalDiscountAmount: number;
|
||||
|
||||
taxableAmount: number;
|
||||
|
||||
ivaCode: string;
|
||||
ivaPercentage: number;
|
||||
ivaAmount: number;
|
||||
|
||||
recCode: string;
|
||||
recPercentage: number;
|
||||
recAmount: number;
|
||||
|
||||
retentionCode: string;
|
||||
retentionPercentage: number;
|
||||
retentionAmount: number;
|
||||
|
||||
taxesAmount: number;
|
||||
|
||||
totalAmount: number;
|
||||
|
||||
taxes: string;
|
||||
}
|
||||
@ -1,51 +1,50 @@
|
||||
import type { ProformaRecipient } from "./proforma-recipient.entity";
|
||||
import type { ProformaStatus } from "./proforma-status.entity";
|
||||
|
||||
/**
|
||||
* Interface que representa una fila de la lista de
|
||||
* proformas en el sistema, adaptada desde la respuesta de la API.
|
||||
* Contiene los campos justos para mostrar
|
||||
* la información básica de cada proforma en la lista.
|
||||
*/
|
||||
|
||||
export interface ProformaListRow {
|
||||
id: string;
|
||||
company_id: string;
|
||||
companyId: string;
|
||||
isProforma: boolean;
|
||||
|
||||
customer_id: string;
|
||||
|
||||
invoice_number: string;
|
||||
status: string;
|
||||
invoiceNumber: string;
|
||||
status: ProformaStatus;
|
||||
series: string;
|
||||
|
||||
invoice_date: string;
|
||||
operation_date: string;
|
||||
invoiceDate: string;
|
||||
operationDate: string;
|
||||
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
languageCode: string;
|
||||
currencyCode: string;
|
||||
|
||||
reference: string;
|
||||
description: string;
|
||||
|
||||
recipient: {
|
||||
tin: string;
|
||||
name: string;
|
||||
recipient: ProformaRecipient;
|
||||
|
||||
street: string;
|
||||
street2: string;
|
||||
city: string;
|
||||
province: string;
|
||||
postal_code: string;
|
||||
country: string;
|
||||
};
|
||||
subtotalAmount: number;
|
||||
subtotalAmountFmt: string;
|
||||
|
||||
subtotal_amount: number;
|
||||
subtotal_amount_fmt: string;
|
||||
discountPercentage: number;
|
||||
discountPercentageFmt: string;
|
||||
|
||||
discount_percentage: number;
|
||||
discount_percentage_fmt: string;
|
||||
discountAmount: number;
|
||||
discountAmountFmt: string;
|
||||
|
||||
discount_amount: number;
|
||||
discount_amount_fmt: string;
|
||||
taxableAmount: number;
|
||||
taxableAmountFmt: string;
|
||||
|
||||
taxable_amount: number;
|
||||
taxable_amount_fmt: string;
|
||||
taxesAmount: number;
|
||||
taxesAmountFmt: string;
|
||||
|
||||
taxes_amount: number;
|
||||
taxes_amount_fmt: string;
|
||||
totalAmount: number;
|
||||
totalAmountFmt: string;
|
||||
|
||||
total_amount: number;
|
||||
total_amount_fmt: string;
|
||||
|
||||
linked_invoice_id: string;
|
||||
linkedInvoiceId: string;
|
||||
}
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
import type { ProformaListRow } from "./proforma-list-row.entity";
|
||||
|
||||
/**
|
||||
* Interface que representa la respuesta paginada de una lista de proformas,
|
||||
* adaptada desde la respuesta de la API.
|
||||
*/
|
||||
|
||||
export interface ProformaList {
|
||||
items: ProformaListRow[];
|
||||
total_pages: number;
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Interface que representa el destinatario de una proforma en el sistema,
|
||||
* adaptada desde la respuesta de la API.
|
||||
*/
|
||||
|
||||
export interface ProformaRecipient {
|
||||
id: string;
|
||||
name: string;
|
||||
tin: string;
|
||||
|
||||
street: string;
|
||||
street2: string;
|
||||
|
||||
city: string;
|
||||
province: string;
|
||||
postalCode: string;
|
||||
country: string;
|
||||
}
|
||||
@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Enumeración que representa
|
||||
* los posibles estados de una proforma en el sistema.
|
||||
*/
|
||||
|
||||
export enum PROFORMA_STATUS {
|
||||
DRAFT = "draft",
|
||||
SENT = "sent",
|
||||
@ -6,13 +11,4 @@ export enum PROFORMA_STATUS {
|
||||
ISSUED = "issued",
|
||||
}
|
||||
|
||||
// Transiciones válidas según reglas del dominio
|
||||
export const PROFORMA_STATUS_TRANSITIONS: Record<PROFORMA_STATUS, PROFORMA_STATUS[]> = {
|
||||
[PROFORMA_STATUS.DRAFT]: [PROFORMA_STATUS.SENT],
|
||||
[PROFORMA_STATUS.SENT]: [PROFORMA_STATUS.APPROVED, PROFORMA_STATUS.REJECTED],
|
||||
[PROFORMA_STATUS.APPROVED]: [PROFORMA_STATUS.ISSUED, PROFORMA_STATUS.DRAFT],
|
||||
[PROFORMA_STATUS.REJECTED]: [PROFORMA_STATUS.DRAFT],
|
||||
[PROFORMA_STATUS.ISSUED]: [],
|
||||
};
|
||||
|
||||
export type ProformaStatus = `${PROFORMA_STATUS}`;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Interface que representa el resumen de impuestos
|
||||
* de una proforma en el sistema,
|
||||
*
|
||||
*/
|
||||
|
||||
export interface ProformaTaxSummary {
|
||||
taxableAmount: number;
|
||||
|
||||
ivaCode: string;
|
||||
ivaPercentage: number;
|
||||
ivaAmount: number;
|
||||
|
||||
recCode: string;
|
||||
recPercentage: number;
|
||||
recAmount: number;
|
||||
|
||||
retentionCode: string;
|
||||
retentionPercentage: number;
|
||||
retentionAmount: number;
|
||||
|
||||
taxesAmount: number;
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
import type { ProformaItem } from "./proforma-item.entity";
|
||||
import type { ProformaRecipient } from "./proforma-recipient.entity";
|
||||
import type { ProformaStatus } from "./proforma-status.entity";
|
||||
import type { ProformaTaxSummary } from "./proforma-tax-summary.entity";
|
||||
|
||||
/**
|
||||
* Interface que representa una proforma en el sistema,
|
||||
* adaptada desde la respuesta de la API.
|
||||
* Contiene todos los campos detallados de la proforma.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface Proforma {
|
||||
id: string;
|
||||
companyId: string;
|
||||
isProforma: boolean;
|
||||
|
||||
invoiceNumber: string;
|
||||
status: ProformaStatus;
|
||||
series: string;
|
||||
|
||||
invoiceDate: string;
|
||||
operationDate: string;
|
||||
|
||||
reference: string;
|
||||
description: string;
|
||||
notes: string;
|
||||
|
||||
languageCode: string;
|
||||
currencyCode: string;
|
||||
|
||||
customerId: string;
|
||||
recipient: ProformaRecipient;
|
||||
|
||||
taxes: ProformaTaxSummary[];
|
||||
|
||||
paymentMethod?: string;
|
||||
|
||||
subtotalAmount: number;
|
||||
|
||||
itemsDiscountAmount: number;
|
||||
globalDiscountPercentage: number;
|
||||
discountAmount: number;
|
||||
|
||||
taxableAmount: number;
|
||||
ivaAmount: number;
|
||||
recAmount: number;
|
||||
retentionAmount: number;
|
||||
taxesAmount: number;
|
||||
|
||||
totalAmount: number;
|
||||
|
||||
items: ProformaItem[];
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from "./use-change-proforma-status-mutation";
|
||||
export * from "./use-delete-proforma-mutation";
|
||||
export * from "./use-list-proformas-query";
|
||||
@ -1,47 +0,0 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import {
|
||||
type DefaultError,
|
||||
type QueryKey,
|
||||
type UseQueryResult,
|
||||
useQuery,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import { type Proforma, getProformaById } from "../../api";
|
||||
|
||||
export const PROFORMA_QUERY_KEY = (proformaId?: string): QueryKey => [
|
||||
"proformas:detail",
|
||||
{
|
||||
proformaId,
|
||||
},
|
||||
];
|
||||
|
||||
type ProformaQueryOptions = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export const useProformaGetQuery = (
|
||||
proformaId?: string,
|
||||
options?: ProformaQueryOptions
|
||||
): UseQueryResult<Proforma, DefaultError> => {
|
||||
const dataSource = useDataSource();
|
||||
const enabled = options?.enabled ?? Boolean(proformaId);
|
||||
|
||||
return useQuery<Proforma, DefaultError>({
|
||||
queryKey: PROFORMA_QUERY_KEY(proformaId),
|
||||
queryFn: async ({ signal }) => getProformaById(dataSource, signal, proformaId),
|
||||
enabled,
|
||||
placeholderData: (previousData, _previousQuery) => previousData, // Mantener datos previos mientras se carga nueva datos (antiguo `keepPreviousData`)
|
||||
});
|
||||
};
|
||||
|
||||
/*export function invalidateProformaDetailCache(qc: QueryClient, id: string) {
|
||||
return qc.invalidateQueries({
|
||||
queryKey: getProformaQueryKey(id ?? "unknown"),
|
||||
exact: Boolean(id),
|
||||
});
|
||||
}
|
||||
|
||||
export function setProformaDetailCache(qc: QueryClient, id: string, data: unknown) {
|
||||
qc.setQueryData(getProformaQueryKey(id), data);
|
||||
}
|
||||
*/
|
||||
@ -1,58 +0,0 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import { ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { UpdateProformaByIdRequestSchema } from "../../../../common";
|
||||
import type { ProformaFormData } from "../../update/types";
|
||||
|
||||
export const PROFORMA_UPDATE_KEY = ["proformas", "update"] as const;
|
||||
|
||||
type UpdateProformaContext = {};
|
||||
|
||||
type UpdateProformaPayload = {
|
||||
id: string;
|
||||
data: Partial<ProformaFormData>;
|
||||
};
|
||||
|
||||
export const useProformaUpdateMutation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const dataSource = useDataSource();
|
||||
const schema = UpdateProformaByIdRequestSchema;
|
||||
|
||||
return useMutation<Proforma, DefaultError, UpdateProformaPayload, UpdateProformaContext>({
|
||||
mutationKey: PROFORMA_UPDATE_KEY,
|
||||
|
||||
mutationFn: async (payload) => {
|
||||
const { id: proformaId, data } = payload;
|
||||
if (!proformaId) {
|
||||
throw new Error("proformaId is required");
|
||||
}
|
||||
|
||||
const result = schema.safeParse(data);
|
||||
if (!result.success) {
|
||||
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
|
||||
}
|
||||
|
||||
const updated = await dataSource.updateOne("proformas", proformaId, data);
|
||||
return updated as Proforma;
|
||||
},
|
||||
|
||||
onSuccess: (updated: Proforma, variables) => {
|
||||
const { id: proformaId } = updated;
|
||||
|
||||
// Invalida el listado para refrescar desde servidor
|
||||
//invalidateProformaListCache(queryClient);
|
||||
|
||||
// Actualiza detalle
|
||||
//setProformaDetailCache(queryClient, proformaId, updated);
|
||||
|
||||
// Actualiza todas las páginas donde aparezca
|
||||
//upsertProformaIntoListCaches(queryClient, { ...updated });
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
// Refresca todos los listados
|
||||
//invalidateProformaListCache(queryClient);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1,4 +0,0 @@
|
||||
export * from "./api";
|
||||
export * from "./entities";
|
||||
export * from "./hooks";
|
||||
export * from "./ui";
|
||||
@ -0,0 +1,3 @@
|
||||
export * from "./use-change-proforma-status-mutation";
|
||||
export * from "./use-delete-proforma-mutation";
|
||||
export * from "./use-list-proformas-query";
|
||||
@ -1,7 +1,7 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { changeProformaStatusById } from "../api/change-proforma-status-by-id.api";
|
||||
import { changeProformaStatusById } from "../../shared/api/change-proforma-status-by-id.api";
|
||||
import type { PROFORMA_STATUS } from "../entities";
|
||||
|
||||
import { LIST_PROFORMAS_QUERY_KEY_PREFIX } from "./use-list-proformas-query";
|
||||
@ -0,0 +1,47 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import {
|
||||
type DefaultError,
|
||||
type QueryKey,
|
||||
type UseQueryResult,
|
||||
useQuery,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import { type Proforma, getProformaById } from "../../api";
|
||||
|
||||
export const PROFORMA_QUERY_KEY = (proformaId?: string): QueryKey => [
|
||||
"proformas:detail",
|
||||
{
|
||||
proformaId,
|
||||
},
|
||||
];
|
||||
|
||||
type ProformaQueryOptions = {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export const useProformaGetQuery = (
|
||||
proformaId?: string,
|
||||
options?: ProformaQueryOptions
|
||||
): UseQueryResult<Proforma, DefaultError> => {
|
||||
const dataSource = useDataSource();
|
||||
const enabled = options?.enabled ?? Boolean(proformaId);
|
||||
|
||||
return useQuery<Proforma, DefaultError>({
|
||||
queryKey: PROFORMA_QUERY_KEY(proformaId),
|
||||
queryFn: async ({ signal }) => getProformaById(dataSource, signal, proformaId),
|
||||
enabled,
|
||||
placeholderData: (previousData, _previousQuery) => previousData, // Mantener datos previos mientras se carga nueva datos (antiguo `keepPreviousData`)
|
||||
});
|
||||
};
|
||||
|
||||
/*export function invalidateProformaDetailCache(qc: QueryClient, id: string) {
|
||||
return qc.invalidateQueries({
|
||||
queryKey: getProformaQueryKey(id ?? "unknown"),
|
||||
exact: Boolean(id),
|
||||
});
|
||||
}
|
||||
|
||||
export function setProformaDetailCache(qc: QueryClient, id: string, data: unknown) {
|
||||
qc.setQueryData(getProformaQueryKey(id), data);
|
||||
}
|
||||
*/
|
||||
@ -0,0 +1,58 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import { ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { UpdateProformaByIdRequestSchema } from "../../../../common";
|
||||
import type { ProformaFormData } from "../../update/types";
|
||||
|
||||
export const PROFORMA_UPDATE_KEY = ["proformas", "update"] as const;
|
||||
|
||||
type UpdateProformaContext = {};
|
||||
|
||||
type UpdateProformaPayload = {
|
||||
id: string;
|
||||
data: Partial<ProformaFormData>;
|
||||
};
|
||||
|
||||
export const useProformaUpdateMutation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const dataSource = useDataSource();
|
||||
const schema = UpdateProformaByIdRequestSchema;
|
||||
|
||||
return useMutation<Proforma, DefaultError, UpdateProformaPayload, UpdateProformaContext>({
|
||||
mutationKey: PROFORMA_UPDATE_KEY,
|
||||
|
||||
mutationFn: async (payload) => {
|
||||
const { id: proformaId, data } = payload;
|
||||
if (!proformaId) {
|
||||
throw new Error("proformaId is required");
|
||||
}
|
||||
|
||||
const result = schema.safeParse(data);
|
||||
if (!result.success) {
|
||||
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
|
||||
}
|
||||
|
||||
const updated = await dataSource.updateOne("proformas", proformaId, data);
|
||||
return updated as Proforma;
|
||||
},
|
||||
|
||||
onSuccess: (updated: Proforma, variables) => {
|
||||
const { id: proformaId } = updated;
|
||||
|
||||
// Invalida el listado para refrescar desde servidor
|
||||
//invalidateProformaListCache(queryClient);
|
||||
|
||||
// Actualiza detalle
|
||||
//setProformaDetailCache(queryClient, proformaId, updated);
|
||||
|
||||
// Actualiza todas las páginas donde aparezca
|
||||
//upsertProformaIntoListCaches(queryClient, { ...updated });
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
// Refresca todos los listados
|
||||
//invalidateProformaListCache(queryClient);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
export * from "./api";
|
||||
export * from "./entities";
|
||||
export * from "./hooks";
|
||||
export * from "./ui";
|
||||
@ -0,0 +1,4 @@
|
||||
export * from "./proforma-update-form.entity";
|
||||
export * from "./proforma-update-form.schema";
|
||||
export * from "./proforma-update-form-defaults";
|
||||
export * from "./proforma-update-patch.entity";
|
||||
@ -0,0 +1,9 @@
|
||||
export interface ProformaItemUpdateForm {
|
||||
id: string;
|
||||
position: string;
|
||||
description: string;
|
||||
quantity: number;
|
||||
unitAmount: number;
|
||||
discountPercentage: number;
|
||||
taxes: string;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
import type { CustomerUpdateForm } from "./proforma-update-form.entity";
|
||||
|
||||
export const defaultCustomerUpdateForm: CustomerUpdateForm = {
|
||||
reference: "",
|
||||
isCompany: true,
|
||||
name: "",
|
||||
tradeName: "",
|
||||
tin: "",
|
||||
|
||||
defaultTaxes: [],
|
||||
|
||||
street: "",
|
||||
street2: "",
|
||||
city: "",
|
||||
province: "",
|
||||
postalCode: "",
|
||||
country: "es",
|
||||
|
||||
primaryEmail: "",
|
||||
secondaryEmail: "",
|
||||
primaryPhone: "",
|
||||
secondaryPhone: "",
|
||||
primaryMobile: "",
|
||||
secondaryMobile: "",
|
||||
|
||||
fax: "",
|
||||
website: "",
|
||||
|
||||
legalRecord: "",
|
||||
|
||||
languageCode: "es",
|
||||
currencyCode: "EUR",
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* ProformaUpdateForm representa el shape de datos del formulario de actualización de proformas.
|
||||
* Es decir, los campos que se muestran en el formulario y que el usuario puede editar.
|
||||
*
|
||||
* Es específico de la UI y no tiene por qué coincidir
|
||||
* con el shape del dominio ni con el de la API.
|
||||
*
|
||||
* Debe cumplir las siguientes reglas:
|
||||
* - nombres en camelCase
|
||||
* - tipos orientados a UI/form
|
||||
* - sin campos de solo lectura que no se editen
|
||||
* - sin shape DTO
|
||||
* - sin detalles impuestos por el widget
|
||||
*/
|
||||
|
||||
import type { ProformaItemForm } from "../../shared/entities";
|
||||
|
||||
export interface ProformaUpdateForm {
|
||||
series: string;
|
||||
|
||||
invoiceDate: string;
|
||||
operationDate: string;
|
||||
|
||||
customerId: string;
|
||||
|
||||
reference: string;
|
||||
notes: string;
|
||||
|
||||
languageCode: string;
|
||||
currencyCode: string;
|
||||
|
||||
globalDiscountPercentage: number;
|
||||
|
||||
paymentMethod: string;
|
||||
|
||||
items: ProformaItemForm[];
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Este esquema es para validar los datos del formulario de actualización de proformas.
|
||||
* No tiene por qué coincidir con el shape de la entidad ni con el de la API.
|
||||
* Solo define los campos que se muestran en el formulario y sus validaciones.
|
||||
*
|
||||
* Reglas:
|
||||
* - no meter transformaciones silenciosas raras en el esquema (ej: .toUpperCase())
|
||||
* - nombres en camelCase
|
||||
* - tipos orientados a UI/form
|
||||
* - sin campos de solo lectura que no se editen
|
||||
* - sin shape DTO
|
||||
* - sin detalles impuestos por el widget
|
||||
*/
|
||||
|
||||
export const ProformaItemFormSchema = z.object({
|
||||
description: z.string().max(2000).optional().default(""),
|
||||
quantity: z.any(), //NumericStringSchema.optional(),
|
||||
unit_amount: z.any(), //NumericStringSchema.optional(),
|
||||
|
||||
subtotal_amount: z.any(), //z.number(),
|
||||
discount_percentage: z.any(), //NumericStringSchema.optional(),
|
||||
discount_amount: z.number(),
|
||||
taxable_amount: z.number(),
|
||||
|
||||
tax_codes: z.array(z.string()).default([]),
|
||||
|
||||
taxes_amount: z.number(),
|
||||
total_amount: z.number(),
|
||||
});
|
||||
|
||||
export const ProformaFormSchema = z.object({
|
||||
invoice_number: z.string().optional(),
|
||||
series: z.string().optional(),
|
||||
|
||||
invoice_date: z.string().optional(),
|
||||
operation_date: z.string().optional(),
|
||||
|
||||
customer_id: z.string().optional(),
|
||||
recipient: z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
tin: z.string().optional(),
|
||||
street: z.string().optional(),
|
||||
street2: z.string().optional(),
|
||||
city: z.string().optional(),
|
||||
province: z.string().optional(),
|
||||
postal_code: z.string().optional(),
|
||||
country: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
reference: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
notes: z.string().optional(),
|
||||
|
||||
language_code: z
|
||||
.string({
|
||||
error: "El idioma es obligatorio",
|
||||
})
|
||||
.min(1, "Debe indicar un idioma")
|
||||
.toUpperCase() // asegura mayúsculas
|
||||
.default("es"),
|
||||
|
||||
currency_code: z
|
||||
.string({
|
||||
error: "La moneda es obligatoria",
|
||||
})
|
||||
.min(1, "La moneda no puede estar vacía")
|
||||
.toUpperCase() // asegura mayúsculas
|
||||
.default("EUR"),
|
||||
|
||||
taxes: z
|
||||
.array(
|
||||
z.object({
|
||||
tax_code: z.string(),
|
||||
tax_label: z.string(),
|
||||
taxable_amount: z.number(),
|
||||
taxes_amount: z.number(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
|
||||
items: z.array(ProformaItemFormSchema).optional(),
|
||||
|
||||
subtotal_amount: z.number(),
|
||||
items_discount_amount: z.number(),
|
||||
discount_percentage: z.number(),
|
||||
discount_amount: z.number(),
|
||||
taxable_amount: z.number(),
|
||||
taxes_amount: z.number(),
|
||||
total_amount: z.number(),
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import type { ProformaUpdateForm } from "./proforma-update-form.entity";
|
||||
|
||||
/**
|
||||
* ProformaUpdatePatch representa los cambios que se van a aplicar a una proforma.
|
||||
* Se representa con las mismas propiedades que ProformaUpdateForm,
|
||||
* pero todas ellas son opcionales.
|
||||
*
|
||||
* A la API solo hay que enviar los campos que han cambiado.
|
||||
*
|
||||
* Reglas:
|
||||
* - debe ser un Partial de ProformaUpdateForm
|
||||
* - no debe tener campos adicionales ni transformaciones
|
||||
* - debe ser un shape orientado a la API, no a la UI ni al dominio
|
||||
* - sin shape DTO, solo tipos simples y directos
|
||||
*/
|
||||
|
||||
export type ProformaUpdatePatch = Partial<ProformaUpdateForm>;
|
||||
Loading…
Reference in New Issue
Block a user