diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f2acddfd..88c58a8e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,13 @@ { - "recommendations": ["biomejs.biome", "cweijan.vscode-mysql-client2"] + "recommendations": [ + "biomejs.biome", + "cweijan.vscode-mysql-client2", + + "bradlc.vscode-tailwindcss", + "ms-vscode.vscode-typescript-next", + "yzhang.markdown-all-in-one", + "ms-vscode.vscode-json", + "formulahendry.auto-rename-tag", + "christian-kohler.path-intellisense" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 761ce94b..46c1f86e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,15 +20,6 @@ "strings": "on" }, - "editor.codeActionsOnSave": { - "source.fixAll": "always", - "source.fixAll.biome": "always", - "source.removeUnusedImports": "always" - }, - "editor.defaultFormatter": "biomejs.biome", - "editor.formatOnSave": true, - "editor.formatOnPaste": false, - "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }, @@ -48,8 +39,16 @@ "editor.defaultFormatter": "biomejs.biome" }, - "prettier.enable": false, - "eslint.enable": false, + // Biome + "biome.enabled": true, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "editor.formatOnPaste": false, + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit", + "source.removeUnusedImports": "always" + }, // other vscode settings "[handlebars]": { diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 8d43c89c..f01a28af 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,7 +1,9 @@ -import { DateTime } from "luxon"; import http from "node:http"; import os from "node:os"; + +import { DateTime } from "luxon"; import { z } from "zod/v4"; + import { createApp } from "./app.ts"; import { tryConnectToDatabase } from "./config/database.ts"; import { ENV } from "./config/index.ts"; diff --git a/biome.json b/biome.json index 4522c822..27d2b0c0 100644 --- a/biome.json +++ b/biome.json @@ -1,69 +1,366 @@ { - "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json", - "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, - "files": { "ignoreUnknown": false, "includes": ["**", "!**/dist"] }, + "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + }, + "files": { + "ignoreUnknown": true, + "includes": [ + "**/*.js", + "**/*.jsx", + "**/*.ts", + "**/*.tsx", + "**/*.json", + "**/*.css", + "**/*.scss" + ], + "experimentalScannerIgnores": [ + "**/node_modules/**", + "**/.next/**", + "**/dist/**", + "**/build/**", + "**/coverage/**", + "**/.turbo/**", + "**/out/**", + "**/.env*", + "**/public/**", + "**/*.d.ts", + "**/storybook-static/**", + "**/.vercel/**" + ] + }, "formatter": { "enabled": true, - "useEditorconfig": true, "formatWithErrors": false, "indentStyle": "space", "indentWidth": 2, - "lineEnding": "lf", "lineWidth": 100, - "attributePosition": "auto", - "bracketSpacing": true + "lineEnding": "lf", + "attributePosition": "auto" }, - "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, "rules": { "recommended": true, - "correctness": { - "useExhaustiveDependencies": "info", - "noUnreachable": "warn" - }, - "complexity": { - "noForEach": "off", - "noBannedTypes": "info", - "noUselessFragments": "off", - "useOptionalChain": "off", - "noThisInStatic": "off" - }, - "suspicious": { - "noImplicitAnyLet": "info", - "noExplicitAny": "info", - "noArrayIndexKey": "info" - }, "style": { - "useImportType": "off", - "noInferrableTypes": "off", + "noDefaultExport": "off", + "noImplicitBoolean": "off", + "noInferrableTypes": "error", + "noNamespace": "error", + "noNegationElse": "warn", "noNonNullAssertion": "info", - "noUselessElse": "off", "noParameterAssign": "error", - "useAsConstAssertion": "error", + "noUnusedTemplateLiteral": "error", + "noUselessElse": "warn", + "useBlockStatements": "off", + "useCollapsedElseIf": "error", + "useConst": "error", "useDefaultParameterLast": "error", "useEnumInitializers": "error", + "useExportType": "error", + "useFilenamingConvention": { + "level": "error", + "options": { + "strictCase": false, + "requireAscii": true, + "filenameCases": ["kebab-case"] + } + }, + "useForOf": "error", + "useFragmentSyntax": "error", + "useImportType": "error", + "useNamingConvention": { + "level": "off", + "options": { + "strictCase": false, + "conventions": [ + { + "selector": { + "kind": "function" + }, + "formats": ["camelCase", "PascalCase"] + }, + { + "selector": { + "kind": "variable" + }, + "formats": ["camelCase", "PascalCase", "CONSTANT_CASE"] + }, + { + "selector": { + "kind": "typeLike" + }, + "formats": ["PascalCase"] + } + ] + } + }, + "useNodejsImportProtocol": "error", + "useNumberNamespace": "error", "useSelfClosingElements": "error", + "useShorthandAssign": "error", + "useShorthandFunctionType": "error", "useSingleVarDeclarator": "error", - "noUnusedTemplateLiteral": "error", - "useNumberNamespace": "error" + "useTemplate": "error", + "useThrowOnlyError": "error" + }, + "suspicious": { + "noExplicitAny": "error", + "noDebugger": "error", + "noDuplicateJsxProps": "error", + "noDuplicateObjectKeys": "error", + "noDuplicateParameters": "error", + "noShadowRestrictedNames": "error", + "noSparseArray": "error", + "noUnsafeNegation": "error", + "noArrayIndexKey": "warn", + "noAssignInExpressions": "error", + "noCatchAssign": "error", + "noClassAssign": "error", + "noCommentText": "error", + "noCompareNegZero": "error", + "noConsole": "warn", + "noConstEnum": "error", + "noControlCharactersInRegex": "error", + "noDoubleEquals": "error", + "noDuplicateCase": "error", + "noEmptyBlockStatements": "error", + "noFallthroughSwitchClause": "error", + "noFunctionAssign": "error", + "noGlobalAssign": "error", + "noLabelVar": "error", + "noMisleadingCharacterClass": "error", + "noPrototypeBuiltins": "error", + "noRedeclare": "error", + "noSelfCompare": "error", + "noUnknownAtRules": "off" + }, + "correctness": { + "noConstAssign": "error", + "noConstructorReturn": "error", + "noEmptyPattern": "error", + "noInvalidConstructorSuper": "error", + "noInvalidUseBeforeDeclaration": "error", + "noSelfAssign": "error", + "noSetterReturn": "error", + "noSwitchDeclarations": "error", + "noUnreachable": "error", + "noUnreachableSuper": "error", + "noUnsafeFinally": "error", + "noUnsafeOptionalChaining": "error", + "noUnusedLabels": "error", + "noUnusedVariables": "warn", + "useExhaustiveDependencies": "error", + "useHookAtTopLevel": "error", + "useIsNan": "error", + "useJsxKeyInIterable": "error", + "useValidForDirection": "error", + "useYield": "error" + }, + "complexity": { + "noBannedTypes": "error", + "noExcessiveCognitiveComplexity": { + "level": "warn", + "options": { + "maxAllowedComplexity": 15 + } + }, + "noForEach": "warn", + "noStaticOnlyClass": "error", + "noThisInStatic": "error", + "noUselessCatch": "error", + "noUselessConstructor": "error", + "noUselessFragments": "error", + "noUselessLabel": "error", + "noUselessRename": "error", + "noUselessSwitchCase": "error", + "noUselessTernary": "error", + "noUselessTypeConstraint": "error", + "noVoid": "error", + "useFlatMap": "error", + "useLiteralKeys": "error", + "useOptionalChain": "error", + "useSimpleNumberKeys": "error", + "useSimplifiedLogicExpression": "error" + }, + "security": { + "noDangerouslySetInnerHtml": "error", + "noDangerouslySetInnerHtmlWithChildren": "error", + "noGlobalEval": "error" }, "a11y": { - "useSemanticElements": "info" + "noAccessKey": "error", + "noAriaHiddenOnFocusable": "error", + "noAriaUnsupportedElements": "error", + "noAutofocus": "error", + "noDistractingElements": "error", + "noHeaderScope": "error", + "noInteractiveElementToNoninteractiveRole": "error", + "noNoninteractiveElementToInteractiveRole": "error", + "noNoninteractiveTabindex": "error", + "noPositiveTabindex": "error", + "noRedundantAlt": "error", + "noRedundantRoles": "error", + "useFocusableInteractive": "error", + "useIframeTitle": "error", + "useKeyWithClickEvents": "error", + "useKeyWithMouseEvents": "error", + "useMediaCaption": "error", + "useSemanticElements": "error", + "useValidAnchor": "error", + "useValidAriaProps": "error", + "useValidAriaValues": "error", + "useValidLang": "error" + }, + "performance": { + "noAccumulatingSpread": "warn", + "noDelete": "error" + } + }, + "domains": { + "next": "all", + "react": "recommended" + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": { + "level": "on", + "options": { + "groups": [ + ":URL:", + ":BLANK_LINE:", + [":BUN:", ":NODE:"], + ":BLANK_LINE:", + ":PACKAGE_WITH_PROTOCOL:", + ":BLANK_LINE:", + [":PACKAGE:"], + ":BLANK_LINE:", + ["!@/**", "!#*", "!~*", "!$*", "!%*"], + ":BLANK_LINE:", + ["@/**", "#*", "~*", "$*", "%*"], + ":BLANK_LINE:", + [":PATH:", "!./**", "!../**"], + ":BLANK_LINE:", + ["../**"], + ":BLANK_LINE:", + ["./**"] + ], + "identifierOrder": "lexicographic" + } + }, + "useSortedAttributes": "on" } } }, "javascript": { "formatter": { - "jsxQuoteStyle": "single", - "quoteProperties": "asNeeded", - "trailingCommas": "es5", - "semicolons": "always", "arrowParentheses": "always", "bracketSameLine": false, + "bracketSpacing": true, + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", "quoteStyle": "double", - "attributePosition": "auto", - "bracketSpacing": true + "semicolons": "always", + "trailingCommas": "es5" + }, + "globals": ["console", "process", "__dirname", "__filename"] + }, + "json": { + "formatter": { + "trailingCommas": "none", + "indentStyle": "space", + "indentWidth": 2 } - } + }, + "css": { + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100, + "quoteStyle": "double" + }, + "linter": { + "enabled": true + } + }, + "overrides": [ + { + "includes": ["**/*.test.{js,ts,tsx}", "**/*.spec.{js,ts,tsx}", "**/__tests__/**"], + "linter": { + "rules": { + "suspicious": { + "noExplicitAny": "off", + "noConsole": "off" + }, + "style": { + "noNonNullAssertion": "off" + } + } + } + }, + { + "includes": ["**/next.config.{js,ts}", "**/tailwind.config.{js,ts}", "**/*.config.{js,ts}"], + "linter": { + "rules": { + "style": { + "noDefaultExport": "off" + }, + "suspicious": { + "noExplicitAny": "off" + } + } + } + }, + { + "includes": ["**/pages/**", "**/app/**/page.{tsx,jsx}", "**/app/**/layout.{tsx,jsx}"], + "linter": { + "rules": { + "style": { + "noDefaultExport": "off" + } + } + } + }, + { + "includes": ["**/*.d.ts", "**/lib/env/*.ts"], + "linter": { + "rules": { + "style": { + "noNamespace": "off", + "useNamingConvention": { + "level": "error", + "options": { + "strictCase": false, + "conventions": [ + { + "selector": { + "kind": "objectLiteralProperty" + }, + "formats": ["CONSTANT_CASE", "camelCase"] + } + ] + } + } + }, + "suspicious": { + "noExplicitAny": "error" + }, + "complexity": { + "noExcessiveCognitiveComplexity": { + "level": "error", + "options": { + "maxAllowedComplexity": 12 + } + } + } + } + } + } + ] } diff --git a/modules/customer-invoices/src/api/application/helpers/format-payment_method-dto.ts b/modules/customer-invoices/src/api/application/helpers/format-payment_method-dto.ts index 38e48e52..52ae9514 100644 --- a/modules/customer-invoices/src/api/application/helpers/format-payment_method-dto.ts +++ b/modules/customer-invoices/src/api/application/helpers/format-payment_method-dto.ts @@ -1,7 +1,7 @@ -import { GetCustomerInvoiceByIdResponseDTO } from "@erp/customer-invoices/common"; +import type { GetIssueInvoiceByIdResponseDTO } from "@erp/customer-invoices/common"; export function formatPaymentMethodDTO( - paymentMethod?: GetCustomerInvoiceByIdResponseDTO["payment_method"] + paymentMethod?: GetIssueInvoiceByIdResponseDTO["payment_method"] ) { if (!paymentMethod) { return null; diff --git a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice-items.full.presenter.ts b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice-items.full.presenter.ts index b25d59bb..16cd71b3 100644 --- a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice-items.full.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice-items.full.presenter.ts @@ -1,11 +1,12 @@ import { Presenter } from "@erp/core/api"; import { toEmptyString } from "@repo/rdx-ddd"; -import { ArrayElement } from "@repo/rdx-utils"; -import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto"; -import { CustomerInvoiceItem, CustomerInvoiceItems } from "../../../domain"; +import type { ArrayElement } from "@repo/rdx-utils"; + +import type { GetIssueInvoiceByIdResponseDTO } from "../../../../common/dto"; +import type { CustomerInvoiceItem, CustomerInvoiceItems } from "../../../domain"; type GetCustomerInvoiceItemByInvoiceIdResponseDTO = ArrayElement< - GetCustomerInvoiceByIdResponseDTO["items"] + GetIssueInvoiceByIdResponseDTO["items"] >; export class CustomerInvoiceItemsFullPresenter extends Presenter { @@ -48,7 +49,7 @@ export class CustomerInvoiceItemsFullPresenter extends Presenter { }; } - toOutput(invoiceItems: CustomerInvoiceItems): GetCustomerInvoiceByIdResponseDTO["items"] { + toOutput(invoiceItems: CustomerInvoiceItems): GetIssueInvoiceByIdResponseDTO["items"] { return invoiceItems.map(this._mapItem); } } diff --git a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts index 2ccfab59..a5274a6b 100644 --- a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts @@ -1,15 +1,17 @@ import { Presenter } from "@erp/core/api"; import { toEmptyString } from "@repo/rdx-ddd"; -import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto"; -import { CustomerInvoice } from "../../../domain"; -import { CustomerInvoiceItemsFullPresenter } from "./customer-invoice-items.full.presenter"; -import { RecipientInvoiceFullPresenter } from "./recipient-invoice.full.representer"; + +import type { GetIssueInvoiceByIdResponseDTO } from "../../../../common/dto"; +import type { CustomerInvoice } from "../../../domain"; + +import type { CustomerInvoiceItemsFullPresenter } from "./customer-invoice-items.full.presenter"; +import type { RecipientInvoiceFullPresenter } from "./recipient-invoice.full.representer"; export class CustomerInvoiceFullPresenter extends Presenter< CustomerInvoice, - GetCustomerInvoiceByIdResponseDTO + GetIssueInvoiceByIdResponseDTO > { - toOutput(invoice: CustomerInvoice): GetCustomerInvoiceByIdResponseDTO { + toOutput(invoice: CustomerInvoice): GetIssueInvoiceByIdResponseDTO { const itemsPresenter = this.presenterRegistry.getPresenter({ resource: "customer-invoice-items", projection: "FULL", diff --git a/modules/customer-invoices/src/api/application/presenters/domain/recipient-invoice.full.representer.ts b/modules/customer-invoices/src/api/application/presenters/domain/recipient-invoice.full.representer.ts index 1ed19743..1da9f750 100644 --- a/modules/customer-invoices/src/api/application/presenters/domain/recipient-invoice.full.representer.ts +++ b/modules/customer-invoices/src/api/application/presenters/domain/recipient-invoice.full.representer.ts @@ -1,9 +1,10 @@ import { Presenter } from "@erp/core/api"; import { DomainValidationError, toEmptyString } from "@repo/rdx-ddd"; -import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto"; -import { CustomerInvoice, InvoiceRecipient } from "../../../domain"; -type GetRecipientInvoiceByInvoiceIdResponseDTO = GetCustomerInvoiceByIdResponseDTO["recipient"]; +import type { GetIssueInvoiceByIdResponseDTO } from "../../../../common/dto"; +import type { CustomerInvoice, InvoiceRecipient } from "../../../domain"; + +type GetRecipientInvoiceByInvoiceIdResponseDTO = GetIssueInvoiceByIdResponseDTO["recipient"]; export class RecipientInvoiceFullPresenter extends Presenter { toOutput(invoice: CustomerInvoice): GetRecipientInvoiceByInvoiceIdResponseDTO { diff --git a/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice-items.report.presenter.ts b/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice-items.report.presenter.ts index 6aa964e7..88e7fb89 100644 --- a/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice-items.report.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice-items.report.presenter.ts @@ -1,9 +1,10 @@ -import { IPresenterOutputParams, Presenter } from "@erp/core/api"; -import { GetCustomerInvoiceByIdResponseDTO } from "@erp/customer-invoices/common"; -import { ArrayElement } from "@repo/rdx-utils"; -import { FormatMoneyOptions, formatMoneyDTO, formatQuantityDTO } from "../../helpers"; +import { type IPresenterOutputParams, Presenter } from "@erp/core/api"; +import type { GetIssueInvoiceByIdResponseDTO } from "@erp/customer-invoices/common"; +import type { ArrayElement } from "@repo/rdx-utils"; -type CustomerInvoiceItemsDTO = GetCustomerInvoiceByIdResponseDTO["items"]; +import { type FormatMoneyOptions, formatMoneyDTO, formatQuantityDTO } from "../../helpers"; + +type CustomerInvoiceItemsDTO = GetIssueInvoiceByIdResponseDTO["items"]; type CustomerInvoiceItemDTO = ArrayElement; export class CustomerInvoiceItemsReportPersenter extends Presenter< diff --git a/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice.report.presenter.ts b/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice.report.presenter.ts index b944086b..cdedd869 100644 --- a/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice.report.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/queries/customer-invoice.report.presenter.ts @@ -1,7 +1,8 @@ import { Presenter } from "@erp/core/api"; -import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto"; + +import type { GetIssueInvoiceByIdResponseDTO } from "../../../../common/dto"; import { - FormatMoneyOptions, + type FormatMoneyOptions, formatDateDTO, formatMoneyDTO, formatPercentageDTO, @@ -9,10 +10,10 @@ import { import { formatPaymentMethodDTO } from "../../helpers/format-payment_method-dto"; export class CustomerInvoiceReportPresenter extends Presenter< - GetCustomerInvoiceByIdResponseDTO, + GetIssueInvoiceByIdResponseDTO, unknown > { - toOutput(invoiceDTO: GetCustomerInvoiceByIdResponseDTO) { + toOutput(invoiceDTO: GetIssueInvoiceByIdResponseDTO) { const itemsPresenter = this.presenterRegistry.getPresenter({ resource: "customer-invoice-items", projection: "REPORT", diff --git a/modules/customer-invoices/src/api/application/presenters/queries/list-customer-invoices.presenter.ts b/modules/customer-invoices/src/api/application/presenters/queries/list-customer-invoices.presenter.ts index aeb8d6cd..9f84cad6 100644 --- a/modules/customer-invoices/src/api/application/presenters/queries/list-customer-invoices.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/queries/list-customer-invoices.presenter.ts @@ -1,15 +1,16 @@ import { Presenter } from "@erp/core/api"; -import { Criteria } from "@repo/rdx-criteria/server"; +import type { Criteria } from "@repo/rdx-criteria/server"; import { toEmptyString } from "@repo/rdx-ddd"; -import { ArrayElement, Collection } from "@repo/rdx-utils"; -import { ListCustomerInvoicesResponseDTO } from "../../../../common/dto"; -import { CustomerInvoiceListDTO } from "../../../infrastructure"; +import type { ArrayElement, Collection } from "@repo/rdx-utils"; + +import type { ListIssueInvoicesResponseDTO } from "../../../../common/dto"; +import type { CustomerInvoiceListDTO } from "../../../infrastructure"; export class ListCustomerInvoicesPresenter extends Presenter { protected _mapInvoice(invoice: CustomerInvoiceListDTO) { const recipientDTO = invoice.recipient.toObjectString(); - const invoiceDTO: ArrayElement = { + const invoiceDTO: ArrayElement = { id: invoice.id.toString(), company_id: invoice.companyId.toString(), is_proforma: invoice.isProforma, @@ -49,7 +50,7 @@ export class ListCustomerInvoicesPresenter extends Presenter { toOutput(params: { customerInvoices: Collection; criteria: Criteria; - }): ListCustomerInvoicesResponseDTO { + }): ListIssueInvoicesResponseDTO { const { customerInvoices, criteria } = params; const invoices = customerInvoices.map((invoice) => this._mapInvoice(invoice)); diff --git a/modules/customer-invoices/src/api/application/services/customer-invoice-application.service.ts b/modules/customer-invoices/src/api/application/services/customer-invoice-application.service.ts index 9832e5e7..81f334cc 100644 --- a/modules/customer-invoices/src/api/application/services/customer-invoice-application.service.ts +++ b/modules/customer-invoices/src/api/application/services/customer-invoice-application.service.ts @@ -1,8 +1,9 @@ -import { Criteria } from "@repo/rdx-criteria/server"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Collection, Maybe, Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { +import type { Criteria } from "@repo/rdx-criteria/server"; +import type { UniqueID } from "@repo/rdx-ddd"; +import { type Collection, Maybe, Result } from "@repo/rdx-utils"; +import type { Transaction } from "sequelize"; + +import type { CustomerInvoiceNumber, CustomerInvoiceSerie, CustomerInvoiceStatus, @@ -10,11 +11,11 @@ import { } from "../../domain"; import { CustomerInvoice, - CustomerInvoicePatchProps, - CustomerInvoiceProps, + type CustomerInvoicePatchProps, + type CustomerInvoiceProps, } from "../../domain/aggregates"; -import { ICustomerInvoiceRepository } from "../../domain/repositories"; -import { CustomerInvoiceListDTO } from "../../infrastructure"; +import type { ICustomerInvoiceRepository } from "../../domain/repositories"; +import type { CustomerInvoiceListDTO } from "../../infrastructure"; export class CustomerInvoiceApplicationService { constructor( @@ -57,57 +58,57 @@ export class CustomerInvoiceApplicationService { * * @param companyId - Identificador de la empresa a la que pertenece la proforma. * @param props - Las propiedades ya validadas para crear la proforma. - * @param invoiceId - Identificador UUID de la proforma (opcional). + * @param proformaId - Identificador UUID de la proforma (opcional). * @returns Result - El agregado construido o un error si falla la creación. */ buildProformaInCompany( companyId: UniqueID, props: Omit, - invoiceId?: UniqueID + proformaId?: UniqueID ): Result { - return CustomerInvoice.create({ ...props, companyId }, invoiceId); + return CustomerInvoice.create({ ...props, companyId }, proformaId); } /** - * Guarda una nueva factura y devuelve la factura guardada. + * Guarda una nueva proforma y devuelve la proforma guardada. * - * @param companyId - Identificador de la empresa a la que pertenece la factura. - * @param invoice - El agregado a guardar. + * @param companyId - Identificador de la empresa a la que pertenece la proforma. + * @param proforma - La proforma a guardar. * @param transaction - Transacción activa para la operación. - * @returns Result - El agregado guardado o un error si falla la operación. + * @returns Result - La proforma guardada o un error si falla la operación. */ async createInvoiceInCompany( companyId: UniqueID, - invoice: CustomerInvoice, + proforma: CustomerInvoice, transaction: Transaction ): Promise> { - const result = await this.repository.create(invoice, transaction); + const result = await this.repository.create(proforma, transaction); if (result.isFailure) { return Result.fail(result.error); } - return this.getInvoiceByIdInCompany(companyId, invoice.id, transaction); + return this.getProformaByIdInCompany(companyId, proforma.id, transaction); } /** - * Actualiza una factura existente y devuelve la factura actualizada. + * Actualiza una proforma existente y devuelve la proforma actualizada. * * @param companyId - Identificador de la empresa a la que pertenece la factura. - * @param invoice - El agregado a guardar. + * @param proforma - La proforma a guardar. * @param transaction - Transacción activa para la operación. - * @returns Result - El agregado guardado o un error si falla la operación. + * @returns Result - La proforma guardada o un error si falla la operación. */ - async updateInvoiceInCompany( + async updateProformaInCompany( companyId: UniqueID, - invoice: CustomerInvoice, + proforma: CustomerInvoice, transaction: Transaction ): Promise> { - const result = await this.repository.update(invoice, transaction); + const result = await this.repository.update(proforma, transaction); if (result.isFailure) { return Result.fail(result.error); } - return this.getInvoiceByIdInCompany(companyId, invoice.id, transaction); + return this.getProformaByIdInCompany(companyId, proforma.id, transaction); } /** @@ -151,12 +152,35 @@ export class CustomerInvoiceApplicationService { * @param transaction - Transacción activa para la operación. * @returns Result - Factura encontrada o error. */ - async getInvoiceByIdInCompany( + async getIssueInvoiceByIdInCompany( companyId: UniqueID, invoiceId: UniqueID, transaction?: Transaction ): Promise> { - return await this.repository.getByIdInCompany(companyId, invoiceId, transaction); + return await this.repository.getByIdInCompany(companyId, invoiceId, transaction, { + where: { + is_proforma: false, + }, + }); + } + + /** + * Recupera una proforma por su identificador único. + * + * @param proformaId - Identificador UUID de la proforma. + * @param transaction - Transacción activa para la operación. + * @returns Result - proforma encontrada o error. + */ + async getProformaByIdInCompany( + companyId: UniqueID, + proformaId: UniqueID, + transaction?: Transaction + ): Promise> { + return await this.repository.getByIdInCompany(companyId, proformaId, transaction, { + where: { + is_proforma: true, + }, + }); } /** @@ -164,18 +188,18 @@ export class CustomerInvoiceApplicationService { * No lo guarda en el repositorio. * * @param companyId - Identificador de la empresa a la que pertenece la factura. - * @param invoiceId - Identificador de la factura a actualizar. + * @param proformaId - Identificador de la factura a actualizar. * @param changes - Subconjunto de props válidas para aplicar. * @param transaction - Transacción activa para la operación. * @returns Result - Factura actualizada o error. */ - async patchInvoiceByIdInCompany( + async patchProformaByIdInCompany( companyId: UniqueID, - invoiceId: UniqueID, + proformaId: UniqueID, changes: CustomerInvoicePatchProps, transaction?: Transaction ): Promise> { - const invoiceResult = await this.getInvoiceByIdInCompany(companyId, invoiceId, transaction); + const invoiceResult = await this.getProformaByIdInCompany(companyId, proformaId, transaction); if (invoiceResult.isFailure) { return Result.fail(invoiceResult.error); diff --git a/modules/customer-invoices/src/api/application/use-cases/create/index.ts b/modules/customer-invoices/src/api/application/use-cases/create/index.ts deleted file mode 100644 index e92f8281..00000000 --- a/modules/customer-invoices/src/api/application/use-cases/create/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./create-customer-invoice.use-case"; diff --git a/modules/customer-invoices/src/api/application/use-cases/index.ts b/modules/customer-invoices/src/api/application/use-cases/index.ts index 7bd71784..457e40a2 100644 --- a/modules/customer-invoices/src/api/application/use-cases/index.ts +++ b/modules/customer-invoices/src/api/application/use-cases/index.ts @@ -1,7 +1,2 @@ -export * from "./change-status-customer-invoice.use-case"; -export * from "./create"; -export * from "./get-customer-invoice.use-case"; -export * from "./issue-customer-invoice.use-case"; -export * from "./list-customer-invoices.use-case"; -export * from "./report"; -export * from "./update"; +export * from "./issue-invoices"; +export * from "./proformas"; diff --git a/modules/customer-invoices/src/api/application/use-cases/get-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/get-issue-invoice.use-case.ts similarity index 67% rename from modules/customer-invoices/src/api/application/use-cases/get-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/issue-invoices/get-issue-invoice.use-case.ts index fb457b53..9b9b9276 100644 --- a/modules/customer-invoices/src/api/application/use-cases/get-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/get-issue-invoice.use-case.ts @@ -1,22 +1,23 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { CustomerInvoiceFullPresenter } from "../presenters/domain"; -import { CustomerInvoiceApplicationService } from "../services"; -type GetCustomerInvoiceUseCaseInput = { +import type { CustomerInvoiceFullPresenter } from "../../presenters/domain"; +import type { CustomerInvoiceApplicationService } from "../../services"; + +type GetIssueInvoiceUseCaseInput = { companyId: UniqueID; invoice_id: string; }; -export class GetCustomerInvoiceUseCase { +export class GetIssueInvoiceUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager, private readonly presenterRegistry: IPresenterRegistry ) {} - public execute(params: GetCustomerInvoiceUseCaseInput) { + public execute(params: GetIssueInvoiceUseCaseInput) { const { invoice_id, companyId } = params; const idOrError = UniqueID.create(invoice_id); @@ -32,17 +33,18 @@ export class GetCustomerInvoiceUseCase { return this.transactionManager.complete(async (transaction) => { try { - const invoiceOrError = await this.service.getInvoiceByIdInCompany( + const invoiceOrError = await this.service.getIssueInvoiceByIdInCompany( companyId, invoiceId, transaction ); + if (invoiceOrError.isFailure) { return Result.fail(invoiceOrError.error); } - const customerInvoice = invoiceOrError.data; - const dto = presenter.toOutput(customerInvoice); + const invoice = invoiceOrError.data; + const dto = presenter.toOutput(invoice); return Result.ok(dto); } catch (error: unknown) { diff --git a/modules/customer-invoices/src/api/application/use-cases/issue-invoices/index.ts b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/index.ts new file mode 100644 index 00000000..159e84e8 --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/index.ts @@ -0,0 +1,2 @@ +export * from "./get-issue-invoice.use-case"; +export * from "./report-issue-invoice.use-case"; diff --git a/modules/customer-invoices/src/api/application/use-cases/report/report-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/report-issue-invoice.use-case.ts similarity index 74% rename from modules/customer-invoices/src/api/application/use-cases/report/report-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/issue-invoices/report-issue-invoice.use-case.ts index 8c0343df..059153d0 100644 --- a/modules/customer-invoices/src/api/application/use-cases/report/report-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/issue-invoices/report-issue-invoice.use-case.ts @@ -1,22 +1,23 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { CustomerInvoiceApplicationService } from "../../services/customer-invoice-application.service"; -import { CustomerInvoiceReportPDFPresenter } from "./reporter"; -type ReportCustomerInvoiceUseCaseInput = { +import type { CustomerInvoiceApplicationService } from "../../services"; +import type { CustomerInvoiceReportPDFPresenter } from "../proformas"; + +type ReportIssueInvoiceUseCaseInput = { companyId: UniqueID; invoice_id: string; }; -export class ReportCustomerInvoiceUseCase { +export class ReportIssueInvoiceUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager, private readonly presenterRegistry: IPresenterRegistry ) {} - public async execute(params: ReportCustomerInvoiceUseCaseInput) { + public async execute(params: ReportIssueInvoiceUseCaseInput) { const { invoice_id, companyId } = params; const idOrError = UniqueID.create(invoice_id); @@ -34,11 +35,12 @@ export class ReportCustomerInvoiceUseCase { return this.transactionManager.complete(async (transaction) => { try { - const invoiceOrError = await this.service.getInvoiceByIdInCompany( + const invoiceOrError = await this.service.getIssueInvoiceByIdInCompany( companyId, invoiceId, transaction ); + if (invoiceOrError.isFailure) { return Result.fail(invoiceOrError.error); } diff --git a/modules/customer-invoices/src/api/application/use-cases/change-status-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/change-status-proforma.use-case.ts similarity index 77% rename from modules/customer-invoices/src/api/application/use-cases/change-status-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/change-status-proforma.use-case.ts index 98ec6af3..22227546 100644 --- a/modules/customer-invoices/src/api/application/use-cases/change-status-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/change-status-proforma.use-case.ts @@ -1,17 +1,18 @@ -import { ITransactionManager } from "@erp/core/api"; -import { ChangeStatusCustomerInvoiceByIdRequestDTO } from "@erp/customer-invoices/common"; +import type { ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { ProformaCustomerInvoiceDomainService } from "../../domain"; -import { CustomerInvoiceApplicationService } from "../services"; -type ChangeStatusCustomerInvoiceUseCaseInput = { +import type { ChangeStatusProformaByIdRequestDTO } from "../../../../common"; +import { ProformaCustomerInvoiceDomainService } from "../../../domain"; +import type { CustomerInvoiceApplicationService } from "../../services"; + +type ChangeStatusProformaUseCaseInput = { companyId: UniqueID; proforma_id: string; - dto: ChangeStatusCustomerInvoiceByIdRequestDTO; + dto: ChangeStatusProformaByIdRequestDTO; }; -export class ChangeStatusCustomerInvoiceUseCase { +export class ChangeStatusProformaUseCase { private readonly proformaDomainService: ProformaCustomerInvoiceDomainService; constructor( @@ -21,7 +22,7 @@ export class ChangeStatusCustomerInvoiceUseCase { this.proformaDomainService = new ProformaCustomerInvoiceDomainService(); } - public execute(params: ChangeStatusCustomerInvoiceUseCaseInput) { + public execute(params: ChangeStatusProformaUseCaseInput) { const { proforma_id, companyId, diff --git a/modules/customer-invoices/src/api/application/use-cases/create/create-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/create-proforma.use-case.ts similarity index 78% rename from modules/customer-invoices/src/api/application/use-cases/create/create-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/create-proforma.use-case.ts index b5d6c125..e0ab717e 100644 --- a/modules/customer-invoices/src/api/application/use-cases/create/create-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/create-proforma.use-case.ts @@ -1,19 +1,25 @@ -import { JsonTaxCatalogProvider } from "@erp/core"; -import { DuplicateEntityError, IPresenterRegistry, ITransactionManager } from "@erp/core/api"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { CreateCustomerInvoiceRequestDTO } from "../../../../common/dto"; -import { CustomerInvoiceFullPresenter } from "../../presenters"; -import { CustomerInvoiceApplicationService } from "../../services/customer-invoice-application.service"; -import { CreateCustomerInvoicePropsMapper } from "./map-dto-to-create-customer-invoice-props"; +import type { JsonTaxCatalogProvider } from "@erp/core"; +import { + DuplicateEntityError, + type IPresenterRegistry, + type ITransactionManager, +} from "@erp/core/api"; +import type { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; +import type { Transaction } from "sequelize"; -type CreateCustomerInvoiceUseCaseInput = { +import type { CreateProformaRequestDTO } from "../../../../../common"; +import type { CustomerInvoiceFullPresenter } from "../../../presenters"; +import type { CustomerInvoiceApplicationService } from "../../../services"; + +import { CreateCustomerInvoicePropsMapper } from "./map-dto-to-create-proforma-props"; + +type CreateProformaUseCaseInput = { companyId: UniqueID; - dto: CreateCustomerInvoiceRequestDTO; + dto: CreateProformaRequestDTO; }; -export class CreateCustomerInvoiceUseCase { +export class CreateProformaUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager, @@ -21,7 +27,7 @@ export class CreateCustomerInvoiceUseCase { private readonly taxCatalog: JsonTaxCatalogProvider ) {} - public async execute(params: CreateCustomerInvoiceUseCaseInput) { + public async execute(params: CreateProformaUseCaseInput) { const { dto, companyId } = params; const presenter = this.presenterRegistry.getPresenter({ resource: "customer-invoice", @@ -51,7 +57,7 @@ export class CreateCustomerInvoiceUseCase { // 3) Construir entidad de dominio const proformaProps = { ...props, - invoiceNumber: Maybe.some(newProformaNumber), + invoiceNumber: newProformaNumber, }; const buildResult = this.service.buildProformaInCompany(companyId, proformaProps, id); diff --git a/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/index.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/index.ts new file mode 100644 index 00000000..d1d4cd6d --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/index.ts @@ -0,0 +1 @@ +export * from "./create-proforma.use-case"; diff --git a/modules/customer-invoices/src/api/application/use-cases/create/map-dto-to-create-customer-invoice-props.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/map-dto-to-create-proforma-props.ts similarity index 86% rename from modules/customer-invoices/src/api/application/use-cases/create/map-dto-to-create-customer-invoice-props.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/map-dto-to-create-proforma-props.ts index ffec061a..25e1c31e 100644 --- a/modules/customer-invoices/src/api/application/use-cases/create/map-dto-to-create-customer-invoice-props.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/create-proforma/map-dto-to-create-proforma-props.ts @@ -1,39 +1,37 @@ -import { JsonTaxCatalogProvider } from "@erp/core"; +import type { JsonTaxCatalogProvider } from "@erp/core"; import { Tax } from "@erp/core/api"; import { CurrencyCode, DomainError, - extractOrPushError, LanguageCode, - maybeFromNullableVO, Percentage, TextValue, UniqueID, UtcDate, ValidationErrorCollection, - ValidationErrorDetail, + type ValidationErrorDetail, + extractOrPushError, + maybeFromNullableVO, } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; -import { - CreateCustomerInvoiceItemRequestDTO, - CreateCustomerInvoiceRequestDTO, -} from "../../../../common/dto"; + +import type { CreateProformaItemRequestDTO, CreateProformaRequestDTO } from "../../../../../common"; import { CustomerInvoiceItem, CustomerInvoiceItemDescription, - CustomerInvoiceItemProps, + type CustomerInvoiceItemProps, CustomerInvoiceItems, CustomerInvoiceNumber, - CustomerInvoiceProps, + type CustomerInvoiceProps, CustomerInvoiceSerie, CustomerInvoiceStatus, InvoicePaymentMethod, - InvoiceRecipient, + type InvoiceRecipient, ItemAmount, ItemDiscount, ItemQuantity, ItemTaxes, -} from "../../../domain"; +} from "../../../../domain"; /** * Convierte el DTO a las props validadas (CustomerProps). @@ -56,13 +54,13 @@ export class CreateCustomerInvoicePropsMapper { this.errors = []; } - public map(dto: CreateCustomerInvoiceRequestDTO, companyId: UniqueID) { + public map(dto: CreateProformaRequestDTO, companyId: UniqueID) { try { this.errors = []; const defaultStatus = CustomerInvoiceStatus.createDraft(); - const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors); + const proformaId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors); const isProforma = true; @@ -74,8 +72,8 @@ export class CreateCustomerInvoicePropsMapper { const recipient = Maybe.none(); - const invoiceNumber = extractOrPushError( - maybeFromNullableVO(dto.invoice_number, (value) => CustomerInvoiceNumber.create(value)), + const proformaNumber = extractOrPushError( + CustomerInvoiceNumber.create(dto.invoice_number), "invoice_number", this.errors ); @@ -153,13 +151,14 @@ export class CreateCustomerInvoicePropsMapper { ); } - const invoiceProps: CustomerInvoiceProps = { + const proformaProps: CustomerInvoiceProps = { companyId, isProforma, + proformaId: Maybe.none(), status: defaultStatus!, + invoiceNumber: proformaNumber!, series: series!, - invoiceNumber: invoiceNumber!, invoiceDate: invoiceDate!, operationDate: operationDate!, @@ -181,13 +180,13 @@ export class CreateCustomerInvoicePropsMapper { discountPercentage: discountPercentage!, }; - return Result.ok({ id: invoiceId!, props: invoiceProps }); + return Result.ok({ id: proformaId!, props: proformaProps }); } catch (err: unknown) { return Result.fail(new DomainError("Customer invoice props mapping failed", { cause: err })); } } - private mapItems(items: CreateCustomerInvoiceItemRequestDTO[]) { + private mapItems(items: CreateProformaItemRequestDTO[]) { const invoiceItems = CustomerInvoiceItems.create({ currencyCode: this.currencyCode!, languageCode: this.languageCode!, @@ -246,10 +245,10 @@ export class CreateCustomerInvoicePropsMapper { return invoiceItems; } - private mapTaxes(item: CreateCustomerInvoiceItemRequestDTO, itemIndex: number) { + private mapTaxes(item: CreateProformaItemRequestDTO, itemIndex: number) { const taxes = ItemTaxes.create([]); - item.taxes.split(",").every((tax_code, taxIndex) => { + item.taxes.split(",").forEach((tax_code, taxIndex) => { const taxResult = Tax.createFromCode(tax_code, this.taxCatalog); if (taxResult.isSuccess) { taxes.add(taxResult.data); diff --git a/modules/customer-invoices/src/api/application/use-cases/delete-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/delete-proforma.use-case.ts similarity index 65% rename from modules/customer-invoices/src/api/application/use-cases/delete-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/delete-proforma.use-case.ts index 4499bc33..7b6b3f59 100644 --- a/modules/customer-invoices/src/api/application/use-cases/delete-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/delete-proforma.use-case.ts @@ -1,23 +1,24 @@ -import { EntityNotFoundError, ITransactionManager } from "@erp/core/api"; +import { EntityNotFoundError, type ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { CustomerInvoiceApplicationService } from "../services"; -type DeleteCustomerInvoiceUseCaseInput = { +import type { CustomerInvoiceApplicationService } from "../../services"; + +type DeleteProformaUseCaseInput = { companyId: UniqueID; - invoice_id: string; + proforma_id: string; }; -export class DeleteCustomerInvoiceUseCase { +export class DeleteProformaUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager ) {} - public execute(params: DeleteCustomerInvoiceUseCaseInput) { - const { invoice_id, companyId } = params; + public execute(params: DeleteProformaUseCaseInput) { + const { proforma_id, companyId } = params; - const idOrError = UniqueID.create(invoice_id); + const idOrError = UniqueID.create(proforma_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); @@ -40,9 +41,7 @@ export class DeleteCustomerInvoiceUseCase { const invoiceExists = existsCheck.data; if (!invoiceExists) { - return Result.fail( - new EntityNotFoundError("Customer invoice", "id", invoiceId.toString()) - ); + return Result.fail(new EntityNotFoundError("Proforma", "id", invoiceId.toString())); } return await this.service.deleteInvoiceByIdInCompany(companyId, invoiceId, transaction); diff --git a/modules/customer-invoices/src/api/application/use-cases/proformas/get-proforma.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/get-proforma.use-case.ts new file mode 100644 index 00000000..82a610a8 --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/get-proforma.use-case.ts @@ -0,0 +1,54 @@ +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; + +import type { CustomerInvoiceFullPresenter } from "../../presenters/domain"; +import type { CustomerInvoiceApplicationService } from "../../services"; + +type GetProformaUseCaseInput = { + companyId: UniqueID; + proforma_id: string; +}; + +export class GetProformaUseCase { + constructor( + private readonly service: CustomerInvoiceApplicationService, + private readonly transactionManager: ITransactionManager, + private readonly presenterRegistry: IPresenterRegistry + ) {} + + public execute(params: GetProformaUseCaseInput) { + const { proforma_id, companyId } = params; + + const idOrError = UniqueID.create(proforma_id); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } + + const proformaId = idOrError.data; + const presenter = this.presenterRegistry.getPresenter({ + resource: "customer-invoice", + projection: "FULL", + }) as CustomerInvoiceFullPresenter; + + return this.transactionManager.complete(async (transaction) => { + try { + const proformaOrError = await this.service.getProformaByIdInCompany( + companyId, + proformaId, + transaction + ); + if (proformaOrError.isFailure) { + return Result.fail(proformaOrError.error); + } + + const proforma = proformaOrError.data; + const dto = presenter.toOutput(proforma); + + return Result.ok(dto); + } catch (error: unknown) { + return Result.fail(error as Error); + } + }); + } +} diff --git a/modules/customer-invoices/src/api/application/use-cases/proformas/index.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/index.ts new file mode 100644 index 00000000..7df91db2 --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/index.ts @@ -0,0 +1,8 @@ +export * from "./change-status-proforma.use-case"; +export * from "./create-proforma"; +export * from "./delete-proforma.use-case"; +export * from "./get-proforma.use-case"; +export * from "./issue-proforma.use-case"; +export * from "./list-proformas.use-case"; +export * from "./report-proforma"; +export * from "./update-proforma"; diff --git a/modules/customer-invoices/src/api/application/use-cases/issue-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/issue-proforma.use-case.ts similarity index 92% rename from modules/customer-invoices/src/api/application/use-cases/issue-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/issue-proforma.use-case.ts index e03d6db1..98e57f4d 100644 --- a/modules/customer-invoices/src/api/application/use-cases/issue-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/issue-proforma.use-case.ts @@ -1,12 +1,13 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID, UtcDate } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; + import { IssueCustomerInvoiceDomainService, ProformaCustomerInvoiceDomainService, -} from "../../domain"; -import { CustomerInvoiceFullPresenter } from "../presenters"; -import { CustomerInvoiceApplicationService } from "../services"; +} from "../../../domain"; +import type { CustomerInvoiceFullPresenter } from "../../presenters"; +import type { CustomerInvoiceApplicationService } from "../../services"; type IssueCustomerInvoiceUseCaseInput = { companyId: UniqueID; @@ -22,7 +23,7 @@ type IssueCustomerInvoiceUseCaseInput = { * - Marca la proforma como "issued" * - Persiste ambas dentro de la misma transacción */ -export class IssueCustomerInvoiceUseCase { +export class IssueProformaInvoiceUseCase { private readonly issueDomainService: IssueCustomerInvoiceDomainService; private readonly proformaDomainService: ProformaCustomerInvoiceDomainService; diff --git a/modules/customer-invoices/src/api/application/use-cases/list-customer-invoices.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/list-proformas.use-case.ts similarity index 64% rename from modules/customer-invoices/src/api/application/use-cases/list-customer-invoices.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/list-proformas.use-case.ts index e8b426d4..58c332fd 100644 --- a/modules/customer-invoices/src/api/application/use-cases/list-customer-invoices.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/list-proformas.use-case.ts @@ -1,18 +1,19 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; -import { Criteria } from "@repo/rdx-criteria/server"; -import { UniqueID } from "@repo/rdx-ddd"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { Criteria } from "@repo/rdx-criteria/server"; +import type { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { ListCustomerInvoicesResponseDTO } from "../../../common/dto"; -import { ListCustomerInvoicesPresenter } from "../presenters"; -import { CustomerInvoiceApplicationService } from "../services"; +import type { Transaction } from "sequelize"; -type ListCustomerInvoicesUseCaseInput = { +import type { ListIssueInvoicesResponseDTO } from "../../../../common/dto"; +import type { ListCustomerInvoicesPresenter } from "../../presenters"; +import type { CustomerInvoiceApplicationService } from "../../services"; + +type ListProformasUseCaseInput = { companyId: UniqueID; criteria: Criteria; }; -export class ListCustomerInvoicesUseCase { +export class ListProformasUseCase { constructor( private readonly service: CustomerInvoiceApplicationService, private readonly transactionManager: ITransactionManager, @@ -20,8 +21,8 @@ export class ListCustomerInvoicesUseCase { ) {} public execute( - params: ListCustomerInvoicesUseCaseInput - ): Promise> { + params: ListProformasUseCaseInput + ): Promise> { const { criteria, companyId } = params; const presenter = this.presenterRegistry.getPresenter({ resource: "customer-invoice", diff --git a/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/index.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/index.ts new file mode 100644 index 00000000..79d1f5a4 --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/index.ts @@ -0,0 +1,2 @@ +export * from "./report-proforma.use-case"; +export * from "./reporter"; diff --git a/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/report-proforma.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/report-proforma.use-case.ts new file mode 100644 index 00000000..ce881f8b --- /dev/null +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/report-proforma.use-case.ts @@ -0,0 +1,59 @@ +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; + +import type { CustomerInvoiceApplicationService } from "../../../services/customer-invoice-application.service"; + +import type { CustomerInvoiceReportPDFPresenter } from "./reporter"; + +type ReportProformaUseCaseInput = { + companyId: UniqueID; + proforma_id: string; +}; + +export class ReportProformaUseCase { + constructor( + private readonly service: CustomerInvoiceApplicationService, + private readonly transactionManager: ITransactionManager, + private readonly presenterRegistry: IPresenterRegistry + ) {} + + public async execute(params: ReportProformaUseCaseInput) { + const { proforma_id, companyId } = params; + + const idOrError = UniqueID.create(proforma_id); + + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } + + const invoiceId = idOrError.data; + const pdfPresenter = this.presenterRegistry.getPresenter({ + resource: "customer-invoice", + projection: "REPORT", + format: "PDF", + }) as CustomerInvoiceReportPDFPresenter; + + return this.transactionManager.complete(async (transaction) => { + try { + const proformaOrError = await this.service.getProformaByIdInCompany( + companyId, + invoiceId, + transaction + ); + if (proformaOrError.isFailure) { + return Result.fail(proformaOrError.error); + } + + const proforma = proformaOrError.data; + const pdfData = await pdfPresenter.toOutput(proforma); + return Result.ok({ + data: pdfData, + filename: `proforma-${proforma.invoiceNumber}.pdf`, + }); + } catch (error: unknown) { + return Result.fail(error as Error); + } + }); + } +} diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.html.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.html.ts similarity index 85% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.html.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.html.ts index a58e01a3..90e2f18b 100644 --- a/modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.html.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.html.ts @@ -1,9 +1,14 @@ import { readFileSync } from "node:fs"; import path from "node:path"; + import { Presenter } from "@erp/core/api"; import * as handlebars from "handlebars"; -import { CustomerInvoice } from "../../../../domain"; -import { CustomerInvoiceFullPresenter, CustomerInvoiceReportPresenter } from "../../../presenters"; + +import type { CustomerInvoice } from "../../../../../domain"; +import type { + CustomerInvoiceFullPresenter, + CustomerInvoiceReportPresenter, +} from "../../../../presenters"; export class CustomerInvoiceReportHTMLPresenter extends Presenter { toOutput(customerInvoice: CustomerInvoice): string { diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.pdf.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.pdf.ts similarity index 92% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.pdf.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.pdf.ts index 19cb8ad1..3e25611c 100644 --- a/modules/customer-invoices/src/api/application/use-cases/report/reporter/customer-invoice.report.pdf.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/customer-invoice.report.pdf.ts @@ -1,8 +1,10 @@ import { Presenter } from "@erp/core/api"; import puppeteer from "puppeteer"; import report from "puppeteer-report"; -import { CustomerInvoice } from "../../../../domain"; -import { CustomerInvoiceReportHTMLPresenter } from "./customer-invoice.report.html"; + +import type { CustomerInvoice } from "../../../../../domain"; + +import type { CustomerInvoiceReportHTMLPresenter } from "./customer-invoice.report.html"; // https://plnkr.co/edit/lWk6Yd?preview diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/index.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/index.ts similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/index.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/index.ts diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/logo1.jpg b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/logo1.jpg similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/logo1.jpg rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/logo1.jpg diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template.hbs similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template.hbs diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs_BAK b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template.hbs_BAK similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs_BAK rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template.hbs_BAK diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template_proforma.hbs b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template_proforma.hbs similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template_proforma.hbs rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/template_proforma.hbs diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/uecko-footer-logos.jpg b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/uecko-footer-logos.jpg similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/uecko-footer-logos.jpg rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/uecko-footer-logos.jpg diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/uecko-logo.svg b/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/uecko-logo.svg similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/uecko-logo.svg rename to modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates/customer-invoice/uecko-logo.svg diff --git a/modules/customer-invoices/src/api/application/use-cases/update/index.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/index.ts similarity index 100% rename from modules/customer-invoices/src/api/application/use-cases/update/index.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/index.ts diff --git a/modules/customer-invoices/src/api/application/use-cases/update/map-dto-to-update-customer-invoice-props.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/map-dto-to-update-customer-invoice-props.ts similarity index 94% rename from modules/customer-invoices/src/api/application/use-cases/update/map-dto-to-update-customer-invoice-props.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/map-dto-to-update-customer-invoice-props.ts index 551da2a1..68a9ebf4 100644 --- a/modules/customer-invoices/src/api/application/use-cases/update/map-dto-to-update-customer-invoice-props.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/map-dto-to-update-customer-invoice-props.ts @@ -6,14 +6,14 @@ import { UniqueID, UtcDate, ValidationErrorCollection, - ValidationErrorDetail, + type ValidationErrorDetail, extractOrPushError, maybeFromNullableVO, } from "@repo/rdx-ddd"; import { Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-utils"; -import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common/dto"; -import { CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../domain"; +import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto"; +import { type CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../../domain"; /** * mapDTOToUpdateCustomerInvoicePatchProps @@ -29,7 +29,7 @@ import { CustomerInvoicePatchProps, CustomerInvoiceSerie } from "../../../domain * */ -export function mapDTOToUpdateCustomerInvoicePatchProps(dto: UpdateCustomerInvoiceByIdRequestDTO) { +export function mapDTOToUpdateCustomerInvoicePatchProps(dto: UpdateProformaByIdRequestDTO) { try { const errors: ValidationErrorDetail[] = []; const props: CustomerInvoicePatchProps = {}; diff --git a/modules/customer-invoices/src/api/application/use-cases/update/update-customer-invoice.use-case.ts b/modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/update-customer-invoice.use-case.ts similarity index 70% rename from modules/customer-invoices/src/api/application/use-cases/update/update-customer-invoice.use-case.ts rename to modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/update-customer-invoice.use-case.ts index 3e5d39be..077fe38b 100644 --- a/modules/customer-invoices/src/api/application/use-cases/update/update-customer-invoice.use-case.ts +++ b/modules/customer-invoices/src/api/application/use-cases/proformas/update-proforma/update-customer-invoice.use-case.ts @@ -1,17 +1,19 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common"; -import { CustomerInvoicePatchProps } from "../../../domain"; -import { CustomerInvoiceFullPresenter } from "../../presenters"; -import { CustomerInvoiceApplicationService } from "../../services/customer-invoice-application.service"; +import type { Transaction } from "sequelize"; + +import type { UpdateProformaByIdRequestDTO } from "../../../../../common"; +import type { CustomerInvoicePatchProps } from "../../../../domain"; +import type { CustomerInvoiceFullPresenter } from "../../../presenters"; +import type { CustomerInvoiceApplicationService } from "../../../services/customer-invoice-application.service"; + import { mapDTOToUpdateCustomerInvoicePatchProps } from "./map-dto-to-update-customer-invoice-props"; type UpdateCustomerInvoiceUseCaseInput = { companyId: UniqueID; - invoice_id: string; - dto: UpdateCustomerInvoiceByIdRequestDTO; + proforma_id: string; + dto: UpdateProformaByIdRequestDTO; }; export class UpdateCustomerInvoiceUseCase { @@ -22,9 +24,9 @@ export class UpdateCustomerInvoiceUseCase { ) {} public execute(params: UpdateCustomerInvoiceUseCaseInput) { - const { companyId, invoice_id, dto } = params; + const { companyId, proforma_id, dto } = params; - const idOrError = UniqueID.create(invoice_id); + const idOrError = UniqueID.create(proforma_id); if (idOrError.isFailure) { return Result.fail(idOrError.error); } @@ -45,7 +47,7 @@ export class UpdateCustomerInvoiceUseCase { return this.transactionManager.complete(async (transaction: Transaction) => { try { - const updatedInvoice = await this.service.patchInvoiceByIdInCompany( + const updatedInvoice = await this.service.patchProformaByIdInCompany( companyId, invoiceId, patchProps, @@ -56,7 +58,7 @@ export class UpdateCustomerInvoiceUseCase { return Result.fail(updatedInvoice.error); } - const invoiceOrError = await this.service.updateInvoiceInCompany( + const invoiceOrError = await this.service.updateProformaInCompany( companyId, updatedInvoice.data, transaction diff --git a/modules/customer-invoices/src/api/application/use-cases/report/index.ts b/modules/customer-invoices/src/api/application/use-cases/report/index.ts deleted file mode 100644 index 08f1ec1c..00000000 --- a/modules/customer-invoices/src/api/application/use-cases/report/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./report-customer-invoice.use-case"; -export * from "./reporter"; diff --git a/modules/customer-invoices/src/api/domain/aggregates/customer-invoice.ts b/modules/customer-invoices/src/api/domain/aggregates/customer-invoice.ts index adffa2f5..2cffb720 100644 --- a/modules/customer-invoices/src/api/domain/aggregates/customer-invoice.ts +++ b/modules/customer-invoices/src/api/domain/aggregates/customer-invoice.ts @@ -1,21 +1,22 @@ import { AggregateRoot, - CurrencyCode, + type CurrencyCode, DomainValidationError, - LanguageCode, - Percentage, - TextValue, - UniqueID, - UtcDate, + type LanguageCode, + type Percentage, + type TextValue, + type UniqueID, + type UtcDate, } from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; -import { CustomerInvoiceItems, InvoicePaymentMethod, InvoiceTaxTotal } from "../entities"; +import { type Maybe, Result } from "@repo/rdx-utils"; + +import { CustomerInvoiceItems, type InvoicePaymentMethod, type InvoiceTaxTotal } from "../entities"; import { - CustomerInvoiceNumber, - CustomerInvoiceSerie, - CustomerInvoiceStatus, + type CustomerInvoiceNumber, + type CustomerInvoiceSerie, + type CustomerInvoiceStatus, InvoiceAmount, - InvoiceRecipient, + type InvoiceRecipient, } from "../value-objects"; export interface CustomerInvoiceProps { @@ -24,7 +25,7 @@ export interface CustomerInvoiceProps { isProforma: boolean; status: CustomerInvoiceStatus; - proformaId: Maybe; + proformaId: Maybe; // <- proforma padre en caso de issue series: Maybe; invoiceNumber: CustomerInvoiceNumber; @@ -106,7 +107,7 @@ export class CustomerInvoice // Reglas de negocio / validaciones - if (!customerInvoice.isProforma && !customerInvoice.hasRecipient) { + if (!(customerInvoice.isProforma || customerInvoice.hasRecipient)) { return Result.fail( new DomainValidationError( "MISSING_CUSTOMER_DATA", diff --git a/modules/customer-invoices/src/api/domain/repositories/customer-invoice-repository.interface.ts b/modules/customer-invoices/src/api/domain/repositories/customer-invoice-repository.interface.ts index b249463b..e0887634 100644 --- a/modules/customer-invoices/src/api/domain/repositories/customer-invoice-repository.interface.ts +++ b/modules/customer-invoices/src/api/domain/repositories/customer-invoice-repository.interface.ts @@ -1,9 +1,10 @@ -import { Criteria } from "@repo/rdx-criteria/server"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Collection, Result } from "@repo/rdx-utils"; -import { CustomerInvoiceListDTO } from "../../infrastructure"; -import { CustomerInvoice } from "../aggregates"; -import { CustomerInvoiceStatus } from "../value-objects"; +import type { Criteria } from "@repo/rdx-criteria/server"; +import type { UniqueID } from "@repo/rdx-ddd"; +import type { Collection, Result } from "@repo/rdx-utils"; + +import type { CustomerInvoiceListDTO } from "../../infrastructure"; +import type { CustomerInvoice } from "../aggregates"; +import type { CustomerInvoiceStatus } from "../value-objects"; /** * Interfaz del repositorio para el agregado `CustomerInvoice`. @@ -45,7 +46,8 @@ export interface ICustomerInvoiceRepository { getByIdInCompany( companyId: UniqueID, id: UniqueID, - transaction?: unknown + transaction: unknown, + options: unknown ): Promise>; /** diff --git a/modules/customer-invoices/src/api/index.ts b/modules/customer-invoices/src/api/index.ts index 537b3f86..ad24c39f 100644 --- a/modules/customer-invoices/src/api/index.ts +++ b/modules/customer-invoices/src/api/index.ts @@ -1,7 +1,9 @@ -import { IModuleServer, ModuleParams } from "@erp/core/api"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Transaction } from "sequelize"; -import { buildCustomerInvoiceDependencies, customerInvoicesRouter, models } from "./infrastructure"; +import type { IModuleServer, ModuleParams } from "@erp/core/api"; +import type { UniqueID } from "@repo/rdx-ddd"; +import type { Transaction } from "sequelize"; + +import { buildCustomerInvoiceDependencies, models, proformasRouter } from "./infrastructure"; +import { issueInvoicesRouter } from "./infrastructure/express/issue-invoices.routes"; export const customerInvoicesAPIModule: IModuleServer = { name: "customer-invoices", @@ -11,7 +13,8 @@ export const customerInvoicesAPIModule: IModuleServer = { async init(params: ModuleParams) { // const contacts = getService("contacts"); const { logger } = params; - customerInvoicesRouter(params); + proformasRouter(params); + issueInvoicesRouter(params); logger.info("🚀 CustomerInvoices module initialized", { label: this.name }); }, diff --git a/modules/customer-invoices/src/api/infrastructure/dependencies.ts b/modules/customer-invoices/src/api/infrastructure/dependencies.ts index a5bc02f3..9dc70a90 100644 --- a/modules/customer-invoices/src/api/infrastructure/dependencies.ts +++ b/modules/customer-invoices/src/api/infrastructure/dependencies.ts @@ -1,15 +1,16 @@ // modules/invoice/infrastructure/invoice-dependencies.factory.ts -import { JsonTaxCatalogProvider, SpainTaxCatalogProvider } from "@erp/core"; +import { type JsonTaxCatalogProvider, SpainTaxCatalogProvider } from "@erp/core"; import type { IMapperRegistry, IPresenterRegistry, ModuleParams } from "@erp/core/api"; import { InMemoryMapperRegistry, InMemoryPresenterRegistry, SequelizeTransactionManager, } from "@erp/core/api"; + import { - ChangeStatusCustomerInvoiceUseCase, - CreateCustomerInvoiceUseCase, + ChangeStatusProformaUseCase, + CreateProformaUseCase, CustomerInvoiceApplicationService, CustomerInvoiceFullPresenter, CustomerInvoiceItemsFullPresenter, @@ -17,14 +18,15 @@ import { CustomerInvoiceReportHTMLPresenter, CustomerInvoiceReportPDFPresenter, CustomerInvoiceReportPresenter, - GetCustomerInvoiceUseCase, - IssueCustomerInvoiceUseCase, + GetProformaUseCase, + IssueProformaInvoiceUseCase, ListCustomerInvoicesPresenter, - ListCustomerInvoicesUseCase, + ListProformasUseCase, RecipientInvoiceFullPresenter, - ReportCustomerInvoiceUseCase, + ReportProformaUseCase, UpdateCustomerInvoiceUseCase, } from "../application"; + import { CustomerInvoiceDomainMapper, CustomerInvoiceListMapper } from "./mappers"; import { CustomerInvoiceRepository } from "./sequelize"; import { SequelizeInvoiceNumberGenerator } from "./services"; @@ -39,14 +41,14 @@ export type CustomerInvoiceDeps = { taxes: JsonTaxCatalogProvider; }; useCases: { - list: () => ListCustomerInvoicesUseCase; - get: () => GetCustomerInvoiceUseCase; - create: () => CreateCustomerInvoiceUseCase; + list: () => ListProformasUseCase; + get: () => GetProformaUseCase; + create: () => CreateProformaUseCase; update: () => UpdateCustomerInvoiceUseCase; //delete: () => DeleteCustomerInvoiceUseCase; - report: () => ReportCustomerInvoiceUseCase; - issue: () => IssueCustomerInvoiceUseCase; - changeStatus: () => ChangeStatusCustomerInvoiceUseCase; + report: () => ReportProformaUseCase; + issue: () => IssueProformaInvoiceUseCase; + changeStatus: () => ChangeStatusProformaUseCase; }; }; @@ -117,21 +119,15 @@ export function buildCustomerInvoiceDependencies(params: ModuleParams): Customer ]); const useCases = { - list: () => new ListCustomerInvoicesUseCase(appService, transactionManager, presenterRegistry), - get: () => new GetCustomerInvoiceUseCase(appService, transactionManager, presenterRegistry), + list: () => new ListProformasUseCase(appService, transactionManager, presenterRegistry), + get: () => new GetProformaUseCase(appService, transactionManager, presenterRegistry), create: () => - new CreateCustomerInvoiceUseCase( - appService, - transactionManager, - presenterRegistry, - catalogs.taxes - ), + new CreateProformaUseCase(appService, transactionManager, presenterRegistry, catalogs.taxes), update: () => new UpdateCustomerInvoiceUseCase(appService, transactionManager, presenterRegistry), - report: () => - new ReportCustomerInvoiceUseCase(appService, transactionManager, presenterRegistry), - issue: () => new IssueCustomerInvoiceUseCase(appService, transactionManager, presenterRegistry), - changeStatus: () => new ChangeStatusCustomerInvoiceUseCase(appService, transactionManager), + report: () => new ReportProformaUseCase(appService, transactionManager, presenterRegistry), + issue: () => new IssueProformaInvoiceUseCase(appService, transactionManager, presenterRegistry), + changeStatus: () => new ChangeStatusProformaUseCase(appService, transactionManager), }; return { diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/delete-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/delete-customer-invoice.controller.ts deleted file mode 100644 index 15c0a036..00000000 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/delete-customer-invoice.controller.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { DeleteCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; - -export class DeleteCustomerInvoiceController extends ExpressController { - public constructor( - private readonly useCase: DeleteCustomerInvoiceUseCase - /* private readonly presenter: any */ - ) { - super(); - this.errorMapper = customerInvoicesApiErrorMapper; - - // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query - this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); - } - - async executeImpl() { - const tenantId = this.getTenantId()!; // garantizado por tenantGuard - const { id } = this.req.params; - - const result = await this.useCase.execute({ id, tenantId }); - - return result.match( - (data) => this.ok(data), - (err) => this.handleError(err) - ); - } -} diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/index.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/index.ts index 87230f1d..457e40a2 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/index.ts @@ -1,8 +1,2 @@ -export * from "./change-status-customer-invoice.controller"; -export * from "./create-customer-invoice.controller"; -//export * from "./delete-customer-invoice.controller"; -export * from "./get-customer-invoice.controller"; -export * from "./issue-customer-invoice.controller"; -export * from "./list-customer-invoices.controller"; -export * from "./report-customer-invoice.controller"; -export * from "./update-customer-invoice.controller"; +export * from "./issue-invoices"; +export * from "./proformas"; diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/get-issue-invoice.controller.ts similarity index 56% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/get-issue-invoice.controller.ts index 69af2070..f8d72c27 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/get-issue-invoice.controller.ts @@ -1,9 +1,10 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { GetCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -export class GetCustomerInvoiceController extends ExpressController { - public constructor(private readonly useCase: GetCustomerInvoiceUseCase) { +import type { GetProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class GetIssueInvoiceController extends ExpressController { + public constructor(private readonly useCase: GetProformaUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -18,7 +19,7 @@ export class GetCustomerInvoiceController extends ExpressController { } const { invoice_id } = this.req.params; - const result = await this.useCase.execute({ invoice_id, companyId }); + const result = await this.useCase.execute({ proforma_id: invoice_id, companyId }); return result.match( (data) => this.ok(data), diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/index.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/index.ts new file mode 100644 index 00000000..fc35aeab --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/index.ts @@ -0,0 +1,2 @@ +export * from "./get-issue-invoice.controller"; +export * from "./list-issue-invoices.controller"; diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/list-issue-invoices.controller.ts similarity index 78% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/list-issue-invoices.controller.ts index 696c43ba..40481385 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-invoices/list-issue-invoices.controller.ts @@ -1,10 +1,11 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; import { Criteria } from "@repo/rdx-criteria/server"; -import { ListCustomerInvoicesUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; -export class ListCustomerInvoicesController extends ExpressController { - public constructor(private readonly useCase: ListCustomerInvoicesUseCase) { +import type { ListProformasUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class ListIssueInvoicesController extends ExpressController { + public constructor(private readonly useCase: ListProformasUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/change-status-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/change-status-proforma.controller.ts similarity index 55% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/change-status-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/change-status-proforma.controller.ts index 38495110..5de1e80a 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/change-status-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/change-status-proforma.controller.ts @@ -1,10 +1,11 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { ChangeStatusCustomerInvoiceByIdRequestDTO } from "@erp/customer-invoices/common"; -import { ChangeStatusCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -export class ChangeStatusCustomerInvoiceController extends ExpressController { - public constructor(private readonly useCase: ChangeStatusCustomerInvoiceUseCase) { +import type { ChangeStatusProformaByIdRequestDTO } from "../../../../../common/dto"; +import type { ChangeStatusProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class ChangeStatusProformaController extends ExpressController { + public constructor(private readonly useCase: ChangeStatusProformaUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -12,7 +13,7 @@ export class ChangeStatusCustomerInvoiceController extends ExpressController { this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); } - async executeImpl(): Promise { + protected async executeImpl() { const companyId = this.getTenantId(); // garantizado por tenantGuard if (!companyId) { return this.forbiddenError("Tenant ID not found"); @@ -23,7 +24,7 @@ export class ChangeStatusCustomerInvoiceController extends ExpressController { return this.invalidInputError("Proforma ID missing"); } - const dto = this.req.body as ChangeStatusCustomerInvoiceByIdRequestDTO; + const dto = this.req.body as ChangeStatusProformaByIdRequestDTO; const result = await this.useCase.execute({ proforma_id, dto, companyId }); return result.match( diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/create-proforma.controller.ts similarity index 54% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/create-proforma.controller.ts index a76b70da..090935b0 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/create-proforma.controller.ts @@ -1,11 +1,11 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { CreateCustomerInvoiceRequestDTO } from "../../../../common/dto"; -import { CreateCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import type { CreateProformaRequestDTO } from "../../../../../common/dto"; +import type { CreateProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; -export class CreateCustomerInvoiceController extends ExpressController { - public constructor(private readonly useCase: CreateCustomerInvoiceUseCase) { +export class CreateProformaController extends ExpressController { + public constructor(private readonly useCase: CreateProformaUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -18,7 +18,7 @@ export class CreateCustomerInvoiceController extends ExpressController { if (!companyId) { return this.forbiddenError("Tenant ID not found"); } - const dto = this.req.body as CreateCustomerInvoiceRequestDTO; + const dto = this.req.body as CreateProformaRequestDTO; const result = await this.useCase.execute({ dto, companyId }); diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/delete-proforma.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/delete-proforma.controller.ts new file mode 100644 index 00000000..5f345906 --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/delete-proforma.controller.ts @@ -0,0 +1,33 @@ +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; + +import type { DeleteProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class DeleteProformaController extends ExpressController { + public constructor( + private readonly useCase: DeleteProformaUseCase + /* private readonly presenter: any */ + ) { + super(); + this.errorMapper = customerInvoicesApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); + } + + protected async executeImpl() { + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const { proforma_id } = this.req.params; + + const result = await this.useCase.execute({ proforma_id, companyId }); + + return result.match( + (data) => this.ok(data), + (err) => this.handleError(err) + ); + } +} diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/get-proforma.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/get-proforma.controller.ts new file mode 100644 index 00000000..5d431079 --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/get-proforma.controller.ts @@ -0,0 +1,29 @@ +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; + +import type { GetProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class GetProformaController extends ExpressController { + public constructor(private readonly useCase: GetProformaUseCase) { + super(); + this.errorMapper = customerInvoicesApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); + } + + protected async executeImpl() { + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + const { proforma_id } = this.req.params; + + const result = await this.useCase.execute({ proforma_id, companyId }); + + return result.match( + (data) => this.ok(data), + (err) => this.handleError(err) + ); + } +} diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/index.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/index.ts new file mode 100644 index 00000000..930e5f5e --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/index.ts @@ -0,0 +1,8 @@ +export * from "./change-status-proforma.controller"; +export * from "./create-proforma.controller"; +export * from "./delete-proforma.controller"; +export * from "./get-proforma.controller"; +export * from "./issue-proforma.controller"; +export * from "./list-proformas.controller"; +export * from "./report-proforma.controller"; +export * from "./update-proforma.controller"; diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/issue-proforma.controller.ts similarity index 65% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/issue-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/issue-proforma.controller.ts index 9fa13873..a2698b59 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/issue-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/issue-proforma.controller.ts @@ -1,9 +1,10 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { IssueCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -export class IssueCustomerInvoiceController extends ExpressController { - public constructor(private readonly useCase: IssueCustomerInvoiceUseCase) { +import type { IssueProformaInvoiceUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class IssueProformaController extends ExpressController { + public constructor(private readonly useCase: IssueProformaInvoiceUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -11,7 +12,7 @@ export class IssueCustomerInvoiceController extends ExpressController { this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); } - async executeImpl(): Promise { + protected async executeImpl() { const companyId = this.getTenantId(); // garantizado por tenantGuard if (!companyId) { return this.forbiddenError("Tenant ID not found"); diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/list-proformas.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/list-proformas.controller.ts new file mode 100644 index 00000000..38157072 --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/list-proformas.controller.ts @@ -0,0 +1,52 @@ +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; +import { Criteria } from "@repo/rdx-criteria/server"; + +import type { ListProformasUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class ListProformasController extends ExpressController { + public constructor(private readonly useCase: ListProformasUseCase) { + super(); + this.errorMapper = customerInvoicesApiErrorMapper; + + // 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query + this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); + } + + private getCriteriaWithDefaultOrder() { + if (this.criteria.hasOrder()) { + return this.criteria; + } + + const { q: quicksearch, filters, pageSize, pageNumber } = this.criteria.toPrimitives(); + return Criteria.fromPrimitives( + filters, + "invoice_date", + "DESC", + pageSize, + pageNumber, + quicksearch + ); + } + + protected async executeImpl() { + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const criteria = this.getCriteriaWithDefaultOrder(); + const result = await this.useCase.execute({ criteria, companyId }); + + return result.match( + (data) => + this.ok(data, { + "X-Total-Count": String(data.total_items), + "Pagination-Count": String(data.total_pages), + "Pagination-Page": String(data.page), + "Pagination-Limit": String(data.per_page), + }), + (err) => this.handleError(err) + ); + } +} diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/report-proforma.controller.ts similarity index 53% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/report-proforma.controller.ts index 90863b19..d742d69a 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/report-proforma.controller.ts @@ -1,9 +1,10 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { ReportCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -export class ReportCustomerInvoiceController extends ExpressController { - public constructor(private readonly useCase: ReportCustomerInvoiceUseCase) { +import type { ReportProformaUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class ReportProformaController extends ExpressController { + public constructor(private readonly useCase: ReportProformaUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -16,9 +17,9 @@ export class ReportCustomerInvoiceController extends ExpressController { if (!companyId) { return this.forbiddenError("Tenant ID not found"); } - const { invoice_id } = this.req.params; + const { proforma_id } = this.req.params; - const result = await this.useCase.execute({ invoice_id, companyId }); + const result = await this.useCase.execute({ proforma_id, companyId }); return result.match( ({ data, filename }) => this.downloadPDF(data, filename), diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/update-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/update-proforma.controller.ts similarity index 55% rename from modules/customer-invoices/src/api/infrastructure/express/controllers/update-customer-invoice.controller.ts rename to modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/update-proforma.controller.ts index 21576af3..33985b29 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/update-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/proformas/update-proforma.controller.ts @@ -1,9 +1,10 @@ -import { authGuard, ExpressController, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -import { UpdateCustomerInvoiceByIdRequestDTO } from "../../../../common/dto"; -import { UpdateCustomerInvoiceUseCase } from "../../../application"; -import { customerInvoicesApiErrorMapper } from "../customer-invoices-api-error-mapper"; +import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; -export class UpdateCustomerInvoiceController extends ExpressController { +import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto"; +import type { UpdateCustomerInvoiceUseCase } from "../../../../application"; +import { customerInvoicesApiErrorMapper } from "../../customer-invoices-api-error-mapper"; + +export class UpdateProformaController extends ExpressController { public constructor(private readonly useCase: UpdateCustomerInvoiceUseCase) { super(); this.errorMapper = customerInvoicesApiErrorMapper; @@ -12,7 +13,7 @@ export class UpdateCustomerInvoiceController extends ExpressController { this.registerGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); } - async executeImpl(): Promise { + protected async executeImpl() { const companyId = this.getTenantId(); if (!companyId) { return this.forbiddenError("Tenant ID not found"); @@ -23,9 +24,9 @@ export class UpdateCustomerInvoiceController extends ExpressController { return this.invalidInputError("Proforma ID missing"); } - const dto = this.req.body as UpdateCustomerInvoiceByIdRequestDTO; + const dto = this.req.body as UpdateProformaByIdRequestDTO; - const result = await this.useCase.execute({ invoice_id: proforma_id, companyId, dto }); + const result = await this.useCase.execute({ proforma_id, companyId, dto }); return result.match( (data) => this.ok(data), diff --git a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices-api-error-mapper.ts b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices-api-error-mapper.ts index d100f2be..721175fd 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices-api-error-mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices-api-error-mapper.ts @@ -4,16 +4,17 @@ import { ApiErrorMapper, ConflictApiError, - ErrorToApiRule, + type ErrorToApiRule, ValidationApiError, } from "@erp/core/api"; + import { - CustomerInvoiceIdAlreadyExistsError, - EntityIsNotProformaError, + type CustomerInvoiceIdAlreadyExistsError, + type EntityIsNotProformaError, + type ProformaCannotBeConvertedToInvoiceError, isCustomerInvoiceIdAlreadyExistsError, isEntityIsNotProformaError, isProformaCannotBeConvertedToInvoiceError, - ProformaCannotBeConvertedToInvoiceError, } from "../../domain"; // Crea una regla específica (prioridad alta para sobreescribir mensajes) diff --git a/modules/customer-invoices/src/api/infrastructure/express/index.ts b/modules/customer-invoices/src/api/infrastructure/express/index.ts index 057b5b4d..1901664b 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/index.ts @@ -1 +1,2 @@ -export * from "./customer-invoices.routes"; +export * from "./issue-invoices.routes"; +export * from "./proformas.routes"; diff --git a/modules/customer-invoices/src/api/infrastructure/express/issue-invoices.routes.ts b/modules/customer-invoices/src/api/infrastructure/express/issue-invoices.routes.ts new file mode 100644 index 00000000..c49f8184 --- /dev/null +++ b/modules/customer-invoices/src/api/infrastructure/express/issue-invoices.routes.ts @@ -0,0 +1,83 @@ +import { type RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth/api"; +import { type ModuleParams, validateRequest } from "@erp/core/api"; +import type { ILogger } from "@repo/rdx-logger"; +import { type Application, type NextFunction, type Request, type Response, Router } from "express"; +import type { Sequelize } from "sequelize"; + +import { + GetIssueInvoiceByIdRequestSchema, + ListIssueInvoicesRequestSchema, + ReportIssueInvoiceByIdRequestSchema, +} from "../../../common/dto"; +import { buildCustomerInvoiceDependencies } from "../dependencies"; + +import { + GetProformaController, + ListProformasController, + ReportProformaController, +} from "./controllers"; + +export const issueInvoicesRouter = (params: ModuleParams) => { + const { app, baseRoutePath, logger } = params as { + app: Application; + database: Sequelize; + baseRoutePath: string; + logger: ILogger; + }; + + const deps = buildCustomerInvoiceDependencies(params); + + const router: Router = Router({ mergeParams: true }); + if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { + // 🔐 Autenticación + Tenancy para TODO el router + router.use( + (req: Request, res: Response, next: NextFunction) => + mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas + ); + } + + router.use([ + (req: Request, res: Response, next: NextFunction) => + enforceUser()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas + + (req: Request, res: Response, next: NextFunction) => + enforceTenant()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas + ]); + + // ---------------------------------------------- + + router.get( + "/", + //checkTabContext, + validateRequest(ListIssueInvoicesRequestSchema, "params"), + async (req: Request, res: Response, next: NextFunction) => { + const useCase = deps.useCases.list(); + const controller = new ListProformasController(useCase /*, deps.presenters.list */); + return controller.execute(req, res, next); + } + ); + + router.get( + "/:invoice_id", + //checkTabContext, + validateRequest(GetIssueInvoiceByIdRequestSchema, "params"), + (req: Request, res: Response, next: NextFunction) => { + const useCase = deps.useCases.get(); + const controller = new GetProformaController(useCase); + return controller.execute(req, res, next); + } + ); + + router.get( + "/:invoice_id/report", + //checkTabContext, + validateRequest(ReportIssueInvoiceByIdRequestSchema, "params"), + (req: Request, res: Response, next: NextFunction) => { + const useCase = deps.useCases.report(); + const controller = new ReportProformaController(useCase); + return controller.execute(req, res, next); + } + ); + + app.use(`${baseRoutePath}/customer-invoices`, router); +}; diff --git a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts b/modules/customer-invoices/src/api/infrastructure/express/proformas.routes.ts similarity index 56% rename from modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts rename to modules/customer-invoices/src/api/infrastructure/express/proformas.routes.ts index 93a598c7..9a22d31f 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/proformas.routes.ts @@ -1,30 +1,32 @@ -import { enforceTenant, enforceUser, mockUser, RequestWithAuth } from "@erp/auth/api"; -import { ModuleParams, validateRequest } from "@erp/core/api"; -import { ILogger } from "@repo/rdx-logger"; -import { Application, NextFunction, Request, Response, Router } from "express"; -import { Sequelize } from "sequelize"; +import { type RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth/api"; +import { type ModuleParams, validateRequest } from "@erp/core/api"; import { - ChangeStatusCustomerInvoiceByIdParamsRequestSchema, - ChangeStatusCustomerInvoiceByIdRequestSchema, - CreateCustomerInvoiceRequestSchema, - CustomerInvoiceListRequestSchema, - GetCustomerInvoiceByIdRequestSchema, - ReportCustomerInvoiceByIdRequestSchema, - UpdateCustomerInvoiceByIdParamsRequestSchema, - UpdateCustomerInvoiceByIdRequestSchema, -} from "../../../common/dto"; -import { buildCustomerInvoiceDependencies } from "../dependencies"; -import { - ChangeStatusCustomerInvoiceController, - CreateCustomerInvoiceController, - GetCustomerInvoiceController, - ListCustomerInvoicesController, - ReportCustomerInvoiceController, - UpdateCustomerInvoiceController, -} from "./controllers"; -import { IssueCustomerInvoiceController } from "./controllers/issue-customer-invoice.controller"; + ChangeStatusProformaByIdParamsRequestSchema, + ChangeStatusProformaByIdRequestSchema, + CreateProformaRequestSchema, + GetProformaByIdRequestSchema, + ListProformasRequestSchema, + ReportProformaByIdRequestSchema, + UpdateProformaByIdParamsRequestSchema, + UpdateProformaByIdRequestSchema, +} from "@erp/customer-invoices/common"; +import type { ILogger } from "@repo/rdx-logger"; +import { type Application, type NextFunction, type Request, type Response, Router } from "express"; +import type { Sequelize } from "sequelize"; -export const customerInvoicesRouter = (params: ModuleParams) => { +import { buildCustomerInvoiceDependencies } from "../dependencies"; + +import { + ChangeStatusProformaController, + CreateProformaController, + GetProformaController, + ListProformasController, + ReportProformaController, + UpdateProformaController, +} from "./controllers/proformas"; +import { IssueProformaController } from "./controllers/proformas/issue-proforma.controller"; + +export const proformasRouter = (params: ModuleParams) => { const { app, baseRoutePath, logger } = params as { app: Application; database: Sequelize; @@ -56,21 +58,21 @@ export const customerInvoicesRouter = (params: ModuleParams) => { router.get( "/", //checkTabContext, - validateRequest(CustomerInvoiceListRequestSchema, "params"), + validateRequest(ListProformasRequestSchema, "params"), async (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.list(); - const controller = new ListCustomerInvoicesController(useCase /*, deps.presenters.list */); + const controller = new ListProformasController(useCase /*, deps.presenters.list */); return controller.execute(req, res, next); } ); router.get( - "/:invoice_id", + "/:proforma_id", //checkTabContext, - validateRequest(GetCustomerInvoiceByIdRequestSchema, "params"), + validateRequest(GetProformaByIdRequestSchema, "params"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.get(); - const controller = new GetCustomerInvoiceController(useCase); + const controller = new GetProformaController(useCase); return controller.execute(req, res, next); } ); @@ -79,10 +81,10 @@ export const customerInvoicesRouter = (params: ModuleParams) => { "/", //checkTabContext, - validateRequest(CreateCustomerInvoiceRequestSchema, "body"), + validateRequest(CreateProformaRequestSchema, "body"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.create(); - const controller = new CreateCustomerInvoiceController(useCase); + const controller = new CreateProformaController(useCase); return controller.execute(req, res, next); } ); @@ -91,17 +93,17 @@ export const customerInvoicesRouter = (params: ModuleParams) => { "/:proforma_id", //checkTabContext, - validateRequest(UpdateCustomerInvoiceByIdParamsRequestSchema, "params"), - validateRequest(UpdateCustomerInvoiceByIdRequestSchema, "body"), + validateRequest(UpdateProformaByIdParamsRequestSchema, "params"), + validateRequest(UpdateProformaByIdRequestSchema, "body"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.update(); - const controller = new UpdateCustomerInvoiceController(useCase); + const controller = new UpdateProformaController(useCase); return controller.execute(req, res, next); } ); /*router.delete( - "/:invoice_id", + "/:proforma_id", //checkTabContext, validateRequest(DeleteCustomerInvoiceByIdRequestSchema, "params"), @@ -113,12 +115,12 @@ export const customerInvoicesRouter = (params: ModuleParams) => { );*/ router.get( - "/:invoice_id/report", + "/:proforma_id/report", //checkTabContext, - validateRequest(ReportCustomerInvoiceByIdRequestSchema, "params"), + validateRequest(ReportProformaByIdRequestSchema, "params"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.report(); - const controller = new ReportCustomerInvoiceController(useCase); + const controller = new ReportProformaController(useCase); return controller.execute(req, res, next); } ); @@ -127,12 +129,12 @@ export const customerInvoicesRouter = (params: ModuleParams) => { "/:proforma_id/status", //checkTabContext, - validateRequest(ChangeStatusCustomerInvoiceByIdParamsRequestSchema, "params"), - validateRequest(ChangeStatusCustomerInvoiceByIdRequestSchema, "body"), + validateRequest(ChangeStatusProformaByIdParamsRequestSchema, "params"), + validateRequest(ChangeStatusProformaByIdRequestSchema, "body"), (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.changeStatus(); - const controller = new ChangeStatusCustomerInvoiceController(useCase); + const controller = new ChangeStatusProformaController(useCase); return controller.execute(req, res, next); } ); @@ -146,10 +148,10 @@ export const customerInvoicesRouter = (params: ModuleParams) => { (req: Request, res: Response, next: NextFunction) => { const useCase = deps.useCases.issue(); - const controller = new IssueCustomerInvoiceController(useCase); + const controller = new IssueProformaController(useCase); return controller.execute(req, res, next); } ); - app.use(`${baseRoutePath}/customer-invoices`, router); + app.use(`${baseRoutePath}/proformas`, router); }; diff --git a/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts b/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts index fb5e3f67..f3f1776e 100644 --- a/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts @@ -4,16 +4,22 @@ import { SequelizeRepository, translateSequelizeError, } from "@erp/core/api"; -import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Collection, Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { CustomerInvoice, CustomerInvoiceStatus, ICustomerInvoiceRepository } from "../../domain"; -import { +import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; +import type { UniqueID } from "@repo/rdx-ddd"; +import { type Collection, Result } from "@repo/rdx-utils"; +import type { FindOptions, InferAttributes, Transaction } from "sequelize"; + +import type { + CustomerInvoice, + CustomerInvoiceStatus, + ICustomerInvoiceRepository, +} from "../../domain"; +import type { CustomerInvoiceListDTO, ICustomerInvoiceDomainMapper, ICustomerInvoiceListMapper, } from "../mappers"; + import { CustomerInvoiceModel } from "./models/customer-invoice.model"; import { CustomerInvoiceItemModel } from "./models/customer-invoice-item.model"; import { CustomerInvoiceItemTaxModel } from "./models/customer-invoice-item-tax.model"; @@ -185,12 +191,14 @@ export class CustomerInvoiceRepository * @param companyId - Identificador UUID de la empresa a la que pertenece la factura. * @param id - UUID de la factura. * @param transaction - Transacción activa para la operación. + * @params options - Opciones adicionales para la consulta (Sequelize FindOptions) * @returns Result */ async getByIdInCompany( companyId: UniqueID, id: UniqueID, - transaction: Transaction + transaction: Transaction, + options: FindOptions> = {} ): Promise> { const { CustomerModel } = this._database.models; @@ -199,14 +207,36 @@ export class CustomerInvoiceRepository resource: "customer-invoice", }); - const row = await CustomerInvoiceModel.findOne({ - where: { id: id.toString(), company_id: companyId.toString() }, - order: [[{ model: CustomerInvoiceItemModel, as: "items" }, "position", "ASC"]], + // Normalización defensiva de order/include + const normalizedOrder = Array.isArray(options.order) + ? options.order + : options.order + ? [options.order] + : []; + + const normalizedInclude = Array.isArray(options.include) + ? options.include + : options.include + ? [options.include] + : []; + + const mergedOptions: FindOptions> = { + ...options, + where: { + id: id.toString(), + company_id: companyId.toString(), + ...(options.where ?? {}), + }, + order: [ + ...normalizedOrder, + [{ model: CustomerInvoiceItemModel, as: "items" }, "position", "ASC"], + ], include: [ + ...normalizedInclude, { model: CustomerModel, as: "current_customer", - required: false, // false => LEFT JOIN + required: false, }, { model: CustomerInvoiceItemModel, @@ -227,7 +257,9 @@ export class CustomerInvoiceRepository }, ], transaction, - }); + }; + + const row = await CustomerInvoiceModel.findOne(mergedOptions); if (!row) { return Result.fail(new EntityNotFoundError("CustomerInvoice", "id", id.toString())); diff --git a/modules/customer-invoices/src/common/dto/request/change-status-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/change-status-customer-invoice-by-id.request.dto.ts deleted file mode 100644 index add2ba41..00000000 --- a/modules/customer-invoices/src/common/dto/request/change-status-customer-invoice-by-id.request.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from "zod/v4"; - -export const ChangeStatusCustomerInvoiceByIdParamsRequestSchema = z.object({ - proforma_id: z.string(), -}); - -export const ChangeStatusCustomerInvoiceByIdRequestSchema = z.object({ - new_status: z.string(), -}); - -export type ChangeStatusCustomerInvoiceByIdRequestDTO = Partial< - z.infer ->; diff --git a/modules/customer-invoices/src/common/dto/request/customer-invoices-list.request.dto.ts b/modules/customer-invoices/src/common/dto/request/customer-invoices-list.request.dto.ts deleted file mode 100644 index 93c25a9b..00000000 --- a/modules/customer-invoices/src/common/dto/request/customer-invoices-list.request.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { CriteriaSchema } from "@erp/core"; -import { z } from "zod/v4"; - -export const CustomerInvoiceListRequestSchema = CriteriaSchema; -export type CustomerInvoiceListRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.request.dto.ts deleted file mode 100644 index e0e8f27f..00000000 --- a/modules/customer-invoices/src/common/dto/request/delete-customer-invoice-by-id.request.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { z } from "zod/v4"; - -/** - * Este DTO es utilizado por el endpoint: - * `DELETE /customer-invoices/:id` (eliminar una factura por ID). - * - */ - -export const DeleteCustomerInvoiceByIdRequestSchema = z.object({ - id: z.string(), -}); - -export type DeleteCustomerInvoiceByIdRequestDTO = z.infer< - typeof DeleteCustomerInvoiceByIdRequestSchema ->; diff --git a/modules/customer-invoices/src/common/dto/request/get-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/get-customer-invoice-by-id.request.dto.ts deleted file mode 100644 index 6b915088..00000000 --- a/modules/customer-invoices/src/common/dto/request/get-customer-invoice-by-id.request.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod/v4"; - -export const GetCustomerInvoiceByIdRequestSchema = z.object({ - invoice_id: z.string(), -}); - -export type GetCustomerInvoiceByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/index.ts b/modules/customer-invoices/src/common/dto/request/index.ts index 6647c31a..457e40a2 100644 --- a/modules/customer-invoices/src/common/dto/request/index.ts +++ b/modules/customer-invoices/src/common/dto/request/index.ts @@ -1,7 +1,2 @@ -export * from "./change-status-customer-invoice-by-id.request.dto"; -export * from "./create-customer-invoice.request.dto"; -export * from "./customer-invoices-list.request.dto"; -export * from "./delete-customer-invoice-by-id.request.dto"; -export * from "./get-customer-invoice-by-id.request.dto"; -export * from "./report-customer-invoice-by-id.request.dto"; -export * from "./update-customer-invoice-by-id.request.dto"; +export * from "./issue-invoices"; +export * from "./proformas"; diff --git a/modules/customer-invoices/src/common/dto/request/issue-invoices/get-issue-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/issue-invoices/get-issue-invoice-by-id.request.dto.ts new file mode 100644 index 00000000..ae12a5f3 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/issue-invoices/get-issue-invoice-by-id.request.dto.ts @@ -0,0 +1,7 @@ +import { z } from "zod/v4"; + +export const GetIssueInvoiceByIdRequestSchema = z.object({ + invoice_id: z.string(), +}); + +export type GetIssueInvoiceByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/issue-invoices/index.ts b/modules/customer-invoices/src/common/dto/request/issue-invoices/index.ts new file mode 100644 index 00000000..1c4bb67f --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/issue-invoices/index.ts @@ -0,0 +1,3 @@ +export * from "./get-issue-invoice-by-id.request.dto"; +export * from "./list-issue-invoices.request.dto"; +export * from "./report-issue-invoice-by-id.request.dto"; diff --git a/modules/customer-invoices/src/common/dto/request/issue-invoices/list-issue-invoices.request.dto.ts b/modules/customer-invoices/src/common/dto/request/issue-invoices/list-issue-invoices.request.dto.ts new file mode 100644 index 00000000..618e85c8 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/issue-invoices/list-issue-invoices.request.dto.ts @@ -0,0 +1,5 @@ +import { CriteriaSchema } from "@erp/core"; +import { z } from "zod/v4"; + +export const ListIssueInvoicesRequestSchema = CriteriaSchema; +export type ListIssueInvoicesRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/issue-invoices/report-issue-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/issue-invoices/report-issue-invoice-by-id.request.dto.ts new file mode 100644 index 00000000..34cfea7f --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/issue-invoices/report-issue-invoice-by-id.request.dto.ts @@ -0,0 +1,7 @@ +import { z } from "zod/v4"; + +export const ReportIssueInvoiceByIdRequestSchema = z.object({ + invoice_id: z.string(), +}); + +export type ReportIssueInvoiceByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/change-status-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/change-status-proforma-by-id.request.dto.ts new file mode 100644 index 00000000..c0c3264c --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/change-status-proforma-by-id.request.dto.ts @@ -0,0 +1,13 @@ +import { z } from "zod/v4"; + +export const ChangeStatusProformaByIdParamsRequestSchema = z.object({ + proforma_id: z.string(), +}); + +export const ChangeStatusProformaByIdRequestSchema = z.object({ + new_status: z.string(), +}); + +export type ChangeStatusProformaByIdRequestDTO = Partial< + z.infer +>; diff --git a/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts similarity index 68% rename from modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts rename to modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts index 836f2108..d0563c8a 100644 --- a/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts @@ -1,7 +1,7 @@ import { NumericStringSchema, PercentageSchema } from "@erp/core"; import { z } from "zod/v4"; -export const CreateCustomerInvoiceItemRequestSchema = z.object({ +export const CreateProformaItemRequestSchema = z.object({ id: z.uuid(), position: z.string(), description: z.string().default(""), @@ -10,8 +10,9 @@ export const CreateCustomerInvoiceItemRequestSchema = z.object({ discount_percentage: NumericStringSchema.default(""), taxes: z.string().default(""), }); +export type CreateProformaItemRequestDTO = z.infer; -export const CreateCustomerInvoiceRequestSchema = z.object({ +export const CreateProformaRequestSchema = z.object({ id: z.uuid(), invoice_number: z.string(), @@ -35,10 +36,6 @@ export const CreateCustomerInvoiceRequestSchema = z.object({ payment_method: z.string().default(""), - items: z.array(CreateCustomerInvoiceItemRequestSchema).default([]), + items: z.array(CreateProformaItemRequestSchema).default([]), }); - -export type CreateCustomerInvoiceItemRequestDTO = z.infer< - typeof CreateCustomerInvoiceItemRequestSchema ->; -export type CreateCustomerInvoiceRequestDTO = z.infer; +export type CreateProformaRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/delete-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/delete-proforma-by-id.request.dto.ts new file mode 100644 index 00000000..eba27fec --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/delete-proforma-by-id.request.dto.ts @@ -0,0 +1,13 @@ +import { z } from "zod/v4"; + +/** + * Este DTO es utilizado por el endpoint: + * `DELETE /customer-invoices/:id` (eliminar una factura por ID). + * + */ + +export const DeleteProformaByIdRequestSchema = z.object({ + id: z.string(), +}); + +export type DeleteProformaByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/get-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/get-proforma-by-id.request.dto.ts new file mode 100644 index 00000000..fb74d29d --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/get-proforma-by-id.request.dto.ts @@ -0,0 +1,7 @@ +import { z } from "zod/v4"; + +export const GetProformaByIdRequestSchema = z.object({ + proforma_id: z.string(), +}); + +export type GetProformaByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/index.ts b/modules/customer-invoices/src/common/dto/request/proformas/index.ts new file mode 100644 index 00000000..626ccdf1 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/index.ts @@ -0,0 +1,7 @@ +export * from "./change-status-proforma-by-id.request.dto"; +export * from "./create-proforma.request.dto"; +export * from "./delete-proforma-by-id.request.dto"; +export * from "./get-proforma-by-id.request.dto"; +export * from "./list-proformas.request.dto"; +export * from "./report-proforma-by-id.request.dto"; +export * from "./update-proforma-by-id.request.dto"; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/list-proformas.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/list-proformas.request.dto.ts new file mode 100644 index 00000000..30a6068f --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/list-proformas.request.dto.ts @@ -0,0 +1,5 @@ +import { CriteriaSchema } from "@erp/core"; +import type { z } from "zod/v4"; + +export const ListProformasRequestSchema = CriteriaSchema; +export type ProformasListRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts new file mode 100644 index 00000000..f2ff55a8 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts @@ -0,0 +1,7 @@ +import { z } from "zod/v4"; + +export const ReportProformaByIdRequestSchema = z.object({ + proforma_id: z.string(), +}); + +export type ReportProformaByIdRequestDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts similarity index 77% rename from modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts rename to modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts index 107c83c2..1cb92808 100644 --- a/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/update-proforma-by-id.request.dto.ts @@ -1,11 +1,11 @@ import { MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core"; import { z } from "zod/v4"; -export const UpdateCustomerInvoiceByIdParamsRequestSchema = z.object({ +export const UpdateProformaByIdParamsRequestSchema = z.object({ proforma_id: z.string(), }); -export const UpdateCustomerInvoiceByIdRequestSchema = z.object({ +export const UpdateProformaByIdRequestSchema = z.object({ series: z.string().optional(), invoice_date: z.string().optional(), @@ -38,6 +38,4 @@ export const UpdateCustomerInvoiceByIdRequestSchema = z.object({ .default([]), }); -export type UpdateCustomerInvoiceByIdRequestDTO = Partial< - z.infer ->; +export type UpdateProformaByIdRequestDTO = Partial>; diff --git a/modules/customer-invoices/src/common/dto/request/report-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/report-customer-invoice-by-id.request.dto.ts deleted file mode 100644 index 7f57648d..00000000 --- a/modules/customer-invoices/src/common/dto/request/report-customer-invoice-by-id.request.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { z } from "zod/v4"; - -export const ReportCustomerInvoiceByIdRequestSchema = z.object({ - invoice_id: z.string(), -}); - -export type ReportCustomerInvoiceByIdRequestDTO = z.infer< - typeof ReportCustomerInvoiceByIdRequestSchema ->; diff --git a/modules/customer-invoices/src/common/dto/response/index.ts b/modules/customer-invoices/src/common/dto/response/index.ts index a0d6acaa..457e40a2 100644 --- a/modules/customer-invoices/src/common/dto/response/index.ts +++ b/modules/customer-invoices/src/common/dto/response/index.ts @@ -1,4 +1,2 @@ -export * from "./create-customer-invoice.response.dto"; -export * from "./get-customer-invoice-by-id.response.dto"; -export * from "./list-customer-invoices.response.dto"; -export * from "./update-customer-invoice-by-id.response.dto"; +export * from "./issue-invoices"; +export * from "./proformas"; diff --git a/modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/issue-invoices/get-issue-invoice-by-id.response.dto.ts similarity index 91% rename from modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.response.dto.ts rename to modules/customer-invoices/src/common/dto/response/issue-invoices/get-issue-invoice-by-id.response.dto.ts index 9c377395..f8f7f5f0 100644 --- a/modules/customer-invoices/src/common/dto/response/get-customer-invoice-by-id.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/issue-invoices/get-issue-invoice-by-id.response.dto.ts @@ -1,7 +1,7 @@ import { MetadataSchema, MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core"; import { z } from "zod/v4"; -export const GetCustomerInvoiceByIdResponseSchema = z.object({ +export const GetIssueInvoiceByIdResponseSchema = z.object({ id: z.uuid(), company_id: z.uuid(), @@ -79,6 +79,4 @@ export const GetCustomerInvoiceByIdResponseSchema = z.object({ metadata: MetadataSchema.optional(), }); -export type GetCustomerInvoiceByIdResponseDTO = z.infer< - typeof GetCustomerInvoiceByIdResponseSchema ->; +export type GetIssueInvoiceByIdResponseDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/issue-invoices/index.ts b/modules/customer-invoices/src/common/dto/response/issue-invoices/index.ts new file mode 100644 index 00000000..61996aea --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/issue-invoices/index.ts @@ -0,0 +1,2 @@ +export * from "./get-issue-invoice-by-id.response.dto"; +export * from "./list-issue-invoices.response.dto"; diff --git a/modules/customer-invoices/src/common/dto/response/list-customer-invoices.response.dto.ts b/modules/customer-invoices/src/common/dto/response/issue-invoices/list-issue-invoices.response.dto.ts similarity index 85% rename from modules/customer-invoices/src/common/dto/response/list-customer-invoices.response.dto.ts rename to modules/customer-invoices/src/common/dto/response/issue-invoices/list-issue-invoices.response.dto.ts index 031e26e5..ca372be0 100644 --- a/modules/customer-invoices/src/common/dto/response/list-customer-invoices.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/issue-invoices/list-issue-invoices.response.dto.ts @@ -6,7 +6,7 @@ import { } from "@erp/core"; import { z } from "zod/v4"; -export const ListCustomerInvoicesResponseSchema = createPaginatedListSchema( +export const ListIssueInvoicesResponseSchema = createPaginatedListSchema( z.object({ id: z.uuid(), company_id: z.uuid(), @@ -51,4 +51,4 @@ export const ListCustomerInvoicesResponseSchema = createPaginatedListSchema( }) ); -export type ListCustomerInvoicesResponseDTO = z.infer; +export type ListIssueInvoicesResponseDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/create-customer-invoice.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/create-proforma.response.dto.ts similarity index 86% rename from modules/customer-invoices/src/common/dto/response/create-customer-invoice.response.dto.ts rename to modules/customer-invoices/src/common/dto/response/proformas/create-proforma.response.dto.ts index c5cbb799..a694867d 100644 --- a/modules/customer-invoices/src/common/dto/response/create-customer-invoice.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/proformas/create-proforma.response.dto.ts @@ -7,7 +7,7 @@ import { } from "@erp/core"; import { z } from "zod/v4"; -export const CreateCustomerInvoiceResponseSchema = z.object({ +export const CreateProformaResponseSchema = z.object({ id: z.uuid(), company_id: z.uuid(), @@ -52,4 +52,4 @@ export const CreateCustomerInvoiceResponseSchema = z.object({ metadata: MetadataSchema.optional(), }); -export type CreateCustomerInvoiceResponseDTO = z.infer; +export type CreateProformaResponseDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/get-proforma-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/proformas/get-proforma-by-id.response.dto.ts new file mode 100644 index 00000000..45b6a029 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/get-proforma-by-id.response.dto.ts @@ -0,0 +1,82 @@ +import { MetadataSchema, MoneySchema, PercentageSchema, QuantitySchema } from "@erp/core"; +import { z } from "zod/v4"; + +export const GetProformaByIdResponseSchema = z.object({ + id: z.uuid(), + company_id: z.uuid(), + + is_proforma: z.string(), + invoice_number: z.string(), + status: z.string(), + series: z.string(), + + invoice_date: z.string(), + operation_date: z.string(), + + reference: z.string(), + description: z.string(), + notes: z.string(), + + language_code: z.string(), + currency_code: z.string(), + + customer_id: z.string(), + recipient: z.object({ + id: z.string(), + name: z.string(), + tin: z.string(), + street: z.string(), + street2: z.string(), + city: z.string(), + province: z.string(), + postal_code: z.string(), + country: z.string(), + }), + + taxes: z.array( + z.object({ + tax_code: z.string(), + taxable_amount: MoneySchema, + taxes_amount: MoneySchema, + }) + ), + + payment_method: z + .object({ + payment_id: z.string(), + payment_description: z.string(), + }) + .optional(), + + subtotal_amount: MoneySchema, + items_discount_amount: MoneySchema, + discount_percentage: PercentageSchema, + discount_amount: MoneySchema, + taxable_amount: MoneySchema, + taxes_amount: MoneySchema, + total_amount: MoneySchema, + + items: z.array( + z.object({ + id: z.uuid(), + is_valued: z.string(), + position: z.string(), + description: z.string(), + quantity: QuantitySchema, + unit_amount: MoneySchema, + + tax_codes: z.array(z.string()), + + subtotal_amount: MoneySchema, + discount_percentage: PercentageSchema, + discount_amount: MoneySchema, + taxable_amount: MoneySchema, + taxes_amount: MoneySchema, + total_amount: MoneySchema, + }) + ), + + metadata: MetadataSchema.optional(), +}); + +export type GetProformaByIdResponseDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/proformas/index.ts b/modules/customer-invoices/src/common/dto/response/proformas/index.ts new file mode 100644 index 00000000..3f1edcee --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/index.ts @@ -0,0 +1,3 @@ +export * from "./create-proforma.response.dto"; +export * from "./get-proforma-by-id.response.dto"; +export * from "./list-proformas.response.dto"; 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 new file mode 100644 index 00000000..ed18c797 --- /dev/null +++ b/modules/customer-invoices/src/common/dto/response/proformas/list-proformas.response.dto.ts @@ -0,0 +1,54 @@ +import { + MetadataSchema, + MoneySchema, + PercentageSchema, + createPaginatedListSchema, +} from "@erp/core"; +import { z } from "zod/v4"; + +export const ListProformasResponseSchema = createPaginatedListSchema( + z.object({ + id: z.uuid(), + company_id: z.uuid(), + is_proforma: z.boolean(), + + customer_id: z.string(), + + invoice_number: z.string(), + status: z.string(), + series: z.string(), + + invoice_date: z.string(), + operation_date: z.string(), + + language_code: z.string(), + currency_code: z.string(), + + reference: z.string(), + description: z.string(), + + recipient: z.object({ + tin: z.string(), + name: z.string(), + street: z.string(), + street2: z.string(), + city: z.string(), + postal_code: z.string(), + province: z.string(), + country: z.string(), + }), + + taxes: z.string(), + + subtotal_amount: MoneySchema, + discount_percentage: PercentageSchema, + discount_amount: MoneySchema, + taxable_amount: MoneySchema, + taxes_amount: MoneySchema, + total_amount: MoneySchema, + + metadata: MetadataSchema.optional(), + }) +); + +export type ListProformasResponseDTO = z.infer; diff --git a/modules/customer-invoices/src/common/dto/response/update-customer-invoice-by-id.response.dto.ts b/modules/customer-invoices/src/common/dto/response/update-customer-invoice-by-id.response.dto.ts deleted file mode 100644 index 9d2f1941..00000000 --- a/modules/customer-invoices/src/common/dto/response/update-customer-invoice-by-id.response.dto.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AmountSchema, MetadataSchema, PercentageSchema, QuantitySchema } from "@erp/core"; -import { z } from "zod/v4"; - -export const UpdateCustomerInvoiceByIdResponseSchema = z.object({ - id: z.uuid(), - company_id: z.uuid(), - - invoice_number: z.string(), - status: z.string(), - series: z.string(), - - invoice_date: z.string(), - operation_date: z.string(), - - notes: z.string(), - - language_code: z.string(), - currency_code: z.string(), - - subtotal_amount: AmountSchema, - discount_percentage: PercentageSchema, - discount_amount: AmountSchema, - taxable_amount: AmountSchema, - tax_amount: AmountSchema, - total_amount: AmountSchema, - - items: z.array( - z.object({ - id: z.uuid(), - position: z.string(), - description: z.string(), - quantity: QuantitySchema, - unit_amount: AmountSchema, - discount_percentage: PercentageSchema, - total_amount: AmountSchema, - }) - ), - - metadata: MetadataSchema.optional(), -}); - -export type UpdateCustomerInvoiceByIdResponseDTO = z.infer< - typeof UpdateCustomerInvoiceByIdResponseSchema ->; diff --git a/modules/customer-invoices/src/web/customer-invoice-routes.tsx b/modules/customer-invoices/src/web/customer-invoice-routes.tsx index e0cf5071..bc447a12 100644 --- a/modules/customer-invoices/src/web/customer-invoice-routes.tsx +++ b/modules/customer-invoices/src/web/customer-invoice-routes.tsx @@ -1,15 +1,13 @@ -import { ModuleClientParams } from "@erp/core/client"; +import type { ModuleClientParams } from "@erp/core/client"; import { lazy } from "react"; -import { Outlet, RouteObject } from "react-router-dom"; +import { Outlet, type RouteObject } from "react-router-dom"; // Lazy load components const InvoicesLayout = lazy(() => import("./components").then((m) => ({ default: m.InvoicesLayout })) ); -const InvoiceListPage = lazy(() => - import("./pages").then((m) => ({ default: m.InvoiceListPage })) -); +const InvoiceListPage = lazy(() => import("./pages").then((m) => ({ default: m.InvoiceListPage }))); const CustomerInvoiceAdd = lazy(() => import("./pages").then((m) => ({ default: m.CustomerInvoiceCreate })) @@ -20,6 +18,20 @@ const InvoiceUpdatePage = lazy(() => export const CustomerInvoiceRoutes = (params: ModuleClientParams): RouteObject[] => { return [ + { + path: "proformas", + element: ( + + + + ), + children: [ + { path: "", index: true, element: }, // index + { path: "list", element: }, + { path: "create", element: }, + { path: ":id/edit", element: }, + ], + }, { path: "customer-invoices", element: ( @@ -30,8 +42,6 @@ export const CustomerInvoiceRoutes = (params: ModuleClientParams): RouteObject[] children: [ { path: "", index: true, element: }, // index { path: "list", element: }, - { path: "create", element: }, - { path: ":id/edit", element: }, // /*{ path: "create", element: }, diff --git a/modules/customer-invoices/src/web/pages/proformas/list/index.ts b/modules/customer-invoices/src/web/pages/proformas/list/index.ts new file mode 100644 index 00000000..2e67ac4b --- /dev/null +++ b/modules/customer-invoices/src/web/pages/proformas/list/index.ts @@ -0,0 +1 @@ +export * from "./proforma-list-page"; diff --git a/modules/customer-invoices/src/web/pages/proformas/list/proforma-list-page.tsx b/modules/customer-invoices/src/web/pages/proformas/list/proforma-list-page.tsx new file mode 100644 index 00000000..324fcfd0 --- /dev/null +++ b/modules/customer-invoices/src/web/pages/proformas/list/proforma-list-page.tsx @@ -0,0 +1,111 @@ +import { PageHeader } from "@erp/core/components"; +import { ErrorAlert } from "@erp/customers/components"; +import { AppContent, AppHeader, BackHistoryButton, useDebounce } from "@repo/rdx-ui/components"; +import { Button } from "@repo/shadcn-ui/components"; +import { PlusIcon } from "lucide-react"; +import { useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useInvoicesQuery } from "../../hooks"; +import { useTranslation } from "../../i18n"; +import { invoiceResumeDtoToFormAdapter } from "../../schemas/invoice-resume-dto.adapter"; +import { InvoicesListGrid } from "./invoices-list-grid"; + +export const ProformaListPage = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + const [search, setSearch] = useState(""); + + const debouncedQ = useDebounce(search, 300); + + const criteria = useMemo( + () => ({ + q: debouncedQ || "", + pageSize, + pageNumber: pageIndex, + }), + [pageSize, pageIndex, debouncedQ] + ); + + const { data, isLoading, isError, error } = useInvoicesQuery({ + criteria, + }); + + const invoicesPageData = useMemo(() => { + if (!data) return undefined; + return { + ...data, + items: invoiceResumeDtoToFormAdapter.fromDto(data.items), + }; + }, [data]); + + const handlePageChange = (newPageIndex: number) => { + setPageIndex(newPageIndex); + }; + + const handlePageSizeChange = (newSize: number) => { + setPageSize(newSize); + setPageIndex(0); + }; + + const handleSearchChange = (value: string) => { + // Normalización ligera: recorta y colapsa espacios internos + const cleaned = value.trim().replace(/\s+/g, " "); + setSearch(cleaned); + setPageIndex(0); + }; + + if (isError || !invoicesPageData) { + return ( + + + + + ); + } + + return ( + <> + + + + + } + /> + + +
+
+ +
+
+
+ + ); +}; diff --git a/modules/customer-invoices/src/web/schemas/index.ts b/modules/customer-invoices/src/web/schemas/index.ts index ded0809d..fb60f5b7 100644 --- a/modules/customer-invoices/src/web/schemas/index.ts +++ b/modules/customer-invoices/src/web/schemas/index.ts @@ -1,5 +1,5 @@ -export * from "./invoice-dto.adapter"; -export * from "./invoice-resume-dto.adapter"; -export * from "./invoice-resume.form.schema"; export * from "./invoice.form.schema"; +export * from "./invoice-dto.adapter"; +export * from "./invoice-resume.form.schema"; +export * from "./invoice-resume-dto.adapter"; export * from "./invoices.api.schema"; diff --git a/modules/customer-invoices/src/web/schemas/invoice-dto.adapter.ts b/modules/customer-invoices/src/web/schemas/invoice-dto.adapter.ts index 6e58c69a..f66ca42e 100644 --- a/modules/customer-invoices/src/web/schemas/invoice-dto.adapter.ts +++ b/modules/customer-invoices/src/web/schemas/invoice-dto.adapter.ts @@ -1,16 +1,18 @@ import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core"; -import { - GetCustomerInvoiceByIdResponseDTO, + +import type { + GetIssueInvoiceByIdResponseDTO, UpdateCustomerInvoiceByIdRequestDTO, } from "../../common"; -import { InvoiceContextValue } from "../context"; -import { InvoiceFormData } from "./invoice.form.schema"; +import type { InvoiceContextValue } from "../context"; + +import type { InvoiceFormData } from "./invoice.form.schema"; /** * Convierte el DTO completo de API a datos numéricos para el formulario. */ export const invoiceDtoToFormAdapter = { - fromDto(dto: GetCustomerInvoiceByIdResponseDTO, context: InvoiceContextValue): InvoiceFormData { + fromDto(dto: GetIssueInvoiceByIdResponseDTO, context: InvoiceContextValue): InvoiceFormData { const { taxCatalog } = context; return { invoice_number: dto.invoice_number, @@ -30,6 +32,7 @@ export const invoiceDtoToFormAdapter = { currency_code: dto.currency_code, subtotal_amount: MoneyDTOHelper.toNumber(dto.subtotal_amount), + items_discount_amount: 0, discount_percentage: PercentageDTOHelper.toNumber(dto.discount_percentage), discount_amount: MoneyDTOHelper.toNumber(dto.discount_amount), taxable_amount: MoneyDTOHelper.toNumber(dto.taxable_amount), diff --git a/modules/customer-invoices/src/web/schemas/invoices.api.schema.ts b/modules/customer-invoices/src/web/schemas/invoices.api.schema.ts index 8fcfeba3..697cbab7 100644 --- a/modules/customer-invoices/src/web/schemas/invoices.api.schema.ts +++ b/modules/customer-invoices/src/web/schemas/invoices.api.schema.ts @@ -1,15 +1,15 @@ -import { z } from "zod/v4"; +import type { PaginationSchema } from "@erp/core"; +import type { ArrayElement } from "@repo/rdx-utils"; +import type { z } from "zod/v4"; -import { PaginationSchema } from "@erp/core"; -import { ArrayElement } from "@repo/rdx-utils"; import { CreateCustomerInvoiceRequestSchema, - GetCustomerInvoiceByIdResponseSchema, - ListCustomerInvoicesResponseSchema, + GetIssueInvoiceByIdResponseSchema, + ListIssueInvoicesResponseSchema, UpdateCustomerInvoiceByIdRequestSchema, } from "../../common"; -export const CustomerInvoiceSchema = GetCustomerInvoiceByIdResponseSchema.omit({ +export const CustomerInvoiceSchema = GetIssueInvoiceByIdResponseSchema.omit({ metadata: true, }); export const CustomerInvoiceCreateSchema = CreateCustomerInvoiceRequestSchema; @@ -24,7 +24,7 @@ export type CustomerInvoiceCreateInput = z.infer; // Cuerpo para actualizar // Resultado de consulta con criteria (paginado, etc.) -export const CustomerInvoicesPageSchema = ListCustomerInvoicesResponseSchema.omit({ +export const CustomerInvoicesPageSchema = ListIssueInvoicesResponseSchema.omit({ metadata: true, }); diff --git a/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts b/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts index 29a7a265..b02303c8 100644 --- a/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts +++ b/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts @@ -1,6 +1,7 @@ -import { FindOptions, Op, OrderItem, Sequelize, WhereOptions } from "sequelize"; -import { Criteria } from "./critera"; -import { type ConvertParams, type CriteriaMappings, ICriteriaToOrmConverter } from "./types"; +import { type FindOptions, Op, type OrderItem, Sequelize, type WhereOptions } from "sequelize"; + +import type { Criteria } from "./critera"; +import type { ConvertParams, CriteriaMappings, ICriteriaToOrmConverter } from "./types"; import { appendOrder, prependOrder } from "./utils"; /** @@ -146,7 +147,7 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { } } - private transformValue(operator: symbol, value: any): any { + private transformValue(operator: symbol, value: unknown): unknown { if (operator === Op.like || operator === Op.notLike) return `%${value}%`; return value; }