From 6ea2e2df5bbeb5f5e5fbffeb820ed0117d23334d Mon Sep 17 00:00:00 2001 From: david Date: Sun, 5 Apr 2026 12:47:54 +0200 Subject: [PATCH] Repaso de proformas --- .../use-cases/report-proforma.use-case.ts | 1 + .../proformas/express/proformas.routes.ts | 4 +- .../issue-proforma-by-id.request.dto.ts | 4 + .../update-proforma-by-id.request.dto.ts | 29 ++-- .../common/dto/response/proformas/index.ts | 3 + .../issue-proforma-by-id.response.dto.ts | 9 ++ .../proformas/list-proformas.response.dto.ts | 2 +- .../report-proforma-by-id.response.dto.ts | 1 + .../update-proformas-by-id.response.dto.ts | 7 + .../src/web/customer-invoice-routes.tsx | 2 +- .../create-customer-invoice-edit-form.tsx | 4 +- ...hange-proforma-status-dialog-controller.ts | 6 +- .../helpers/proforma-status-ui.ts | 2 +- .../change-status/ui/change-status-dialog.tsx | 2 +- .../ui/components/status-node.tsx | 2 +- .../web/proformas/create/entities/index.ts | 3 + .../entities/proforma-create-form-default.ts | 49 ++++++ .../entities/proforma-create-form.entity.ts | 37 +++++ .../entities/proforma-create-form.schema.ts | 48 ++++++ .../proforma-item-create-form.entity.ts | 9 ++ .../use-delete-proforma-dialog-controller.ts | 2 +- .../ui/components/delete-proforma-dialog.tsx | 2 +- .../web/proformas/issue-proforma/api/index.ts | 1 - .../api/issue-proforma-invoice.api.ts | 18 --- .../use-list-proformas-page.controller.ts | 4 +- .../use-list-proformas.controller.ts | 2 +- .../blocks/proformas-grid/proformas-grid.tsx | 2 +- .../use-proforma-grid-columns.tsx | 2 +- .../ui/components/proforma-status-badge.tsx | 2 +- .../pages/update/proforma-update-comp.tsx | 2 +- .../adapters/get-proforma-by-id.adapter.ts | 129 ++++++++++++++++ .../web/proformas/shared/adapters/index.ts | 1 - .../shared/adapters/list-proformas.adapter.ts | 141 +++++++++--------- .../proforma-to-list-row-patch.adapter.ts | 42 ++++++ .../api/change-proforma-status-by-id.api.ts | 24 ++- .../shared/api/create-proforma.api.ts | 33 ++++ .../shared/api/delete-proforma-by-id.api.ts | 28 ++++ .../shared/api/delete-proforma-by-ip.api.ts | 8 - .../shared/api/get-proforma-by-id.api.ts | 30 ++++ .../shared/api/get-proforma-by-ip.api.ts | 9 -- .../src/web/proformas/shared/api/index.ts | 9 +- .../shared/api/issue-proforma-by-id.api.ts | 23 +++ .../api/list-proformas-by-criteria.api.ts | 32 ++++ .../shared/api/list-proformas.api.ts | 17 --- .../shared/api/report-proforma-by-id.api.ts | 35 +++++ .../shared/api/update-proforma-by-id.api.ts | 37 +++++ .../web/proformas/shared/constants/index.ts | 1 + .../payment-method-options.constants.ts | 5 + .../proformas/shared/entities/forms/index.ts | 1 + .../forms/proforma-item-form.entity.ts | 9 ++ .../web/proformas/shared/entities/index.ts | 5 + .../shared/entities/proforma-item.entity.ts | 42 ++++++ .../entities/proforma-list-row.entity.ts | 65 ++++---- .../shared/entities/proforma-list.entity.ts | 5 + .../entities/proforma-recipient.entity.ts | 18 +++ .../shared/entities/proforma-status.entity.ts | 14 +- .../entities/proforma-tax-summary.entity.ts | 23 +++ .../shared/entities/proforma.entity.ts | 54 +++++++ .../src/web/proformas/shared/hooks/index.ts | 3 - .../src/web/proformas/shared/hooks/keys.ts | 0 .../shared/hooks/proforma-cache-strategy.ts | 0 .../shared/hooks/to-validation-errors.ts | 0 .../shared/hooks/use-list-proformas-query.ts | 0 .../hooks/use-proforma-create-mutation.ts | 0 .../hooks/use-proforma-delete-mutation.ts | 0 .../shared/hooks/use-proforma-get-query.ts | 47 ------ .../hooks/use-proforma-update-mutation.ts | 58 ------- .../src/web/proformas/shared/index.ts | 4 - .../src/web/proformas/shared2/hooks/index.ts | 3 + .../use-change-proforma-status-mutation.ts | 2 +- .../hooks/use-delete-proforma-mutation.ts | 0 .../hooks/use-list-proformas-query.tsx | 0 .../shared2/hooks/use-proforma-get-query.ts | 47 ++++++ .../hooks/use-proforma-update-mutation.ts | 58 +++++++ .../src/web/proformas/shared2/index.ts | 4 + .../{shared => shared2}/ui/blocks/index.ts | 0 .../ui/blocks/proforma-layout.tsx | 0 .../proformas/{shared => shared2}/ui/index.ts | 0 .../web/proformas/update/entities/index.ts | 4 + .../proforma-item-update-form.entity.ts | 9 ++ .../entities/proforma-update-form-defaults.ts | 33 ++++ .../entities/proforma-update-form.entity.ts | 37 +++++ .../entities/proforma-update-form.schema.ts | 95 ++++++++++++ .../entities/proforma-update-patch.entity.ts | 17 +++ 84 files changed, 1192 insertions(+), 330 deletions(-) create mode 100644 modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts create mode 100644 modules/customer-invoices/src/common/dto/response/proformas/report-proforma-by-id.response.dto.ts create mode 100644 modules/customer-invoices/src/common/dto/response/proformas/update-proformas-by-id.response.dto.ts create mode 100644 modules/customer-invoices/src/web/proformas/create/entities/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form-default.ts create mode 100644 modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.schema.ts create mode 100644 modules/customer-invoices/src/web/proformas/create/entities/proforma-item-create-form.entity.ts delete mode 100644 modules/customer-invoices/src/web/proformas/issue-proforma/api/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/issue-proforma/api/issue-proforma-invoice.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/adapters/proforma-to-list-row-patch.adapter.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts delete mode 100644 modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-ip.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts delete mode 100644 modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-ip.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts delete mode 100644 modules/customer-invoices/src/web/proformas/shared/api/list-proformas.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/constants/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/constants/payment-method-options.constants.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/forms/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/forms/proforma-item-form.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-recipient.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/keys.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/to-validation-errors.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-create-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared2/hooks/index.ts rename modules/customer-invoices/src/web/proformas/{shared => shared2}/hooks/use-change-proforma-status-mutation.ts (90%) rename modules/customer-invoices/src/web/proformas/{shared => shared2}/hooks/use-delete-proforma-mutation.ts (100%) rename modules/customer-invoices/src/web/proformas/{shared => shared2}/hooks/use-list-proformas-query.tsx (100%) create mode 100644 modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-get-query.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-update-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared2/index.ts rename modules/customer-invoices/src/web/proformas/{shared => shared2}/ui/blocks/index.ts (100%) rename modules/customer-invoices/src/web/proformas/{shared => shared2}/ui/blocks/proforma-layout.tsx (100%) rename modules/customer-invoices/src/web/proformas/{shared => shared2}/ui/index.ts (100%) create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-form.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form-defaults.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts index a4e5f820..7001d2ac 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts @@ -63,6 +63,7 @@ export class ReportProformaUseCase { if (documentResult.isFailure) { return Result.fail(documentResult.error); } + 11; // 5. Devolver artefacto firmado return Result.ok({ diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas.routes.ts b/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas.routes.ts index 6c71d26a..38c45bd2 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas.routes.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas.routes.ts @@ -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); diff --git a/modules/customer-invoices/src/common/dto/request/proformas/issue-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/issue-proforma-by-id.request.dto.ts index fb754316..103b831d 100644 --- a/modules/customer-invoices/src/common/dto/request/proformas/issue-proforma-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/issue-proforma-by-id.request.dto.ts @@ -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 +>; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts index 1cb92808..422a5729 100644 --- a/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts @@ -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>; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/index.ts b/modules/customer-invoices/src/common/dto/response/proformas/index.ts index 3f1edcee..4235dabe 100644 --- a/modules/customer-invoices/src/common/dto/response/proformas/index.ts +++ b/modules/customer-invoices/src/common/dto/response/proformas/index.ts @@ -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"; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts new file mode 100644 index 00000000..40ececdb --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/issue-proforma-by-id.response.dto.ts @@ -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; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/list-proformas.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/list-proformas.response.dto.ts index 49d523df..75d1432f 100644 --- a/modules/customer-invoices/src/common/dto/response/proformas/list-proformas.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/proformas/list-proformas.response.dto.ts @@ -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(), diff --git a/modules/customer-invoices/src/common/dto/response/proformas/report-proforma-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/report-proforma-by-id.response.dto.ts new file mode 100644 index 00000000..eb0f31e1 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/report-proforma-by-id.response.dto.ts @@ -0,0 +1 @@ +export type ReportProformaByIdResponseDTO = Blob; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/update-proformas-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/update-proformas-by-id.response.dto.ts new file mode 100644 index 00000000..96b4d8fc --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/update-proformas-by-id.response.dto.ts @@ -0,0 +1,7 @@ +import { + type GetProformaByIdResponseDTO, + GetProformaByIdResponseSchema, +} from "./get-proforma-by-id.response.dto"; + +export const UpdateProformaByIdResponseSchema = GetProformaByIdResponseSchema; +export type UpdateProformaByIdResponseDTO = GetProformaByIdResponseDTO; diff --git a/modules/customer-invoices/src/web/customer-invoice-routes.tsx b/modules/customer-invoices/src/web/customer-invoice-routes.tsx index a5358ec0..9de27507 100644 --- a/modules/customer-invoices/src/web/customer-invoice-routes.tsx +++ b/modules/customer-invoices/src/web/customer-invoice-routes.tsx @@ -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(() => diff --git a/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx b/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx index cb3f8326..8595b5a3 100644 --- a/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx +++ b/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts index 74d06518..25f0c5d3 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts b/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts index 7b5b9f54..feef7695 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/helpers/proforma-status-ui.ts @@ -7,7 +7,7 @@ import { XCircleIcon, } from "lucide-react"; -import type { ProformaStatus } from "../../shared"; +import type { ProformaStatus } from "../../shared2"; export const getProformaStatusButtonVariant = ( status: ProformaStatus diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx index 604284e8..57b7ff55 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx index 477ddc8d..850e4921 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/components/status-node.tsx @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/create/entities/index.ts b/modules/customer-invoices/src/web/proformas/create/entities/index.ts new file mode 100644 index 00000000..3a31f995 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/create/entities/index.ts @@ -0,0 +1,3 @@ +export * from "./proforma-create-form.entity"; +export * from "./proforma-create-form.schema"; +export * from "./proforma-create-form-default"; diff --git a/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form-default.ts b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form-default.ts new file mode 100644 index 00000000..832ad429 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form-default.ts @@ -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", +}; diff --git a/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.entity.ts b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.entity.ts new file mode 100644 index 00000000..f936f93a --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.entity.ts @@ -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[]; +} diff --git a/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.schema.ts b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.schema.ts new file mode 100644 index 00000000..c21cdcb2 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/create/entities/proforma-create-form.schema.ts @@ -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"), +}); diff --git a/modules/customer-invoices/src/web/proformas/create/entities/proforma-item-create-form.entity.ts b/modules/customer-invoices/src/web/proformas/create/entities/proforma-item-create-form.entity.ts new file mode 100644 index 00000000..e1e1c52c --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/create/entities/proforma-item-create-form.entity.ts @@ -0,0 +1,9 @@ +export interface ProformaItemForm { + id: string; + position: string; + description: string; + quantity: number; + unitAmount: number; + discountPercentage: number; + taxes: string; +} diff --git a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts index 9b699ac2..e88230c3 100644 --- a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx index b3005a60..1a479f5c 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/issue-proforma/api/index.ts b/modules/customer-invoices/src/web/proformas/issue-proforma/api/index.ts deleted file mode 100644 index b69fff16..00000000 --- a/modules/customer-invoices/src/web/proformas/issue-proforma/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./issue-proforma-invoice.api"; diff --git a/modules/customer-invoices/src/web/proformas/issue-proforma/api/issue-proforma-invoice.api.ts b/modules/customer-invoices/src/web/proformas/issue-proforma/api/issue-proforma-invoice.api.ts deleted file mode 100644 index 900b5100..00000000 --- a/modules/customer-invoices/src/web/proformas/issue-proforma/api/issue-proforma-invoice.api.ts +++ /dev/null @@ -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 { - return dataSource.custom({ - path: `proformas/${proformaId}/issue`, - method: "put", - data: {}, - }); -} diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts index 29770d54..0d272424 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts index 2d070b57..13449672 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts @@ -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); diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx index be12910c..fabf3e55 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/proformas-grid.tsx @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx index bf72c61e..55d9c4bc 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/use-proforma-grid-columns.tsx @@ -21,7 +21,7 @@ import { PROFORMA_STATUS_TRANSITIONS, type ProformaListRow, type ProformaStatus, -} from "../../../../shared"; +} from "../../../../shared2"; import { ProformaStatusBadge } from "../../components"; type GridActionHandlers = { diff --git a/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx index 3c2124fe..9dd61666 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/components/proforma-status-badge.tsx @@ -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 diff --git a/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx b/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx index 5d8205e9..9c0eb74b 100644 --- a/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx +++ b/modules/customer-invoices/src/web/proformas/pages/update/proforma-update-comp.tsx @@ -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, diff --git a/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts b/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts new file mode 100644 index 00000000..1c245784 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/adapters/get-proforma-by-id.adapter.ts @@ -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), + }; +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/adapters/index.ts b/modules/customer-invoices/src/web/proformas/shared/adapters/index.ts index 909bb6ed..e69de29b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/adapters/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/adapters/index.ts @@ -1 +0,0 @@ -export * from "./list-proformas.adapter"; diff --git a/modules/customer-invoices/src/web/proformas/shared/adapters/list-proformas.adapter.ts b/modules/customer-invoices/src/web/proformas/shared/adapters/list-proformas.adapter.ts index 9cbf3c1d..92913d2a 100644 --- a/modules/customer-invoices/src/web/proformas/shared/adapters/list-proformas.adapter.ts +++ b/modules/customer-invoices/src/web/proformas/shared/adapters/list-proformas.adapter.ts @@ -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, }; }, }; diff --git a/modules/customer-invoices/src/web/proformas/shared/adapters/proforma-to-list-row-patch.adapter.ts b/modules/customer-invoices/src/web/proformas/shared/adapters/proforma-to-list-row-patch.adapter.ts new file mode 100644 index 00000000..c2ef7432 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/adapters/proforma-to-list-row-patch.adapter.ts @@ -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 & Pick; + +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, + }; + }, +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts index cfbb8705..c617528b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts @@ -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 { - return dataSource.custom({ - path: `proformas/${proformaId}/status`, + params: ChangeProformaStatusByIdParams +): Promise { + const { id, data } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.custom({ + path: `proformas/${id}/status`, method: "patch", - data: { new_status: newStatus }, + data, }); } diff --git a/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts new file mode 100644 index 00000000..7e7b2ee9 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts @@ -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 => { + const { id, data } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.createOne( + "proformas", + data + ); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts new file mode 100644 index 00000000..8208fbda --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts @@ -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 => { + const { id, signal } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.deleteOne("proformas", id, { signal }); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-ip.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-ip.api.ts deleted file mode 100644 index 180ebe64..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-ip.api.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { IDataSource } from "@erp/core/client"; - -export async function deleteProformaById( - dataSource: IDataSource, - proformaId: string -): Promise { - await dataSource.deleteOne("proformas", proformaId); -} diff --git a/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts new file mode 100644 index 00000000..9f1cc308 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts @@ -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 { + const { id, signal } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.getOne("proformas", id, { signal }); +} diff --git a/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-ip.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-ip.api.ts deleted file mode 100644 index 9e890482..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-ip.api.ts +++ /dev/null @@ -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("proformas", id, { signal }); - return response; -} diff --git a/modules/customer-invoices/src/web/proformas/shared/api/index.ts b/modules/customer-invoices/src/web/proformas/shared/api/index.ts index b6482e68..b00e6b8f 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/index.ts @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts new file mode 100644 index 00000000..db9eea2f --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts @@ -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 => { + const { id } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.custom, IssueProformaByIdResponseDTO>({ + path: `proformas/${id}/issue`, + method: "put", + data: {}, + }); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts new file mode 100644 index 00000000..124ea6cd --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts @@ -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 { + const { criteria, signal } = params || { criteria: undefined, signal: undefined }; + return dataSource.getList("proformas", { + signal, + ...criteria, + }); +} diff --git a/modules/customer-invoices/src/web/proformas/shared/api/list-proformas.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/list-proformas.api.ts deleted file mode 100644 index a9545fe1..00000000 --- a/modules/customer-invoices/src/web/proformas/shared/api/list-proformas.api.ts +++ /dev/null @@ -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("proformas", { - signal, - ...criteria, - }); - - return response; -} diff --git a/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts new file mode 100644 index 00000000..8f9679d6 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts @@ -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 => { + const { + id, + data: { format = "PDF" }, + } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.custom({ + path: `proformas/${id}/report`, + method: "get", + data: { + format, + }, + }); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts new file mode 100644 index 00000000..7e9a6afc --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts @@ -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 => { + const { id, data } = params; + + if (!id) throw new Error("proformaId is required"); + + return dataSource.updateOne( + "proformas", + id, + data + ); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/index.ts b/modules/customer-invoices/src/web/proformas/shared/constants/index.ts new file mode 100644 index 00000000..44579d7c --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/constants/index.ts @@ -0,0 +1 @@ +export * from "./payment-method-options.constants"; diff --git a/modules/customer-invoices/src/web/proformas/shared/constants/payment-method-options.constants.ts b/modules/customer-invoices/src/web/proformas/shared/constants/payment-method-options.constants.ts new file mode 100644 index 00000000..2d8d4d88 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/constants/payment-method-options.constants.ts @@ -0,0 +1,5 @@ +export const PAYMENT_METHOD_OPTIONS = [ + { label: "Transferencia", value: "6" }, + { label: "Transferencia bancaria", value: "15" }, + { label: "Domiciliación bancaria", value: "14" }, +]; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/forms/index.ts b/modules/customer-invoices/src/web/proformas/shared/entities/forms/index.ts new file mode 100644 index 00000000..241f12a7 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/forms/index.ts @@ -0,0 +1 @@ +export * from "./proforma-item-form.entity"; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/forms/proforma-item-form.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/forms/proforma-item-form.entity.ts new file mode 100644 index 00000000..25a9f25b --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/forms/proforma-item-form.entity.ts @@ -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; +} diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/index.ts b/modules/customer-invoices/src/web/proformas/shared/entities/index.ts index d6321d22..ca6e9619 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/index.ts @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts new file mode 100644 index 00000000..07bbe68c --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-item.entity.ts @@ -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; +} diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list-row.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list-row.entity.ts index 1d564db6..2ae04919 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list-row.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list-row.entity.ts @@ -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; } diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list.entity.ts index 7094733b..9eed4d38 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-list.entity.ts @@ -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; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-recipient.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-recipient.entity.ts new file mode 100644 index 00000000..6293fa14 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-recipient.entity.ts @@ -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; +} diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts index fcb59539..87d50f4f 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts @@ -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.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}`; diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts new file mode 100644 index 00000000..9803974f --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma-tax-summary.entity.ts @@ -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; +} diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts b/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts new file mode 100644 index 00000000..679258bd --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared/entities/proforma.entity.ts @@ -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[]; +} diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts index 92acba5d..e69de29b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts @@ -1,3 +0,0 @@ -export * from "./use-change-proforma-status-mutation"; -export * from "./use-delete-proforma-mutation"; -export * from "./use-list-proformas-query"; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/keys.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/keys.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/to-validation-errors.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/to-validation-errors.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-create-mutation.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-create-mutation.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts new file mode 100644 index 00000000..e69de29b diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts index 21cb18a6..e69de29b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts @@ -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 => { - const dataSource = useDataSource(); - const enabled = options?.enabled ?? Boolean(proformaId); - - return useQuery({ - 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); -} -*/ diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-update-mutation.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-update-mutation.ts index e1f7570c..e69de29b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-update-mutation.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-update-mutation.ts @@ -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; -}; - -export const useProformaUpdateMutation = () => { - const queryClient = useQueryClient(); - const dataSource = useDataSource(); - const schema = UpdateProformaByIdRequestSchema; - - return useMutation({ - 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); - }, - }); -}; diff --git a/modules/customer-invoices/src/web/proformas/shared/index.ts b/modules/customer-invoices/src/web/proformas/shared/index.ts index da2a3add..e69de29b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/index.ts @@ -1,4 +0,0 @@ -export * from "./api"; -export * from "./entities"; -export * from "./hooks"; -export * from "./ui"; diff --git a/modules/customer-invoices/src/web/proformas/shared2/hooks/index.ts b/modules/customer-invoices/src/web/proformas/shared2/hooks/index.ts new file mode 100644 index 00000000..92acba5d --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared2/hooks/index.ts @@ -0,0 +1,3 @@ +export * from "./use-change-proforma-status-mutation"; +export * from "./use-delete-proforma-mutation"; +export * from "./use-list-proformas-query"; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-change-proforma-status-mutation.ts b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-change-proforma-status-mutation.ts similarity index 90% rename from modules/customer-invoices/src/web/proformas/shared/hooks/use-change-proforma-status-mutation.ts rename to modules/customer-invoices/src/web/proformas/shared2/hooks/use-change-proforma-status-mutation.ts index c33f4039..66014c53 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-change-proforma-status-mutation.ts +++ b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-change-proforma-status-mutation.ts @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-delete-proforma-mutation.ts b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-delete-proforma-mutation.ts similarity index 100% rename from modules/customer-invoices/src/web/proformas/shared/hooks/use-delete-proforma-mutation.ts rename to modules/customer-invoices/src/web/proformas/shared2/hooks/use-delete-proforma-mutation.ts diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.tsx b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-list-proformas-query.tsx similarity index 100% rename from modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.tsx rename to modules/customer-invoices/src/web/proformas/shared2/hooks/use-list-proformas-query.tsx diff --git a/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-get-query.ts b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-get-query.ts new file mode 100644 index 00000000..21cb18a6 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-get-query.ts @@ -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 => { + const dataSource = useDataSource(); + const enabled = options?.enabled ?? Boolean(proformaId); + + return useQuery({ + 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); +} +*/ diff --git a/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-update-mutation.ts b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-update-mutation.ts new file mode 100644 index 00000000..e1f7570c --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared2/hooks/use-proforma-update-mutation.ts @@ -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; +}; + +export const useProformaUpdateMutation = () => { + const queryClient = useQueryClient(); + const dataSource = useDataSource(); + const schema = UpdateProformaByIdRequestSchema; + + return useMutation({ + 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); + }, + }); +}; diff --git a/modules/customer-invoices/src/web/proformas/shared2/index.ts b/modules/customer-invoices/src/web/proformas/shared2/index.ts new file mode 100644 index 00000000..da2a3add --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/shared2/index.ts @@ -0,0 +1,4 @@ +export * from "./api"; +export * from "./entities"; +export * from "./hooks"; +export * from "./ui"; diff --git a/modules/customer-invoices/src/web/proformas/shared/ui/blocks/index.ts b/modules/customer-invoices/src/web/proformas/shared2/ui/blocks/index.ts similarity index 100% rename from modules/customer-invoices/src/web/proformas/shared/ui/blocks/index.ts rename to modules/customer-invoices/src/web/proformas/shared2/ui/blocks/index.ts diff --git a/modules/customer-invoices/src/web/proformas/shared/ui/blocks/proforma-layout.tsx b/modules/customer-invoices/src/web/proformas/shared2/ui/blocks/proforma-layout.tsx similarity index 100% rename from modules/customer-invoices/src/web/proformas/shared/ui/blocks/proforma-layout.tsx rename to modules/customer-invoices/src/web/proformas/shared2/ui/blocks/proforma-layout.tsx diff --git a/modules/customer-invoices/src/web/proformas/shared/ui/index.ts b/modules/customer-invoices/src/web/proformas/shared2/ui/index.ts similarity index 100% rename from modules/customer-invoices/src/web/proformas/shared/ui/index.ts rename to modules/customer-invoices/src/web/proformas/shared2/ui/index.ts diff --git a/modules/customer-invoices/src/web/proformas/update/entities/index.ts b/modules/customer-invoices/src/web/proformas/update/entities/index.ts new file mode 100644 index 00000000..08b24fd7 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/index.ts @@ -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"; diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-form.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-form.entity.ts new file mode 100644 index 00000000..9c8cc110 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-item-update-form.entity.ts @@ -0,0 +1,9 @@ +export interface ProformaItemUpdateForm { + id: string; + position: string; + description: string; + quantity: number; + unitAmount: number; + discountPercentage: number; + taxes: string; +} diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form-defaults.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form-defaults.ts new file mode 100644 index 00000000..9e045cd1 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form-defaults.ts @@ -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", +}; diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts new file mode 100644 index 00000000..72a4be89 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.entity.ts @@ -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[]; +} diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts new file mode 100644 index 00000000..3849da9f --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-form.schema.ts @@ -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(), +}); diff --git a/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts new file mode 100644 index 00000000..36c4a492 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/update/entities/proforma-update-patch.entity.ts @@ -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;