From d57b71c200dba88afd1a2afffbf80356ae6fdedd Mon Sep 17 00:00:00 2001 From: David Date: Thu, 10 Sep 2026 13:26:33 +0200 Subject: [PATCH] refactor: standardize import statements and formatting across components - Updated import statements in `textarea.tsx`, `toggle.tsx`, and `tooltip.tsx` for consistency. - Added missing semicolons and adjusted formatting in `use-mobile.ts` and `use-mobile.tsx`. - Cleaned up `package.json` and `root.json` files by removing unnecessary line breaks. - Simplified `turbo.json` by consolidating array formatting for better readability. --- apps/server/package.json | 2 +- apps/server/src/app.ts | 3 +- .../src/config/company-certificates.json | 22 +- apps/server/src/config/log-config.ts | 4 +- apps/server/src/index.ts | 16 +- .../src/infrastructure/audit/audit-reader.ts | 8 +- .../src/lib/modules/module-loader.test.ts | 22 +- .../http/audit-http.integration.test.ts | 3 +- .../http/audit-runtime.integration.test.ts | 4 +- .../http/proforma-creator.integration.test.ts | 18 +- apps/server/tsup.config.ts | 13 +- apps/web/index.html | 30 +- apps/web/package.json | 2 +- apps/web/src/components/avatar-demo.tsx | 36 +- apps/web/src/components/b-demo.tsx | 14 +- apps/web/src/components/module-routes.tsx | 3 +- apps/web/src/components/slider-demo.tsx | 2 +- .../home/ui/blocks/home-attention-panel.tsx | 4 +- .../blocks/home-monthly-billing-summary.tsx | 13 +- .../blocks/home-proforma-status-summary.tsx | 5 +- .../home/ui/blocks/home-recent-activity.tsx | 4 +- apps/web/src/layout/app-sidebar-nav.tsx | 5 +- .../use-app-company-switcher-controller.ts | 5 +- .../web/src/lib/hooks/use-theme/use-theme.tsx | 4 +- apps/web/src/locales/i18n.ts | 2 +- .../api/initial-onboarding.api.test.ts | 4 +- .../onboarding/api/initial-onboarding.api.ts | 32 +- .../api/initial-onboarding.form.test.ts | 10 +- ...-initial-onboarding-and-restore-session.ts | 12 +- .../onboarding/ui/initial-onboarding-page.tsx | 19 +- .../src/pages/render-error-fallback.test.tsx | 1 + apps/web/src/pages/render-error-fallback.tsx | 5 +- apps/web/src/routes/app-routes.tsx | 6 +- apps/web/src/routes/require-company-guard.tsx | 19 +- .../resolve-initial-onboarding-navigation.ts | 16 +- ...esolve-route-error-page-view-model.test.ts | 6 +- .../resolve-route-error-page-view-model.ts | 7 +- .../src/settings/build-settings-routes.tsx | 5 +- apps/worker/package.json | 2 +- apps/worker/src/config/worker-config.ts | 38 +- apps/worker/src/index.ts | 57 +- .../jobs/recurring-proforma-scheduled-job.ts | 61 +- apps/worker/src/modules/register-modules.ts | 2 +- apps/worker/src/scheduler/register-jobs.ts | 32 +- apps/worker/src/scheduler/scheduled-job.ts | 6 +- apps/worker/src/scheduler/scheduler.test.ts | 25 +- apps/worker/src/scheduler/scheduler.ts | 52 +- apps/worker/tsconfig.json | 9 +- apps/worker/tsup.config.ts | 11 +- biome.json | 95 +-- eslint.config.mjs | 6 +- modules/audit/package.json | 2 +- .../controllers/use-audit-page-controller.ts | 3 +- .../audit/src/web/list/ui/audit-filters.tsx | 17 +- .../web/list/utils/audit-detail-presenters.ts | 19 +- .../list/utils/audit-page-search-params.ts | 38 +- .../audit/src/web/list/utils/audit-period.ts | 19 +- .../shared/api/find-audit-entry-by-id.api.ts | 4 +- .../src/web/shared/hooks/use-audit-entry.ts | 4 +- .../mappers/audit-entry-detail.mapper.ts | 8 +- modules/auth/package.json | 2 +- modules/auth/src/api/lib/passport/index.ts | 1 + .../lib/passport/passport-auth-provider.ts | 2 +- modules/auth/src/common/dto/index.ts | 2 +- modules/auth/src/web/auth-routes.tsx | 2 +- .../auth/src/web/components/auth-guard.tsx | 5 +- .../auth/src/web/components/auth-layout.tsx | 2 +- .../auth/src/web/components/login-form.tsx | 16 +- modules/auth/src/web/context/auth-context.tsx | 7 +- modules/auth/src/web/hooks/use-auth.ts | 3 +- modules/auth/src/web/services/auth-service.ts | 3 +- modules/auth/src/web/services/me-service.ts | 4 +- modules/auth/src/web/services/user-service.ts | 3 +- modules/catalogs/package.json | 2 +- .../services/payment-method-creator.ts | 4 +- .../services/payment-method-deleter.ts | 4 +- .../services/payment-method-status-changer.ts | 4 +- .../services/payment-method-updater.ts | 4 +- .../set-default-payment-method.service.ts | 8 +- .../delete-payment-method-by-id.use-case.ts | 4 +- .../disable-payment-method-by-id.use-case.ts | 4 +- .../enable-payment-method-by-id.use-case.ts | 4 +- .../delete-payment-term-by-id.use-case.ts | 4 +- .../disable-payment-term-by-id.use-case.ts | 4 +- .../enable-payment-term-by-id.use-case.ts | 4 +- .../create-tax-definition-input.mapper.ts | 2 +- .../tax-definitions/repositories/index.ts | 2 +- .../tax-definition-status-changer.service.ts | 24 +- .../tax-definition-updater.service.ts | 12 +- .../delete-tax-definition-by-id.use-case.ts | 4 +- .../disable-tax-definition-by-id.use-case.ts | 4 +- .../enable-tax-definition-by-id.use-case.ts | 4 +- .../get-tax-definition-by-id.use-case.ts | 4 +- .../list-tax-definitions.use-case.ts | 4 +- .../update-tax-definition-by-id.use-case.ts | 8 +- .../delete-tax-regime-by-id.use-case.ts | 4 +- .../disable-tax-regime-by-id.use-case.ts | 4 +- .../enable-tax-regime-by-id.use-case.ts | 4 +- .../payment-method.aggregate.test.ts | 2 +- .../payment-method.aggregate.ts | 2 +- .../__tests__/payment-term.aggregate.test.ts | 10 +- .../payment-terms/payment-term-percentage.ts | 4 +- .../payment-method-reference-usage-checker.ts | 17 +- .../infrastructure/tax-definitions/index.ts | 2 +- .../sequelize-tax-definition-domain.mapper.ts | 10 +- .../sequelize-tax-definition.repository.ts | 18 +- modules/catalogs/src/api/module.test.ts | 4 +- .../get-tax-definition-by-id.response.dto.ts | 4 +- modules/catalogs/src/web/catalogs-routes.tsx | 3 +- .../payment-methods/shared/adapters/index.ts | 2 +- .../shared/api/create-payment-method.api.ts | 5 +- .../web/payment-methods/shared/api/index.ts | 8 +- .../shared/api/update-payment-method.api.ts | 14 +- .../web/payment-methods/shared/hooks/index.ts | 8 +- .../use-payment-method-create-mutation.ts | 2 +- .../use-payment-method-delete-mutation.ts | 2 +- .../use-payment-method-disable-mutation.ts | 2 +- .../use-payment-method-enable-mutation.ts | 2 +- ...use-payment-method-set-default-mutation.ts | 2 +- .../use-payment-method-update-mutation.ts | 2 +- .../src/web/payment-methods/shared/index.ts | 4 +- .../src/web/payment-methods/utils/index.ts | 2 +- .../payment-terms/shared/adapters/index.ts | 2 +- .../src/web/payment-terms/shared/api/index.ts | 8 +- .../shared/api/update-payment-term.api.ts | 5 +- .../web/payment-terms/shared/hooks/index.ts | 8 +- .../hooks/use-payment-term-create-mutation.ts | 2 +- .../hooks/use-payment-term-delete-mutation.ts | 2 +- .../use-payment-term-disable-mutation.ts | 2 +- .../hooks/use-payment-term-enable-mutation.ts | 2 +- .../hooks/use-payment-term-update-mutation.ts | 2 +- .../src/web/payment-terms/shared/index.ts | 4 +- .../src/web/payment-terms/utils/index.ts | 2 +- ...use-payment-methods-settings-controller.ts | 95 ++- .../payment-methods/entities/index.ts | 2 +- .../payment-methods/ui/components/index.ts | 2 +- .../components/payment-method-form-dialog.tsx | 4 +- .../web/settings/payment-methods/ui/index.ts | 2 +- .../utils/build-payment-method-form.ts | 4 +- .../use-payment-terms-settings-controller.ts | 87 ++- .../entities/payment-term-form.schema.ts | 4 +- .../payment-terms/ui/components/index.ts | 2 +- .../payment-terms-no-results-state.tsx | 6 +- .../utils/build-payment-term-form.ts | 1 + .../utils/build-payment-term-params.ts | 6 +- .../payment-term-due-rules.utils.test.ts | 4 +- .../use-tax-regimes-settings-controller.ts | 86 +- .../entities/tax-regime-form.schema.ts | 5 +- .../tax-regimes/ui/components/index.ts | 2 +- .../ui/components/tax-regime-form-dialog.tsx | 9 +- .../src/web/settings/tax-regimes/ui/index.ts | 2 +- .../web/tax-regimes/shared/adapters/index.ts | 2 +- .../shared/api/get-tax-regime-by-id.api.ts | 10 +- .../src/web/tax-regimes/shared/api/index.ts | 8 +- .../shared/api/update-tax-regime.api.ts | 5 +- .../src/web/tax-regimes/shared/hooks/index.ts | 8 +- .../hooks/use-tax-regime-create-mutation.ts | 2 +- .../hooks/use-tax-regime-delete-mutation.ts | 2 +- .../hooks/use-tax-regime-disable-mutation.ts | 2 +- .../hooks/use-tax-regime-enable-mutation.ts | 2 +- .../src/web/tax-regimes/shared/index.ts | 4 +- modules/companies/package.json | 2 +- .../src/api/application/companies/index.ts | 2 +- .../mappers/company-address.mapper.ts | 24 +- .../create-company-input.mapper.test.ts | 8 +- .../mappers/create-company-input.mapper.ts | 40 +- .../mappers/update-company-input.mapper.ts | 86 +- ...pdate-current-company-input.mapper.test.ts | 8 +- .../update-current-company-input.mapper.ts | 78 +- .../use-cases/create-company.use-case.ts | 4 +- .../use-cases/disable-company.use-case.ts | 9 +- .../use-cases/enable-company.use-case.ts | 9 +- .../use-cases/get-company-by-id.use-case.ts | 9 +- .../use-cases/get-current-company.use-case.ts | 9 +- .../application/companies/use-cases/index.ts | 2 +- .../use-cases/update-company.use-case.ts | 12 +- .../update-current-company.use-case.ts | 12 +- .../api/domain/companies/company-status.vo.ts | 17 +- .../src/api/domain/companies/index.ts | 4 +- .../sequelize-company-domain.mapper.ts | 4 +- .../sequelize-company.repository.ts | 6 +- modules/companies/src/api/module.test.ts | 8 +- .../companies/src/common/dto/request/index.ts | 2 +- .../dto/response/company.response.dto.test.ts | 29 +- .../dto/response/company.response.dto.ts | 8 +- modules/companies/src/web/company-routes.tsx | 3 +- .../entities/company-settings-form.schema.ts | 22 +- .../company-details/shared/hooks/keys.ts | 6 +- .../use-company-settings-update-mutation.ts | 7 +- ...ils-settings-page-error-view-model.test.ts | 9 +- ...-details-settings-page-error-view-model.ts | 4 +- modules/core/package.json | 2 +- .../services/document-generation-service.ts | 2 +- .../application/mappers/patch-collector.ts | 2 +- .../presenters/presenter-registry.ts | 2 +- .../renderers/renderer-registry.ts | 2 +- .../audit/sequelize/audit-change.model.ts | 74 +- .../audit/sequelize/audit-event.model.ts | 102 ++- .../audit/sequelize/sequelize-audit-store.ts | 6 +- .../operation-audit-context-provider.ts | 4 +- .../context/operation-context-store.ts | 20 +- .../handlebars-template-resolver.ts | 4 +- .../errors/infrastructure-repository-error.ts | 1 + .../express/express-controller.ts | 12 +- .../infrastructure/express/express-guards.ts | 4 +- .../middlewares/authenticate-request.ts | 16 +- .../operation-context.middleware.ts | 8 +- modules/core/src/api/infrastructure/index.ts | 2 +- .../persistence/sequelize/index.ts | 6 +- .../mappers/sequelize-domain-mapper.ts | 4 +- .../sequelize/sequelize-error-translator.ts | 4 +- .../sequelize-transaction-context.test.ts | 1 + .../sequelize-transaction-manager.ts | 2 +- .../core/src/api/modules/module-runtime.ts | 59 +- .../payments/factuges-payment-catalog.json | 2 +- .../catalogs/taxes/spain-tax-catalog.json | 4 +- modules/core/src/common/dto/critera.dto.ts | 4 +- modules/core/src/common/dto/website.dto.ts | 12 +- .../apply-validation-error-collection.ts | 4 +- .../src/common/helpers/money-dto-helper.ts | 4 +- .../common/helpers/percentage-dto-helper.ts | 8 +- .../src/common/helpers/quantity-dto-helper.ts | 4 +- .../__tests__/page-error-state.test.tsx | 1 + .../core/src/web/components/error-state.tsx | 1 + .../page-keyboard-shortcuts-button.tsx | 4 +- .../src/web/components/form/form-debug.tsx | 9 +- .../components/form/simple-search-input.tsx | 89 ++- .../src/web/components/page-error-state.tsx | 6 +- .../unsaved-changes-cancel-action-button.tsx | 4 +- .../use-unsaved-changes-notifier.tsx | 28 +- .../__tests__/normalize-axios-error.test.ts | 3 +- .../axios/create-axios-instance.ts | 2 +- .../axios/normalize-axios-error.ts | 13 +- .../lib/data-source/build-text-filters.tsx | 4 +- .../errors/get-user-error-message.ts | 3 - .../errors/is-api-problem-details.ts | 10 +- .../build-top-level-form-dirty-patch.ts | 8 +- .../core/src/web/lib/helpers/form-utils.ts | 12 +- .../src/web/lib/helpers/http-url-utils.ts | 8 +- modules/core/src/web/manifest.ts | 2 +- modules/customer-invoices/package.json | 2 +- .../get-company-verifactu-profile.use-case.ts | 5 +- .../errors/invoice-series-inactive.error.ts | 5 +- .../errors/invoice-series-not-found.error.ts | 5 +- .../audit/issued-invoice-audit.test.ts | 4 +- .../models/proforma-issue-read.model.ts | 3 +- ...-to-issued-invoice-props-converter.test.ts | 19 +- ...olve-proforma-issue-fiscal-context.test.ts | 12 +- .../resolve-proforma-issue-fiscal-context.ts | 5 +- ...ued-invoice-items-full-snapshot-builder.ts | 22 +- .../issued-invoice-report-snapshot-builder.ts | 12 +- .../proformas/di/proforma-creator.di.ts | 2 +- .../proformas/di/proforma-finder.di.ts | 2 +- .../proformas/di/proforma-issuer.di.ts | 2 +- .../di/proforma-status-changer.di.ts | 2 +- .../company-report-profile-not-found.error.ts | 3 +- .../api/application/proformas/errors/index.ts | 2 +- ...roforma-issue-read-model.assembler.test.ts | 19 +- .../services/catalog-resolver/index.ts | 2 +- .../proforma-tax-config-resolver.test.ts | 30 +- .../proforma-tax-config-resolver.ts | 51 +- .../proforma-tax-resolver.test.ts | 3 +- .../catalog-resolver/proforma-tax-resolver.ts | 8 +- .../services/proforma-creator.test.ts | 20 +- .../proformas/services/proforma-creator.ts | 6 +- .../services/proforma-duplicator.test.ts | 12 +- .../proformas/services/proforma-duplicator.ts | 102 ++- ...roforma-issuance-readiness-checker.test.ts | 16 +- .../proforma-issuance-readiness-checker.ts | 1 + .../proformas/services/proforma-issuer.ts | 2 +- .../services/proforma-status-changer.ts | 1 + .../proforma-tax-availability-checker.ts | 8 +- .../proforma-items-full-snapshot-builder.ts | 17 +- ...rma-issuance-readiness.snapshot-builder.ts | 9 +- .../proforma-report-snapshot-builder.ts | 12 +- .../archive-proforma-by-id.use-case.ts | 6 +- .../duplicate-proforma-by-id.use-case.ts | 16 +- .../application/proformas/use-cases/index.ts | 2 +- .../use-cases/issue-proforma.use-case.test.ts | 4 +- .../use-cases/issue-proforma.use-case.ts | 2 +- .../unarchive-proforma-by-id.use-case.ts | 6 +- .../audit/recurring-proforma-audit.ts | 6 +- .../application/recurring-proformas/index.ts | 2 +- .../recurring-proformas/repositories/index.ts | 2 +- ...recurring-proforma-repository.interface.ts | 5 +- ...recurring-proforma-occurrence-processor.ts | 1 + .../recurring-proformas/services/index.ts | 8 +- ...g-proforma-activation-readiness-checker.ts | 31 +- .../recurring-proforma-capabilities.test.ts | 2 +- .../recurring-proforma-capabilities.ts | 32 +- ...ing-proforma-from-proforma-creator.test.ts | 6 +- ...ecurring-proforma-from-proforma-creator.ts | 54 +- .../recurring-proforma-materializer.test.ts | 16 +- .../recurring-proforma-materializer.ts | 43 +- ...ring-proforma-occurrence-processor.test.ts | 316 ++++++-- ...recurring-proforma-occurrence-processor.ts | 358 +++++++-- .../recurring-proforma-services.test.ts | 61 +- .../services/recurring-proforma-services.ts | 200 ++++- .../services/recurring-proforma-updater.ts | 178 ++++- .../aggregates/issued-invoice.aggregate.ts | 5 +- .../issued-invoice-item.entity.ts | 8 +- .../issued-invoice-items.collection.ts | 3 +- .../issued-invoice-tax.entity.ts | 12 +- ...issued-invoice-fiscal-classification.vo.ts | 9 +- .../aggregates/proforma.aggregate.ts | 12 +- .../src/api/domain/proformas/errors/index.ts | 4 +- .../proforma-cannot-be-archived-error.ts | 5 +- .../policies/proforma-archive-policy.ts | 2 +- .../services/proforma-compare-tax-totals.ts | 44 +- .../services/proforma-compute-tax-groups.ts | 8 +- .../proforma-taxes-calculator.test.ts | 2 +- .../api/domain/recurring-proformas/index.ts | 2 +- .../recurring-proforma-item.test.ts | 26 +- .../recurring-proforma-occurrence.test.ts | 4 +- .../recurring-proforma-occurrence.ts | 29 +- .../recurring-proforma.test.ts | 35 +- .../recurring-proformas/recurring-proforma.ts | 8 +- modules/customer-invoices/src/api/index.ts | 2 +- .../common/persistence/sequelize/index.ts | 6 +- .../models/company-verifactu-profile.model.ts | 5 +- .../persistence/sequelize/models/index.ts | 14 +- .../models/recurring-proforma-item.model.ts | 22 +- .../models/recurring-proforma-models.test.ts | 5 +- .../recurring-proforma-occurrence.model.ts | 48 +- .../models/recurring-proforma.model.ts | 27 +- ...pany-verifactu-profile.controllers.test.ts | 6 +- ...ice-signed-document-cache-pre-processor.ts | 4 +- .../issued-invoices-api-error-mapper.ts | 2 +- ...quelize-issued-invoice-v2-domain.mapper.ts | 28 +- ...ze-issued-invoice-v2-item-domain.mapper.ts | 2 - ...sued-invoice-v2-recipient-domain.mapper.ts | 2 +- ...e-issued-invoice-v2-taxes-domain.mapper.ts | 43 +- ...invoice-v2-verifactu-domain.mapper.test.ts | 25 +- ...sued-invoice-v2-verifactu-domain.mapper.ts | 11 +- ...ued-invoice-v2-recipient-summary.mapper.ts | 24 +- ...quelize-verifactu-record-summary.mapper.ts | 2 +- .../issued-invoice-v2.repository.ts | 24 +- .../api/infrastructure/proformas/di/index.ts | 4 +- .../di/proforma-persistence-mappers-v2.di.ts | 5 +- .../proformas/di/proformas.di.test.ts | 28 +- .../express/proformas-api-error-mapper.ts | 20 +- .../sequelize-proforma-v2-domain.mapper.ts | 4 +- ...ize-proforma-v2-recipient-domain.mapper.ts | 2 +- ...quelize-proforma-v2-taxes-domain.mapper.ts | 5 +- .../sequelize/mappers/summary/index.ts | 2 +- ...ze-proforma-v2-recipient-summary.mapper.ts | 16 +- .../proforma-quick-search.test.ts | 7 +- .../repositories/proforma-quick-search.ts | 9 +- .../repositories/proforma-v2.repository.ts | 8 +- ...ze-proforma-v2-number-generator.service.ts | 36 +- ...-proforma-from-proforma.controller.test.ts | 54 +- ...rring-proforma-from-proforma.controller.ts | 12 +- ...eate-recurring-proforma.controller.test.ts | 12 +- .../express/controllers/index.ts | 2 +- ...ng-proforma-transitions.controller.test.ts | 358 +++++++-- .../recurring-proformas-read.controllers.ts | 50 +- ...date-recurring-proforma.controller.test.ts | 31 +- .../recurring-proformas/index.ts | 2 +- .../recurring-proforma-domain.mapper.test.ts | 51 +- .../recurring-proforma-domain.mapper.ts | 34 +- ...-proforma-occurrence-domain.mapper.test.ts | 5 +- ...rring-proforma-occurrence-domain.mapper.ts | 4 +- ...ing-proforma-occurrence.repository.test.ts | 39 +- ...ecurring-proforma-occurrence.repository.ts | 16 +- ...sequelize-recurring-proforma.repository.ts | 32 +- .../renderers/issued-invoice.renderer.json.ts | 2 +- .../proformas/create-proforma.request.dto.ts | 6 +- .../proformas/list-proformas.request.dto.ts | 2 +- .../dto/request/recurring-proformas/index.ts | 2 +- .../company-verifactu-profile.response.dto.ts | 5 +- .../src/common/locales/en.json | 2 +- .../src/common/locales/es.json | 2 +- ...e-issued-invoice-detail-page-controller.ts | 2 +- ...ssued-invoice-detail-view-models.entity.ts | 3 +- .../issued-invoice-detail-header-actions.tsx | 6 +- .../issued-invoice-detail-inspector.tsx | 5 +- .../blocks/issued-invoice-general-panel.tsx | 6 +- .../pages/issued-invoice-detail-page.test.tsx | 6 +- .../issued-invoice-document-section.tsx | 5 +- .../issued-invoice-fiscal-section.tsx | 4 +- .../sections/issued-invoice-items-section.tsx | 4 +- .../issued-invoice-recipient-section.tsx | 4 +- .../issued-invoice-summary-section.tsx | 4 +- .../issued-invoice-totals-section.tsx | 4 +- .../issued-invoice-verifactu-section.tsx | 10 +- ...-issued-invoice-detail-view-models.test.ts | 1 + ...build-issued-invoice-detail-view-models.ts | 20 +- .../use-download-invoice-pdf.controller.ts | 4 +- .../hooks/use-download-invoice-pdf-query.ts | 4 +- .../use-issued-invoice-list.controller.ts | 162 ++-- .../use-issued-invoices-grid-columns.test.tsx | 1 + ...invoice-list-page-error-view-model.test.ts | 6 +- ...sued-invoice-list-page-error-view-model.ts | 8 +- .../get-issued-invoice-by-id.adapter.test.ts | 1 + .../issued-invoices/shared/adapters/index.ts | 2 +- .../list-issued-invoice.adapter.test.ts | 1 + .../adapters/list-issued-invoice.adapter.ts | 2 +- .../web/issued-invoices/shared/api/index.ts | 2 +- .../issued-invoices/shared/entities/index.ts | 4 +- .../web/issued-invoices/shared/hooks/index.ts | 2 +- .../proformas/change-status/entities/index.ts | 2 +- .../change-status/ui/change-status-dialog.tsx | 2 +- .../ui/components/timeline-connector.tsx | 8 +- .../proformas/change-status/utils/index.ts | 4 +- .../use-create-proforma-controller.ts | 18 +- .../use-delete-proforma-dialog-controller.ts | 9 +- .../ui/components/delete-proforma-dialog.tsx | 8 +- .../web/proformas/detail/controllers/index.ts | 2 +- .../use-proforma-detail-page-controller.ts | 1 + .../proforma-detail-view-models.entity.ts | 1 + .../web/proformas/detail/ui/blocks/index.ts | 4 +- .../blocks/proforma-detail-header-actions.tsx | 2 +- .../proformas/detail/ui/components/index.ts | 2 +- .../ui/components/proforma-detail-field.tsx | 6 +- .../proforma-detail-header-meta.tsx | 5 +- .../sections/proforma-commercial-section.tsx | 4 +- .../ui/sections/proforma-content-section.tsx | 10 +- .../ui/sections/proforma-customer-section.tsx | 5 +- .../build-proforma-detail-view-models.ts | 57 +- .../use-download-proforma-pdf.controller.ts | 4 +- .../hooks/use-download-proforma-pdf-query.ts | 6 +- .../src/web/proformas/index.ts | 4 +- .../issue-proforma/entities/index.ts | 2 +- .../proformas/issue-proforma/utils/index.ts | 2 +- .../use-list-proformas-page.controller.ts | 10 +- .../use-list-proformas.controller.ts | 12 +- .../proforma-summary-panel.test.tsx | 2 + .../proforma-summary-panel.tsx | 2 +- .../proformas/list/ui/pages/orders-table.tsx | 4 +- .../scoped-list-proformas-page-view.test.tsx | 1 + ...ild-proforma-list-page-error-view-model.ts | 4 +- .../shared/api/archive-proforma-by-id.api.ts | 4 +- .../api/change-proforma-status-by-id.api.ts | 6 +- .../shared/api/create-proforma.api.ts | 4 +- .../shared/api/delete-proforma-by-id.api.ts | 6 +- .../api/duplicate-proforma-by-id.api.ts | 4 +- .../shared/api/get-proforma-by-id.api.ts | 4 +- .../shared/api/issue-proforma-by-id.api.ts | 4 +- .../api/list-proformas-by-criteria.api.ts | 6 +- .../shared/api/report-proforma-by-id.api.ts | 4 +- .../api/unarchive-proforma-by-id.api.ts | 4 +- .../shared/api/update-proforma-by-id.api.ts | 4 +- .../web/proformas/shared/entities/index.ts | 2 +- .../src/web/proformas/shared/hooks/index.ts | 2 +- .../shared/hooks/proforma-cache-strategy.ts | 16 +- .../hooks/use-proforma-delete-mutation.ts | 40 +- .../shared/hooks/use-proforma-get-query.ts | 4 +- .../shared/hooks/use-proformas-list-query.ts | 2 +- .../ensure-current-value-in-select-items.ts | 8 +- .../utils/proforma-fiscal-options.utils.ts | 20 +- ...roforma-to-proforma-update-form.adapter.ts | 4 +- .../use-update-proforma-controller.ts | 8 +- .../use-update-proforma-items-controller.ts | 24 +- .../use-update-proforma-tax-controller.ts | 4 +- .../update/ui/blocks/editor-sidebar.tsx | 8 +- .../update/ui/blocks/proforma-line-editor.tsx | 20 +- .../ui/blocks/proforma-update-header.test.tsx | 2 + .../selected-recipient/customer-card.tsx | 8 +- ...proforma-update-customer-change-dialog.tsx | 10 +- ...forma-update-page-error-view-model.test.ts | 9 +- ...d-proforma-update-page-error-view-model.ts | 4 +- ...build-update-proforma-by-id-params.test.ts | 1 + .../src/web/proformas/update/utils/index.ts | 2 +- ...lve-proforma-update-page-feedback-state.ts | 2 +- .../create/recurring-proforma-create-page.tsx | 4 +- .../recurring-proforma-create-route.test.tsx | 4 +- .../recurring-proforma-create-utils.test.ts | 12 +- .../detail/recurring-proforma-detail-utils.ts | 12 +- ...proforma-occurrences-section-utils.test.ts | 47 +- ...ring-proforma-occurrences-section-utils.ts | 8 +- .../recurring-proforma-list-utils.test.ts | 2 +- .../list/recurring-proforma-list-utils.ts | 16 +- .../list/ui/components/index.ts | 2 +- .../list/ui/pages/index.ts | 2 +- ...ist-recurring-proformas-page-view.test.tsx | 1 + .../api/get-recurring-proforma.api.test.ts | 3 +- .../shared/api/get-recurring-proforma.api.ts | 8 +- ...recurring-proforma-occurrences.api.test.ts | 6 +- .../api/list-recurring-proformas.api.test.ts | 10 +- .../recurring-proformas/shared/hooks/index.ts | 14 +- .../hooks/recurring-proforma-query-keys.ts | 8 +- ...create-recurring-proforma-from-proforma.ts | 4 +- .../hooks/use-create-recurring-proforma.ts | 2 +- .../shared/hooks/use-recurring-proformas.ts | 2 +- ...se-company-verifactu-profile.controller.ts | 11 +- ...pany-verifactu-settings-page-controller.ts | 7 +- .../company-verifactu-profile-form.schema.ts | 5 +- .../update-company-verifactu-profile.api.ts | 15 +- ...mpany-verifactu-profile-update-mutation.ts | 5 +- .../build-company-verifactu-profile-form.ts | 2 +- ...ctu-settings-page-error-view-model.test.ts | 6 +- ...erifactu-settings-page-error-view-model.ts | 4 +- ...update-company-verifactu-profile-params.ts | 2 +- .../src/web/settings/verifactu/utils/index.ts | 2 +- ...-verifactu-settings-page-feedback-state.ts | 2 +- modules/customers/package.json | 2 +- .../application/di/customer-use-cases.di.ts | 2 +- .../mappers/update-customer-input.mapper.ts | 2 +- .../customer-default-payment-term-resolver.ts | 8 +- .../use-cases/create-customer.use-case.ts | 17 +- .../update-customer.use-case.test.ts | 9 +- .../use-cases/update-customer.use-case.ts | 5 +- .../domain/aggregates/customer.aggregate.ts | 2 +- .../src/api/domain/value-objects/index.ts | 2 +- .../sequelize-customer-domain.mapper.ts | 4 +- .../sequelize-customer-summary.mapper.ts | 6 +- .../repositories/customer.repository.ts | 12 +- .../use-select-customer-dialog-controller.ts | 8 +- .../customer-selection/utils/index.ts | 2 +- .../features/get-customer/entities/index.ts | 2 +- .../quick-create-customer/entities/index.ts | 4 +- .../quick-create-customer-dialog.test.tsx | 2 + ...te-customer-params-from-quick-form.test.ts | 24 +- ...-create-customer-params-from-quick-form.ts | 3 +- ...ld-quick-customer-create-default-values.ts | 2 +- .../quick-create-customer/utils/index.ts | 4 +- .../use-customer-create-fiscal-controller.ts | 9 +- .../entities/customer-create-data.entity.ts | 2 +- .../ui/blocks/customer-create-header.test.tsx | 1 + .../ui/editor/customer-create-editor.test.tsx | 2 + .../utils/build-create-customer-params.ts | 52 +- .../utils/build-customer-create-data.ts | 8 +- .../use-list-customers-page.controller.ts | 2 +- .../use-list-customers.controller.ts | 12 +- .../customer-proformas-section.tsx | 2 +- .../customer-summary-panel.test.tsx | 2 + .../use-customer-grid-columns.test.tsx | 7 +- .../pages/list-customers-page-view.test.tsx | 1 + ...ustomer-list-page-error-view-model.test.ts | 9 +- ...ild-customer-list-page-error-view-model.ts | 4 +- .../utils/error-alert-deduplication.test.ts | 10 +- .../adapters/get-customer-by-id.adapter.ts | 2 +- .../shared/adapters/list-customers.adapter.ts | 2 +- .../src/web/shared/api/create-customer.api.ts | 8 +- .../shared/api/delete-customer-by-id.api.ts | 6 +- .../web/shared/api/get-customer-by-id.api.ts | 4 +- .../shared/api/update-customer-by-id.api.ts | 4 +- .../shared/hooks/customer-cache-strategy.ts | 16 +- .../shared/hooks/use-customer-get-query.ts | 4 +- .../use-customer-update-fiscal-controller.ts | 8 +- .../use-customer-update-page.controller.ts | 3 +- .../use-customer-update.controller.ts | 8 +- .../src/web/update/entities/index.ts | 2 +- .../ui/editor/customer-edit-form.test.tsx | 2 + ...tomer-update-page-error-view-model.test.ts | 9 +- ...d-customer-update-page-error-view-model.ts | 4 +- .../build-update-customer-by-id-params.ts | 56 +- .../customers/src/web/update/utils/index.ts | 2 +- ...lve-customer-update-page-feedback-state.ts | 2 +- ...ustomer-view-page-error-view-model.test.ts | 12 +- ...ild-customer-view-page-error-view-model.ts | 4 +- ...solve-customer-view-page-feedback-state.ts | 2 +- modules/document-series/package.json | 2 +- .../create-document-series-command.mapper.ts | 18 +- .../update-document-series-command.mapper.ts | 41 +- .../document-series-repository.interface.ts | 8 +- .../document-series-number-assigner.ts | 13 +- .../assign-next-document-number.use-case.ts | 4 +- .../create-document-series.use-case.ts | 6 +- .../disable-document-series.use-case.ts | 2 +- .../get-document-series-by-id.use-case.ts | 4 +- .../list-document-series.use-case.ts | 4 +- .../update-document-series.use-case.ts | 6 +- .../value-objects/document-series-code.vo.ts | 6 +- .../assign-next-document-number.controller.ts | 4 +- .../document-series-api-error-mapper.ts | 2 +- .../src/api/infrastructure/express/index.ts | 2 +- ...sequelize-document-series-domain.mapper.ts | 19 +- .../create-document-series.request.dto.ts | 11 +- .../update-document-series.request.dto.ts | 11 +- modules/factuges/package.json | 2 +- .../factuges-payment-method-reference.json | 2 +- .../factuges-proforma-link.aggregate.ts | 2 +- .../infraestructure/express/mappers/index.ts | 1 - modules/identity/package.json | 2 +- .../contracts/account-repository.interface.ts | 5 +- ...hange-current-account-password.use-case.ts | 11 +- .../use-cases/get-current-account.use-case.ts | 8 +- .../update-current-account.use-case.ts | 16 +- .../authentication/contracts/index.ts | 2 +- .../use-cases/get-current-session.use-case.ts | 2 +- .../initial-company-admin-assigner.test.ts | 39 +- .../initial-company-admin-assigner.ts | 65 +- .../effective-permissions-resolver.ts | 16 +- .../api/application/roles/contracts/index.ts | 2 +- .../company-admin-role-ensurer.test.ts | 4 +- .../services/company-admin-role-ensurer.ts | 36 +- .../api/domain/accounts/account-status.vo.ts | 12 +- .../api/domain/companies/company-status.vo.ts | 17 +- .../identity-account-api-error-mapper.ts | 27 +- .../express/identity-account.routes.ts | 3 +- .../express/identity-auth-api-error-mapper.ts | 5 +- .../src/api/infrastructure/express/index.ts | 4 +- .../express/middlewares/index.ts | 2 +- .../require-authenticated.middleware.ts | 2 +- .../require-company-context.middleware.ts | 4 +- ...uelize-company-membership-domain.mapper.ts | 28 +- .../sequelize-role-domain.mapper.test.ts | 4 +- .../mappers/sequelize-role-domain.mapper.ts | 24 +- .../sequelize-account.repository.ts | 3 +- ...lize-company-membership.repository.test.ts | 6 +- ...sequelize-company-membership.repository.ts | 24 +- .../sequelize-refresh-token.repository.ts | 2 +- .../repositories/sequelize-role.repository.ts | 24 +- .../security/bcrypt-password-hasher.ts | 7 +- .../src/api/infrastructure/security/index.ts | 2 +- .../jsonwebtoken-access-token-issuer.ts | 2 +- .../jsonwebtoken-access-token-verifier.ts | 2 +- .../change-account-password.response.dto.ts | 4 +- .../update-current-company.response.dto.ts | 4 +- .../update-company-user-roles.request.dto.ts | 4 +- .../delete-company-role-by-id.request.dto.ts | 4 +- .../delete-company-role-by-id.response.dto.ts | 4 +- .../update-company-role-by-id.response.dto.ts | 4 +- modules/identity/src/web/account-routes.tsx | 3 +- .../use-account-page-controller.ts | 2 +- .../adapters/account-profile.adapter.ts | 4 +- ...hange-current-account-password-mutation.ts | 4 +- .../shared/hooks/use-current-account-query.ts | 2 +- .../use-update-current-account-mutation.ts | 4 +- .../utils/build-account-profile-form.ts | 2 +- .../context/identity-auth-session.context.tsx | 739 +++++++++--------- .../src/web/login/controllers/index.ts | 1 - .../identity/src/web/login/entities/index.ts | 1 - .../web/login/entities/login-form.entity.ts | 1 - modules/identity/src/web/login/index.ts | 1 - modules/identity/src/web/login/ui/index.ts | 1 - .../shared/adapters/active-company.adapter.ts | 2 +- .../shared/adapters/auth-session.adapter.ts | 9 +- .../web/shared/api/get-current-session.api.ts | 1 - .../identity/src/web/shared/api/login.api.ts | 6 +- .../identity/src/web/shared/api/logout.api.ts | 6 +- .../src/web/shared/api/refresh-session.api.ts | 1 - .../shared/entities/auth-session.entity.ts | 1 - .../entities/identity-auth-error.entity.ts | 1 - .../identity/src/web/shared/hooks/index.ts | 1 - .../web/shared/hooks/use-login-mutation.ts | 6 +- modules/identity/src/web/shared/index.ts | 1 - .../web/shared/storage/auth-token-storage.ts | 4 +- ...omplete-initial-onboarding-input.mapper.ts | 28 +- ...initial-onboarding-status.use-case.test.ts | 7 +- .../get-initial-onboarding-status.use-case.ts | 8 +- .../initial-onboarding-provisioner.test.ts | 24 +- .../initial-onboarding-provisioner.ts | 28 +- ...mplete-initial-onboarding.use-case.test.ts | 39 +- .../complete-initial-onboarding.use-case.ts | 38 +- .../src/api/application/use-cases/index.ts | 2 +- .../initial-onboarding.aggregate.test.ts | 4 +- .../initial-onboarding.aggregate.ts | 3 +- .../api/infrastructure/di/onboarding.di.ts | 18 +- .../complete-initial-onboarding.controller.ts | 4 +- .../express/onboarding.routes.test.ts | 4 +- ...provision-initial-onboarding.controller.ts | 5 +- ...uelize-initial-onboarding-domain.mapper.ts | 24 +- ...sequelize-initial-onboarding.repository.ts | 16 +- .../src/api/module.composition.test.ts | 10 +- modules/supplier-invoices/package.json | 2 +- ...ize-supplier-invoice-item-domain.mapper.ts | 2 - ...ze-supplier-invoice-taxes-domain.mapper.ts | 2 - modules/suppliers/package.json | 2 +- .../update-supplier-input.mapper.test.ts | 10 +- .../mappers/update-supplier-input.mapper.ts | 5 +- .../src/api/application/repositories/index.ts | 2 +- .../supplier-repository.interface.ts | 1 - .../domain/supplier-snapshot-builder.test.ts | 1 + .../domain/supplier-snapshot-builder.ts | 2 +- .../supplier-summary-snapshot-builder.ts | 2 +- .../use-cases/create-supplier.use-case.ts | 1 + .../use-cases/update-supplier.use-case.ts | 1 + .../src/api/domain/aggregates/index.ts | 2 +- .../src/api/domain/collections/index.ts | 2 +- modules/suppliers/src/api/domain/index.ts | 8 +- .../src/api/domain/value-objects/index.ts | 2 +- .../api/infrastructure/persistence/index.ts | 2 +- .../sequelize-supplier-summary.mapper.ts | 2 +- .../sequelize/repositories/index.ts | 2 +- .../sequelize-supplier.repository.test.ts | 20 +- .../sequelize-supplier.repository.ts | 1 - .../response/list-suppliers.response.dto.ts | 52 +- modules/suppliers/src/common/locales/en.json | 39 +- modules/suppliers/src/common/locales/es.json | 29 +- .../use-list-suppliers.controller.ts | 8 +- .../src/web/shared/api/create-supplier.api.ts | 8 +- .../web/shared/api/get-supplier-by-id.api.ts | 4 +- .../shared/api/update-supplier-by-id.api.ts | 4 +- .../web/shared/forms/supplier-form.test.ts | 2 +- .../src/web/shared/forms/supplier-form.ts | 8 +- .../shared/hooks/supplier-cache-strategy.ts | 8 +- .../shared/hooks/use-supplier-get-query.ts | 4 +- .../use-supplier-update.controller.ts | 8 +- .../src/web/update/supplier-update-page.tsx | 4 +- .../src/web/view/supplier-detail-page.tsx | 4 +- package.json | 12 +- packages/i18n/package.json | 52 +- packages/rdx-audit/package.json | 2 +- packages/rdx-audit/src/audit.test.ts | 19 +- packages/rdx-audit/src/audit.ts | 12 +- .../rdx-audit/src/internal/audit-diff.test.ts | 25 +- packages/rdx-audit/src/internal/audit-diff.ts | 24 +- .../src/internal/audit-input-validator.ts | 22 +- .../internal/audit-state-validator.test.ts | 8 +- .../src/internal/audit-state-validator.ts | 43 +- packages/rdx-criteria/package.json | 2 +- .../src/build-boolean-full-text-query.ts | 4 +- packages/rdx-criteria/src/codelytv/Order.ts | 6 +- .../src/criteria-to-sequelize-converter.ts | 52 +- packages/rdx-criteria/src/utils.ts | 12 +- packages/rdx-ddd/package.json | 2 +- packages/rdx-ddd/src/domain-entity.ts | 4 +- .../rdx-ddd/src/events/domain-event-handle.ts | 2 +- packages/rdx-ddd/src/events/domain-event.ts | 2 +- packages/rdx-ddd/src/helpers/normalizers.ts | 4 +- packages/rdx-ddd/src/specification.ts | 8 +- .../__tests__/phone-number.test.ts | 3 +- packages/rdx-ddd/src/value-objects/city.ts | 2 + .../rdx-ddd/src/value-objects/phone-number.ts | 2 + .../src/value-objects/postal-address.ts | 4 +- .../rdx-ddd/src/value-objects/postal-code.ts | 2 + .../rdx-ddd/src/value-objects/province.ts | 2 + packages/rdx-ddd/src/value-objects/slug.ts | 2 + packages/rdx-ddd/src/value-objects/street.ts | 2 + packages/rdx-logger/package.json | 2 +- .../src/strategies/console-logger.ts | 4 +- packages/rdx-ui/package.json | 6 +- .../src/components/buttons/button-group.tsx | 2 +- .../context-panel/rdx-context-panel.test.tsx | 1 + .../context-panel/rdx-context-panel.tsx | 17 +- .../data-table-pagination-button.tsx | 4 +- .../data-table-pagination.tsx | 4 +- .../datatable/data-table-toolbar.tsx | 20 +- .../datatable/data-table-view-options.tsx | 4 +- .../src/components/datatable/data-table.tsx | 150 ++-- .../form-secondary-actions-menu.tsx | 4 +- packages/rdx-ui/src/components/form/index.ts | 2 +- .../components/form/multi-select-field.tsx | 8 +- .../line-description-field.tsx | 6 +- .../src/components/form/text-area-field.tsx | 4 +- .../components/form/text-field-presets.tsx | 2 - .../src/components/full-screen-modal.tsx | 17 +- .../rdx-ui/src/components/initials-avatar.tsx | 4 +- .../loading-overlay/loading-spin-icon.tsx | 14 +- .../lookup-dialog/lookup-dialog.tsx | 36 +- .../rdx-ui/src/components/multi-select.tsx | 14 +- .../components/right-panel/right-panel.tsx | 7 +- .../row-actions/rdx-row-actions.test.tsx | 9 +- .../row-actions/rdx-row-actions.tsx | 7 +- .../src/components/tailwind-indicator.tsx | 4 +- .../forms/get-first-form-error-message.ts | 2 +- .../forms/prevent-enter-key-submit-form.ts | 12 +- packages/rdx-ui/src/hooks/use-device-info.ts | 20 +- .../rdx-ui/src/hooks/use-row-selection.ts | 8 +- packages/rdx-ui/src/locales/es.json | 2 +- packages/rdx-utils/package.json | 2 +- packages/rdx-utils/src/helpers/date-helper.ts | 20 +- .../rdx-utils/src/helpers/number-helper.ts | 8 +- packages/rdx-utils/src/helpers/patch-field.ts | 25 +- packages/rdx-utils/src/helpers/result.ts | 2 +- packages/shadcn-ui/components.json | 2 +- packages/shadcn-ui/package.json | 7 +- .../shadcn-ui/src/components/accordion.tsx | 47 +- .../shadcn-ui/src/components/alert-dialog.tsx | 94 +-- packages/shadcn-ui/src/components/alert.tsx | 32 +- .../shadcn-ui/src/components/aspect-ratio.tsx | 8 +- packages/shadcn-ui/src/components/avatar.tsx | 57 +- .../shadcn-ui/src/components/breadcrumb.tsx | 83 +- .../shadcn-ui/src/components/button-group.tsx | 42 +- packages/shadcn-ui/src/components/button.tsx | 15 +- .../shadcn-ui/src/components/calendar.tsx | 149 ++-- packages/shadcn-ui/src/components/card.tsx | 48 +- .../shadcn-ui/src/components/carousel.tsx | 174 ++--- packages/shadcn-ui/src/components/chart.tsx | 199 ++--- .../shadcn-ui/src/components/checkbox.tsx | 20 +- .../shadcn-ui/src/components/collapsible.tsx | 14 +- .../shadcn-ui/src/components/combobox.tsx | 161 ++-- packages/shadcn-ui/src/components/command.tsx | 92 +-- .../shadcn-ui/src/components/context-menu.tsx | 153 ++-- packages/shadcn-ui/src/components/dialog.tsx | 81 +- .../shadcn-ui/src/components/direction.tsx | 2 +- packages/shadcn-ui/src/components/drawer.tsx | 76 +- packages/shadcn-ui/src/components/empty.tsx | 43 +- packages/shadcn-ui/src/components/field.tsx | 131 ++-- .../shadcn-ui/src/components/hover-card.tsx | 26 +- .../shadcn-ui/src/components/input-group.tsx | 103 ++- .../shadcn-ui/src/components/input-otp.tsx | 42 +- packages/shadcn-ui/src/components/input.tsx | 15 +- packages/shadcn-ui/src/components/item.tsx | 84 +- packages/shadcn-ui/src/components/kbd.tsx | 12 +- packages/shadcn-ui/src/components/label.tsx | 13 +- packages/shadcn-ui/src/components/menubar.tsx | 150 ++-- .../src/components/native-select.tsx | 51 +- .../src/components/navigation-menu.tsx | 70 +- .../shadcn-ui/src/components/pagination.tsx | 73 +- packages/shadcn-ui/src/components/popover.tsx | 48 +- .../shadcn-ui/src/components/progress.tsx | 48 +- .../shadcn-ui/src/components/radio-group.tsx | 19 +- .../shadcn-ui/src/components/resizable.tsx | 33 +- .../shadcn-ui/src/components/scroll-area.tsx | 30 +- packages/shadcn-ui/src/components/select.tsx | 94 +-- .../shadcn-ui/src/components/separator.tsx | 19 +- packages/shadcn-ui/src/components/sheet.tsx | 77 +- packages/shadcn-ui/src/components/sidebar.tsx | 356 ++++----- .../shadcn-ui/src/components/skeleton.tsx | 8 +- packages/shadcn-ui/src/components/slider.tsx | 21 +- packages/shadcn-ui/src/components/sonner.tsx | 44 +- packages/shadcn-ui/src/components/spinner.tsx | 15 +- packages/shadcn-ui/src/components/switch.tsx | 17 +- packages/shadcn-ui/src/components/table.tsx | 70 +- packages/shadcn-ui/src/components/tabs.tsx | 38 +- .../shadcn-ui/src/components/textarea.tsx | 11 +- packages/shadcn-ui/src/components/toggle.tsx | 17 +- packages/shadcn-ui/src/components/tooltip.tsx | 35 +- packages/shadcn-ui/src/hooks/use-mobile.ts | 22 +- packages/shadcn-ui/src/hooks/use-mobile.tsx | 22 +- packages/typescript-config/package.json | 2 +- packages/typescript-config/root.json | 50 +- turbo.json | 33 +- 816 files changed, 7998 insertions(+), 5405 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index e932574c..44ed9692 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -83,4 +83,4 @@ "engines": { "node": ">=22" } -} \ No newline at end of file +} diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 27ceb51e..093c8c8b 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -1,10 +1,9 @@ +import { type IOperationContextStore, createOperationContextMiddleware } from "@erp/core/api"; import cors, { type CorsOptions } from "cors"; import express, { type Application } from "express"; import helmet from "helmet"; import responseTime from "response-time"; -import { createOperationContextMiddleware, type IOperationContextStore } from "@erp/core/api"; - import type { ConfigType } from "./config"; // ❗️ No cargamos dotenv aquí. Debe hacerse en el entrypoint o en ./config. diff --git a/apps/server/src/config/company-certificates.json b/apps/server/src/config/company-certificates.json index b5c0a975..c740b1e6 100644 --- a/apps/server/src/config/company-certificates.json +++ b/apps/server/src/config/company-certificates.json @@ -1,12 +1,12 @@ { - "acme": { - "certificateId": "acme-prod-cert-v1", - "certificateSecretName": "acme_cert", - "certificatePasswordSecretName": "acme_cert_pwd" - }, - "rodax": { - "certificateId": "no se que poner aqui", - "certificateSecretName": "certificate_secret_name", - "certificatePasswordSecretName": "certificate_password_secret_name" - } -} \ No newline at end of file + "acme": { + "certificateId": "acme-prod-cert-v1", + "certificateSecretName": "acme_cert", + "certificatePasswordSecretName": "acme_cert_pwd" + }, + "rodax": { + "certificateId": "no se que poner aqui", + "certificateSecretName": "certificate_secret_name", + "certificatePasswordSecretName": "certificate_password_secret_name" + } +} diff --git a/apps/server/src/config/log-config.ts b/apps/server/src/config/log-config.ts index 6fe8fc66..db28d1f0 100644 --- a/apps/server/src/config/log-config.ts +++ b/apps/server/src/config/log-config.ts @@ -3,7 +3,9 @@ import type { ILogger } from "@repo/rdx-logger"; import type { ConfigType } from "./config"; const redact = (value: string): string => { - if (value.length <= 4) return "***"; + if (value.length <= 4) { + return "***"; + } return `${value.slice(0, 2)}***${value.slice(-2)}`; }; diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index d297a84b..3b0106db 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -62,7 +62,9 @@ const serverStop = (server: http.Server) => { const forceTimeout = 30000; // Devolvemos siempre la misma promesa para evitar dobles cierres - if (shutdownPromise) return shutdownPromise; + if (shutdownPromise) { + return shutdownPromise; + } shutdownPromise = new Promise((resolve, reject) => { logger.warn("⚡️ Shutting down server"); @@ -165,7 +167,9 @@ const handleSignal = (signal: NodeJS.Signals) => async () => { logger.warn(`Received ${signal}. Starting graceful shutdown...`); try { await serverStop(server); - if (database) await closeDatabase(database); + if (database) { + await closeDatabase(database); + } process.exit(0); } catch (err) { logger.error("Error during shutdown, forcing exit(1)", { err, label: "handleSignal" }); @@ -202,7 +206,9 @@ process.on("unhandledRejection", async (reason: any, promise: Promise) => { isReady = false; try { await serverStop(server); - if (database) await closeDatabase(database); + if (database) { + await closeDatabase(database); + } } finally { process.exit(1); } @@ -219,7 +225,9 @@ process.on("uncaughtException", async (error: Error) => { isReady = false; try { await serverStop(server); - if (database) await closeDatabase(database); + if (database) { + await closeDatabase(database); + } } finally { process.exit(1); } diff --git a/apps/server/src/infrastructure/audit/audit-reader.ts b/apps/server/src/infrastructure/audit/audit-reader.ts index 49f630fd..99e07a05 100644 --- a/apps/server/src/infrastructure/audit/audit-reader.ts +++ b/apps/server/src/infrastructure/audit/audit-reader.ts @@ -183,12 +183,16 @@ export class SequelizeAuditReader implements IAuditReader { }; } private accessWhere(access: AuditReadAccess): Record { - if (access.mode === "unrestricted") return {}; + if (access.mode === "unrestricted") { + return {}; + } const alternatives: Record[] = access.scopes.map((scope) => ({ scope_type: scope.type, scope_id: scope.id, })); - if (access.includeUnscoped) alternatives.push({ scope_type: null, scope_id: null }); + if (access.includeUnscoped) { + alternatives.push({ scope_type: null, scope_id: null }); + } return alternatives.length ? { [Op.or]: alternatives } : { id: { [Op.eq]: null } }; } private quickSearch(value: string): Record[] { diff --git a/apps/server/src/lib/modules/module-loader.test.ts b/apps/server/src/lib/modules/module-loader.test.ts index c1c01fe2..ca8f1dd0 100644 --- a/apps/server/src/lib/modules/module-loader.test.ts +++ b/apps/server/src/lib/modules/module-loader.test.ts @@ -118,13 +118,10 @@ test("module-loader falla cuando una dependencia usada no está declarada", asyn }, }); - await assert.rejects( - async () => { - await initModules(buildModuleParams()); - await startModules(buildModuleParams()); - }, - /without declaring dependency on "catalogs"/i - ); + await assert.rejects(async () => { + await initModules(buildModuleParams()); + await startModules(buildModuleParams()); + }, /without declaring dependency on "catalogs"/i); }); test("module-loader sigue detectando ciclos reales entre módulos", async (t) => { @@ -165,11 +162,8 @@ test("module-loader rechaza la autoreferencia explícita por nombre de módulo", }, }); - await assert.rejects( - async () => { - await initModules(buildModuleParams()); - await startModules(buildModuleParams()); - }, - /without declaring dependency on "companies"/i - ); + await assert.rejects(async () => { + await initModules(buildModuleParams()); + await startModules(buildModuleParams()); + }, /without declaring dependency on "companies"/i); }); diff --git a/apps/server/src/testing/http/audit-http.integration.test.ts b/apps/server/src/testing/http/audit-http.integration.test.ts index 083c9039..221d6cfa 100644 --- a/apps/server/src/testing/http/audit-http.integration.test.ts +++ b/apps/server/src/testing/http/audit-http.integration.test.ts @@ -137,6 +137,7 @@ test("Audit HTTP aplica tenant A/B y valida el DTO estricto", async (t) => { "order=invalid", "from=invalid", "to=invalid", - ]) + ]) { assert.equal((await runtime.request(`/api/v1/audit?${query}`, { headers })).status, 422); + } }); diff --git a/apps/server/src/testing/http/audit-runtime.integration.test.ts b/apps/server/src/testing/http/audit-runtime.integration.test.ts index 30a7888d..5d297a41 100644 --- a/apps/server/src/testing/http/audit-runtime.integration.test.ts +++ b/apps/server/src/testing/http/audit-runtime.integration.test.ts @@ -109,7 +109,9 @@ const auditProbeModule: IModuleServer = { before: { name: "Before" }, after: { name: "After" }, }); - if (result.isFailure) return result; + if (result.isFailure) { + return result; + } return Result.fail(new Error("test rollback")); }); } catch { diff --git a/apps/server/src/testing/http/proforma-creator.integration.test.ts b/apps/server/src/testing/http/proforma-creator.integration.test.ts index 01b5f949..a00ab5b5 100644 --- a/apps/server/src/testing/http/proforma-creator.integration.test.ts +++ b/apps/server/src/testing/http/proforma-creator.integration.test.ts @@ -678,15 +678,17 @@ for (const scenario of [ await assert.rejects( () => transactionManager.complete(async (transaction) => { - if (scenario.run === "statusChanger") + if (scenario.run === "statusChanger") { return statusChanger.changeStatus({ companyId, id: proformaId, newStatus: "sent", transaction, }); - if (scenario.run === "deleter") + } + if (scenario.run === "deleter") { return deleter.delete({ companyId, id: proformaId, transaction }); + } return scenario.name === "unarchive" ? archiver.unarchive({ companyId, id: proformaId, transaction }) : archiver.archive({ companyId, id: proformaId, transaction }); @@ -696,9 +698,15 @@ for (const scenario of [ assert.equal(audit.recordMutationCalls, 1); const persisted = await ProformaModel.findByPk(id); assert.ok(persisted); - if (scenario.name === "status") assert.equal(persisted.status, "draft"); - if (scenario.name === "archive") assert.equal(persisted.archived_at, null); - if (scenario.name === "unarchive") assert.notEqual(persisted.archived_at, null); + if (scenario.name === "status") { + assert.equal(persisted.status, "draft"); + } + if (scenario.name === "archive") { + assert.equal(persisted.archived_at, null); + } + if (scenario.name === "unarchive") { + assert.notEqual(persisted.archived_at, null); + } assert.equal( await AuditEventModel.count({ where: { action: scenario.action, target_id: id } }), eventsBefore diff --git a/apps/server/tsup.config.ts b/apps/server/tsup.config.ts index e145e53d..e706adf3 100644 --- a/apps/server/tsup.config.ts +++ b/apps/server/tsup.config.ts @@ -1,5 +1,6 @@ import fs from "node:fs"; import path from "node:path"; + import { defineConfig } from "tsup"; /** @@ -55,14 +56,20 @@ export default defineConfig({ if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) { const indexTs = path.join(abs, "index.ts"); const indexJs = path.join(abs, "index.js"); - if (fs.existsSync(indexTs)) return { path: indexTs, external: false }; - if (fs.existsSync(indexJs)) return { path: indexJs, external: false }; + if (fs.existsSync(indexTs)) { + return { path: indexTs, external: false }; + } + if (fs.existsSync(indexJs)) { + return { path: indexJs, external: false }; + } } // Si es un fichero con extensión válida const withExts = [".ts", ".js", ".mjs"]; for (const ext of withExts) { - if (fs.existsSync(abs + ext)) return { path: abs + ext, external: false }; + if (fs.existsSync(abs + ext)) { + return { path: abs + ext, external: false }; + } } return { path: abs, external: false }; diff --git a/apps/web/index.html b/apps/web/index.html index 17f4915b..ac554e08 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -1,20 +1,18 @@ + + + + + - - - - - + FactuGES 2026 + + - FactuGES 2026 - - - - - -
- - - - \ No newline at end of file + + +
+ + + diff --git a/apps/web/package.json b/apps/web/package.json index e879b3fc..0b5575b1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -63,4 +63,4 @@ "tw-animate-css": "^1.4.0", "vite-plugin-html": "^3.2.2" } -} \ No newline at end of file +} diff --git a/apps/web/src/components/avatar-demo.tsx b/apps/web/src/components/avatar-demo.tsx index 3ead8da3..8e666fb1 100644 --- a/apps/web/src/components/avatar-demo.tsx +++ b/apps/web/src/components/avatar-demo.tsx @@ -2,61 +2,61 @@ import { Avatar, AvatarFallback, AvatarImage } from "@repo/shadcn-ui/components" export function AvatarDemo() { return ( -
+
- + CN CN - - + + CN - - + + ER -
+
- + CN - + LR - + ER
-
+
- + CN - + LR - + ER
-
+
- + CN - + LR - + ER
diff --git a/apps/web/src/components/b-demo.tsx b/apps/web/src/components/b-demo.tsx index adb74bcb..b1850db3 100644 --- a/apps/web/src/components/b-demo.tsx +++ b/apps/web/src/components/b-demo.tsx @@ -6,8 +6,6 @@ import { BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, -} from "@repo/shadcn-ui/components"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, @@ -19,16 +17,16 @@ export function BreadcrumbDemo() { - Home + Home - - - Toggle menu + + + Toggle menu - + Documentation Themes GitHub @@ -37,7 +35,7 @@ export function BreadcrumbDemo() { - Components + Components diff --git a/apps/web/src/components/module-routes.tsx b/apps/web/src/components/module-routes.tsx index a8a79aa5..046b0795 100644 --- a/apps/web/src/components/module-routes.tsx +++ b/apps/web/src/components/module-routes.tsx @@ -165,8 +165,7 @@ const normalizeRoute = ({ const routeLayout = resolveRouteLayout(route, module, inheritedLayout); const routeProtected = resolveRouteProtected(route, module, inheritedProtected); const routeRequireCompany = - routeProtected && - (route.handle?.requireCompany ?? inheritedRequireCompany ?? true); + routeProtected && (route.handle?.requireCompany ?? inheritedRequireCompany ?? true); const normalizedChildren = route.children ?.map((child) => diff --git a/apps/web/src/components/slider-demo.tsx b/apps/web/src/components/slider-demo.tsx index 90de068f..43c1cd7c 100644 --- a/apps/web/src/components/slider-demo.tsx +++ b/apps/web/src/components/slider-demo.tsx @@ -6,10 +6,10 @@ type SliderProps = React.ComponentProps; export function SliderDemo({ className, ...props }: SliderProps) { return ( ); diff --git a/apps/web/src/home/ui/blocks/home-attention-panel.tsx b/apps/web/src/home/ui/blocks/home-attention-panel.tsx index 12b6feff..14c51c13 100644 --- a/apps/web/src/home/ui/blocks/home-attention-panel.tsx +++ b/apps/web/src/home/ui/blocks/home-attention-panel.tsx @@ -33,7 +33,7 @@ export const HomeAttentionPanel = ({ items, hasError }: HomeAttentionPanelProps)

{t("web.home.attention.empty")}

) : null} - {!hasError ? ( + {hasError ? null : (
    {items.map((item) => (
  • @@ -53,7 +53,7 @@ export const HomeAttentionPanel = ({ items, hasError }: HomeAttentionPanelProps)
  • ))}
- ) : null} + )}
); diff --git a/apps/web/src/home/ui/blocks/home-monthly-billing-summary.tsx b/apps/web/src/home/ui/blocks/home-monthly-billing-summary.tsx index 2c14f95c..f29efb23 100644 --- a/apps/web/src/home/ui/blocks/home-monthly-billing-summary.tsx +++ b/apps/web/src/home/ui/blocks/home-monthly-billing-summary.tsx @@ -9,10 +9,7 @@ interface HomeMonthlyBillingSummaryProps { hasError: boolean; } -export const HomeMonthlyBillingSummary = ({ - data, - hasError, -}: HomeMonthlyBillingSummaryProps) => { +export const HomeMonthlyBillingSummary = ({ data, hasError }: HomeMonthlyBillingSummaryProps) => { const { t } = useAppTranslation(); return ( @@ -22,7 +19,9 @@ export const HomeMonthlyBillingSummary = ({

{t("web.home.monthlyBilling.title")}

-

{t("web.home.monthlyBilling.description")}

+

+ {t("web.home.monthlyBilling.description")} +

{hasError ? ( @@ -32,7 +31,7 @@ export const HomeMonthlyBillingSummary = ({ /> ) : null} - {!hasError ? ( + {hasError ? null : (

{data?.totalAmountLabel ?? @@ -56,7 +55,7 @@ export const HomeMonthlyBillingSummary = ({

) : null}
- ) : null} + )}
); diff --git a/apps/web/src/home/ui/blocks/home-proforma-status-summary.tsx b/apps/web/src/home/ui/blocks/home-proforma-status-summary.tsx index c4cf38c5..6e0cbe2b 100644 --- a/apps/web/src/home/ui/blocks/home-proforma-status-summary.tsx +++ b/apps/web/src/home/ui/blocks/home-proforma-status-summary.tsx @@ -10,10 +10,7 @@ interface HomeProformaStatusSummaryProps { hasError: boolean; } -export const HomeProformaStatusSummary = ({ - items, - hasError, -}: HomeProformaStatusSummaryProps) => { +export const HomeProformaStatusSummary = ({ items, hasError }: HomeProformaStatusSummaryProps) => { const { t } = useAppTranslation(); return ( diff --git a/apps/web/src/home/ui/blocks/home-recent-activity.tsx b/apps/web/src/home/ui/blocks/home-recent-activity.tsx index 52892760..0b21814f 100644 --- a/apps/web/src/home/ui/blocks/home-recent-activity.tsx +++ b/apps/web/src/home/ui/blocks/home-recent-activity.tsx @@ -28,7 +28,9 @@ export const HomeRecentActivity = ({ items, hasPartialError }: HomeRecentActivit

{t("web.home.recentActivity.title")}

-

{t("web.home.recentActivity.description")}

+

+ {t("web.home.recentActivity.description")} +

{hasPartialError ? ( diff --git a/apps/web/src/layout/app-sidebar-nav.tsx b/apps/web/src/layout/app-sidebar-nav.tsx index b9a646aa..1abf80aa 100644 --- a/apps/web/src/layout/app-sidebar-nav.tsx +++ b/apps/web/src/layout/app-sidebar-nav.tsx @@ -175,7 +175,10 @@ export const AppSidebarNav = ({ sections }: AppSidebarNavProps) => { render={ + diff --git a/modules/auth/src/web/context/auth-context.tsx b/modules/auth/src/web/context/auth-context.tsx index 7d6884e0..1f2336b9 100644 --- a/modules/auth/src/web/context/auth-context.tsx +++ b/modules/auth/src/web/context/auth-context.tsx @@ -1,5 +1,6 @@ -import { PropsWithChildren, createContext, useEffect, useState } from "react"; -import { IAuthService } from "../services"; +import { type PropsWithChildren, createContext, useEffect, useState } from "react"; + +import type { IAuthService } from "../services"; export type AuthContextType = { token: string | null; @@ -30,7 +31,7 @@ export const AuthProvider = ({ useEffect(() => { setToken(getAccessToken()); - }, []); + }, [getAccessToken]); const login = async (email: string, password: string) => { const { token } = await authService.login({ email, password }); diff --git a/modules/auth/src/web/hooks/use-auth.ts b/modules/auth/src/web/hooks/use-auth.ts index a497658f..16beb323 100644 --- a/modules/auth/src/web/hooks/use-auth.ts +++ b/modules/auth/src/web/hooks/use-auth.ts @@ -1,5 +1,6 @@ import { useContext } from "react"; -import { AuthContext, AuthContextType } from "../context"; + +import { AuthContext, type AuthContextType } from "../context"; /** * Hook para acceder al contexto de autenticación. diff --git a/modules/auth/src/web/services/auth-service.ts b/modules/auth/src/web/services/auth-service.ts index eebc24bf..23ba8a51 100644 --- a/modules/auth/src/web/services/auth-service.ts +++ b/modules/auth/src/web/services/auth-service.ts @@ -1,5 +1,6 @@ import { useDataSource } from "@erp/core/hooks"; -import { ILoginRequestDTO, ILoginResponseDTO } from "../../common"; + +import type { ILoginRequestDTO, ILoginResponseDTO } from "../../common"; export interface IAuthService { login: (credentials: ILoginRequestDTO) => Promise; diff --git a/modules/auth/src/web/services/me-service.ts b/modules/auth/src/web/services/me-service.ts index d08a2bc8..3042cadc 100644 --- a/modules/auth/src/web/services/me-service.ts +++ b/modules/auth/src/web/services/me-service.ts @@ -1,7 +1,9 @@ import { useDataSource } from "@erp/core/hooks"; import { useQuery } from "@tanstack/react-query"; + import { useAuth } from "../hooks"; -import { User } from "./types"; + +import type { User } from "./types"; export const useCurrentUser = () => { const { token } = useAuth(); diff --git a/modules/auth/src/web/services/user-service.ts b/modules/auth/src/web/services/user-service.ts index e90980f4..fbce3569 100644 --- a/modules/auth/src/web/services/user-service.ts +++ b/modules/auth/src/web/services/user-service.ts @@ -1,6 +1,7 @@ import { useDataSource } from "@erp/core/hooks"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { User } from "./types"; + +import type { User } from "./types"; export const useUsers = () => { const client = useDataSource(); diff --git a/modules/catalogs/package.json b/modules/catalogs/package.json index efdf2e0f..2c16c560 100644 --- a/modules/catalogs/package.json +++ b/modules/catalogs/package.json @@ -54,4 +54,4 @@ "use-debounce": "^10.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/catalogs/src/api/application/payment-methods/services/payment-method-creator.ts b/modules/catalogs/src/api/application/payment-methods/services/payment-method-creator.ts index bc69b0ab..8316fd6e 100644 --- a/modules/catalogs/src/api/application/payment-methods/services/payment-method-creator.ts +++ b/modules/catalogs/src/api/application/payment-methods/services/payment-method-creator.ts @@ -51,7 +51,9 @@ export class PaymentMethodCreator implements IPaymentMethodCreator { before: {}, after: toPaymentMethodAuditState(paymentMethod), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } return Result.ok(paymentMethod); } diff --git a/modules/catalogs/src/api/application/payment-methods/services/payment-method-deleter.ts b/modules/catalogs/src/api/application/payment-methods/services/payment-method-deleter.ts index 44a4c3a6..780616a2 100644 --- a/modules/catalogs/src/api/application/payment-methods/services/payment-method-deleter.ts +++ b/modules/catalogs/src/api/application/payment-methods/services/payment-method-deleter.ts @@ -76,7 +76,9 @@ export class PaymentMethodDeleter implements IPaymentMethodDeleter { before, after: {}, }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } return Result.ok(paymentMethod); } diff --git a/modules/catalogs/src/api/application/payment-methods/services/payment-method-status-changer.ts b/modules/catalogs/src/api/application/payment-methods/services/payment-method-status-changer.ts index 04be242c..eec5baf6 100644 --- a/modules/catalogs/src/api/application/payment-methods/services/payment-method-status-changer.ts +++ b/modules/catalogs/src/api/application/payment-methods/services/payment-method-status-changer.ts @@ -58,7 +58,9 @@ export class PaymentMethodStatusChanger implements IPaymentMethodStatusChanger { before, after: toPaymentMethodAuditState(paymentMethod), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } return Result.ok(paymentMethod); } diff --git a/modules/catalogs/src/api/application/payment-methods/services/payment-method-updater.ts b/modules/catalogs/src/api/application/payment-methods/services/payment-method-updater.ts index 37df1a32..37e3ad3c 100644 --- a/modules/catalogs/src/api/application/payment-methods/services/payment-method-updater.ts +++ b/modules/catalogs/src/api/application/payment-methods/services/payment-method-updater.ts @@ -70,7 +70,9 @@ export class PaymentMethodUpdater implements IPaymentMethodUpdater { before, after: toPaymentMethodAuditState(paymentMethod), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } return Result.ok(paymentMethod); } diff --git a/modules/catalogs/src/api/application/payment-methods/services/set-default-payment-method.service.ts b/modules/catalogs/src/api/application/payment-methods/services/set-default-payment-method.service.ts index 55650f83..08e8d3bf 100644 --- a/modules/catalogs/src/api/application/payment-methods/services/set-default-payment-method.service.ts +++ b/modules/catalogs/src/api/application/payment-methods/services/set-default-payment-method.service.ts @@ -82,7 +82,9 @@ export class SetDefaultPaymentMethodService implements ISetDefaultPaymentMethodS before: previousDefaultState, after: toPaymentMethodAuditState(currentDefault), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } } } @@ -103,7 +105,9 @@ export class SetDefaultPaymentMethodService implements ISetDefaultPaymentMethodS before: previousPaymentMethodState, after: toPaymentMethodAuditState(paymentMethod), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } } return Result.ok(paymentMethod); diff --git a/modules/catalogs/src/api/application/payment-methods/use-cases/delete-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-methods/use-cases/delete-payment-method-by-id.use-case.ts index f549d31a..edd80e87 100644 --- a/modules/catalogs/src/api/application/payment-methods/use-cases/delete-payment-method-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-methods/use-cases/delete-payment-method-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DeletePaymentMethodByIdUseCase { const { payment_method_id, companyId } = params; const idOrError = UniqueID.create(payment_method_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentMethodId = idOrError.data; diff --git a/modules/catalogs/src/api/application/payment-methods/use-cases/disable-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-methods/use-cases/disable-payment-method-by-id.use-case.ts index b82f7f35..0b1a276c 100644 --- a/modules/catalogs/src/api/application/payment-methods/use-cases/disable-payment-method-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-methods/use-cases/disable-payment-method-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DisablePaymentMethodByIdUseCase { const { payment_method_id, companyId } = params; const idOrError = UniqueID.create(payment_method_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentMethodId = idOrError.data; diff --git a/modules/catalogs/src/api/application/payment-methods/use-cases/enable-payment-method-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-methods/use-cases/enable-payment-method-by-id.use-case.ts index cfb6aa83..d6db1c2a 100644 --- a/modules/catalogs/src/api/application/payment-methods/use-cases/enable-payment-method-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-methods/use-cases/enable-payment-method-by-id.use-case.ts @@ -24,7 +24,9 @@ export class EnablePaymentMethodByIdUseCase { const { payment_method_id, companyId } = params; const idOrError = UniqueID.create(payment_method_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentMethodId = idOrError.data; diff --git a/modules/catalogs/src/api/application/payment-terms/use-cases/delete-payment-term-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-terms/use-cases/delete-payment-term-by-id.use-case.ts index ced4b4b8..190bf9bb 100644 --- a/modules/catalogs/src/api/application/payment-terms/use-cases/delete-payment-term-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-terms/use-cases/delete-payment-term-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DeletePaymentTermByIdUseCase { const { payment_term_id, companyId } = params; const idOrError = UniqueID.create(payment_term_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentTermId = idOrError.data; diff --git a/modules/catalogs/src/api/application/payment-terms/use-cases/disable-payment-term-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-terms/use-cases/disable-payment-term-by-id.use-case.ts index d203b017..94c3baed 100644 --- a/modules/catalogs/src/api/application/payment-terms/use-cases/disable-payment-term-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-terms/use-cases/disable-payment-term-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DisablePaymentTermByIdUseCase { const { payment_term_id, companyId } = params; const idOrError = UniqueID.create(payment_term_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentTermId = idOrError.data; diff --git a/modules/catalogs/src/api/application/payment-terms/use-cases/enable-payment-term-by-id.use-case.ts b/modules/catalogs/src/api/application/payment-terms/use-cases/enable-payment-term-by-id.use-case.ts index 356ce6b8..a9d350c5 100644 --- a/modules/catalogs/src/api/application/payment-terms/use-cases/enable-payment-term-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/payment-terms/use-cases/enable-payment-term-by-id.use-case.ts @@ -24,7 +24,9 @@ export class EnablePaymentTermByIdUseCase { const { payment_term_id, companyId } = params; const idOrError = UniqueID.create(payment_term_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const paymentTermId = idOrError.data; diff --git a/modules/catalogs/src/api/application/tax-definitions/mappers/create-tax-definition-input.mapper.ts b/modules/catalogs/src/api/application/tax-definitions/mappers/create-tax-definition-input.mapper.ts index e4920a10..c4b81833 100644 --- a/modules/catalogs/src/api/application/tax-definitions/mappers/create-tax-definition-input.mapper.ts +++ b/modules/catalogs/src/api/application/tax-definitions/mappers/create-tax-definition-input.mapper.ts @@ -9,7 +9,7 @@ import { } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import type { ITaxDefinitionCreateProps, TaxDefinitionCode } from "../../../domain"; +import type { ITaxDefinitionCreateProps } from "../../../domain"; export interface ICreateTaxDefinitionInputMapper { map( diff --git a/modules/catalogs/src/api/application/tax-definitions/repositories/index.ts b/modules/catalogs/src/api/application/tax-definitions/repositories/index.ts index aecaa2c7..03df3698 100644 --- a/modules/catalogs/src/api/application/tax-definitions/repositories/index.ts +++ b/modules/catalogs/src/api/application/tax-definitions/repositories/index.ts @@ -1 +1 @@ -export * from './tax-definition-repository.interface'; +export * from "./tax-definition-repository.interface"; diff --git a/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-status-changer.service.ts b/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-status-changer.service.ts index 4738121e..7769f055 100644 --- a/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-status-changer.service.ts +++ b/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-status-changer.service.ts @@ -17,15 +17,21 @@ export class TaxDefinitionStatusChanger implements ITaxDefinitionStatusChanger { public async enable(companyId: UniqueID, id: UniqueID, transaction?: unknown) { const existing = await this.repository.getByIdInCompany(companyId, id, transaction); - if (existing.isFailure) return Result.fail(existing.error); + if (existing.isFailure) { + return Result.fail(existing.error); + } const taxDefinition = existing.data; const changeResult = taxDefinition.enable(); - if (changeResult.isFailure) return Result.fail(changeResult.error); + if (changeResult.isFailure) { + return Result.fail(changeResult.error); + } if (changeResult.data) { const saveResult = await this.repository.update(taxDefinition, transaction); - if (saveResult.isFailure) return Result.fail(saveResult.error); + if (saveResult.isFailure) { + return Result.fail(saveResult.error); + } } return Result.ok(changeResult.data); @@ -33,15 +39,21 @@ export class TaxDefinitionStatusChanger implements ITaxDefinitionStatusChanger { public async disable(companyId: UniqueID, id: UniqueID, transaction?: unknown) { const existing = await this.repository.getByIdInCompany(companyId, id, transaction); - if (existing.isFailure) return Result.fail(existing.error); + if (existing.isFailure) { + return Result.fail(existing.error); + } const taxDefinition = existing.data; const changeResult = taxDefinition.disable(); - if (changeResult.isFailure) return Result.fail(changeResult.error); + if (changeResult.isFailure) { + return Result.fail(changeResult.error); + } if (changeResult.data) { const saveResult = await this.repository.update(taxDefinition, transaction); - if (saveResult.isFailure) return Result.fail(saveResult.error); + if (saveResult.isFailure) { + return Result.fail(saveResult.error); + } } return Result.ok(changeResult.data); diff --git a/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-updater.service.ts b/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-updater.service.ts index 27250b0b..5d13b523 100644 --- a/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-updater.service.ts +++ b/modules/catalogs/src/api/application/tax-definitions/services/tax-definition-updater.service.ts @@ -22,16 +22,22 @@ export class TaxDefinitionUpdater implements ITaxDefinitionUpdater { const { companyId, id, patch, transaction } = params; const existingResult = await this.repository.getByIdInCompany(companyId, id, transaction); - if (existingResult.isFailure) return Result.fail(existingResult.error); + if (existingResult.isFailure) { + return Result.fail(existingResult.error); + } const taxDefinition = existingResult.data; const updateResult = taxDefinition.update(patch); - if (updateResult.isFailure) return Result.fail(updateResult.error); + if (updateResult.isFailure) { + return Result.fail(updateResult.error); + } if (updateResult.data) { const saveResult = await this.repository.update(taxDefinition, transaction); - if (saveResult.isFailure) return Result.fail(saveResult.error); + if (saveResult.isFailure) { + return Result.fail(saveResult.error); + } } return Result.ok(taxDefinition); diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/delete-tax-definition-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/delete-tax-definition-by-id.use-case.ts index 8a8bab71..369a0870 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/delete-tax-definition-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/delete-tax-definition-by-id.use-case.ts @@ -12,7 +12,9 @@ export class DeleteTaxDefinitionByIdUseCase { public async execute(companyId: UniqueID, id: UniqueID) { const result = await this.deps.deleter.deleteByIdInCompany(companyId, id); - if (result.isFailure) return result; + if (result.isFailure) { + return result; + } return Result.ok(result.data); } diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/disable-tax-definition-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/disable-tax-definition-by-id.use-case.ts index 6ea61810..7f8014b8 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/disable-tax-definition-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/disable-tax-definition-by-id.use-case.ts @@ -12,7 +12,9 @@ export class DisableTaxDefinitionByIdUseCase { public async execute(companyId: UniqueID, id: UniqueID) { const result = await this.deps.statusChanger.disable(companyId, id); - if (result.isFailure) return result; + if (result.isFailure) { + return result; + } return Result.ok(result.data); } diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/enable-tax-definition-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/enable-tax-definition-by-id.use-case.ts index 2a48d635..a5052009 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/enable-tax-definition-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/enable-tax-definition-by-id.use-case.ts @@ -12,7 +12,9 @@ export class EnableTaxDefinitionByIdUseCase { public async execute(companyId: UniqueID, id: UniqueID) { const result = await this.deps.statusChanger.enable(companyId, id); - if (result.isFailure) return result; + if (result.isFailure) { + return result; + } return Result.ok(result.data); } diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/get-tax-definition-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/get-tax-definition-by-id.use-case.ts index 8bae2eb8..0fa1a1f9 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/get-tax-definition-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/get-tax-definition-by-id.use-case.ts @@ -14,7 +14,9 @@ export class GetTaxDefinitionByIdUseCase { public async execute(companyId: UniqueID, id: UniqueID) { const found = await this.deps.finder.getByIdInCompany(companyId, id); - if (found.isFailure) return found; + if (found.isFailure) { + return found; + } const snapshot = this.deps.fullSnapshotBuilder.toOutput(found.data as any); return Result.ok(snapshot); diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/list-tax-definitions.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/list-tax-definitions.use-case.ts index 3977074c..af91236a 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/list-tax-definitions.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/list-tax-definitions.use-case.ts @@ -15,7 +15,9 @@ export class ListTaxDefinitionsUseCase { public async execute(companyId: UniqueID, criteria: Criteria) { const found = await this.deps.repository.findByCriteriaInCompany(companyId, criteria); - if (found.isFailure) return found; + if (found.isFailure) { + return found; + } const collection = found.data.map((item) => this.deps.summarySnapshotBuilder.toOutput(item)); return Result.ok(collection); diff --git a/modules/catalogs/src/api/application/tax-definitions/use-cases/update-tax-definition-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-definitions/use-cases/update-tax-definition-by-id.use-case.ts index 16ec4203..493df0fe 100644 --- a/modules/catalogs/src/api/application/tax-definitions/use-cases/update-tax-definition-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-definitions/use-cases/update-tax-definition-by-id.use-case.ts @@ -21,7 +21,9 @@ export class UpdateTaxDefinitionByIdUseCase { public async execute(companyId: UniqueID, params: UpdateTaxDefinitionByIdUseCaseInput) { const mapped = this.deps.dtoMapper.map(params.dto as any); - if (mapped.isFailure) return mapped; + if (mapped.isFailure) { + return mapped; + } const result = await this.deps.updater.update({ companyId, @@ -29,7 +31,9 @@ export class UpdateTaxDefinitionByIdUseCase { patch: mapped.data, transaction: undefined, }); - if (result.isFailure) return result; + if (result.isFailure) { + return result; + } const snapshot = this.deps.fullSnapshotBuilder.toOutput(result.data as any); return Result.ok(snapshot); diff --git a/modules/catalogs/src/api/application/tax-regimes/use-cases/delete-tax-regime-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-regimes/use-cases/delete-tax-regime-by-id.use-case.ts index 5418ef7a..c0a6cb30 100644 --- a/modules/catalogs/src/api/application/tax-regimes/use-cases/delete-tax-regime-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-regimes/use-cases/delete-tax-regime-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DeleteTaxRegimeByIdUseCase { const { tax_regime_id, companyId } = params; const idOrError = UniqueID.create(tax_regime_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const taxRegimeId = idOrError.data; diff --git a/modules/catalogs/src/api/application/tax-regimes/use-cases/disable-tax-regime-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-regimes/use-cases/disable-tax-regime-by-id.use-case.ts index ebe0d4e5..ca539ee2 100644 --- a/modules/catalogs/src/api/application/tax-regimes/use-cases/disable-tax-regime-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-regimes/use-cases/disable-tax-regime-by-id.use-case.ts @@ -24,7 +24,9 @@ export class DisableTaxRegimeByIdUseCase { const { tax_regime_id, companyId } = params; const idOrError = UniqueID.create(tax_regime_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const taxRegimeId = idOrError.data; diff --git a/modules/catalogs/src/api/application/tax-regimes/use-cases/enable-tax-regime-by-id.use-case.ts b/modules/catalogs/src/api/application/tax-regimes/use-cases/enable-tax-regime-by-id.use-case.ts index c64cf07c..e7b54f25 100644 --- a/modules/catalogs/src/api/application/tax-regimes/use-cases/enable-tax-regime-by-id.use-case.ts +++ b/modules/catalogs/src/api/application/tax-regimes/use-cases/enable-tax-regime-by-id.use-case.ts @@ -24,7 +24,9 @@ export class EnableTaxRegimeByIdUseCase { const { tax_regime_id, companyId } = params; const idOrError = UniqueID.create(tax_regime_id); - if (idOrError.isFailure) return Result.fail(idOrError.error); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } const taxRegimeId = idOrError.data; diff --git a/modules/catalogs/src/api/domain/payment-methods/__tests__/payment-method.aggregate.test.ts b/modules/catalogs/src/api/domain/payment-methods/__tests__/payment-method.aggregate.test.ts index a5f3dd2e..af20aeb7 100644 --- a/modules/catalogs/src/api/domain/payment-methods/__tests__/payment-method.aggregate.test.ts +++ b/modules/catalogs/src/api/domain/payment-methods/__tests__/payment-method.aggregate.test.ts @@ -1,5 +1,5 @@ -import { describe, test } from "node:test"; import assert from "node:assert/strict"; +import { describe, test } from "node:test"; import { Name, UniqueID } from "@repo/rdx-ddd"; import { Maybe } from "@repo/rdx-utils"; diff --git a/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts b/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts index 5c16edc1..ed8d06cf 100644 --- a/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts +++ b/modules/catalogs/src/api/domain/payment-methods/payment-method.aggregate.ts @@ -3,8 +3,8 @@ import { type Maybe, Result } from "@repo/rdx-utils"; import { PaymentMethodCannotBeDefaultError, - PaymentMethodCannotBeDisabledError, PaymentMethodCannotBeDeletedError, + PaymentMethodCannotBeDisabledError, PaymentMethodCannotBeUpdatedError, } from "./errors"; diff --git a/modules/catalogs/src/api/domain/payment-terms/__tests__/payment-term.aggregate.test.ts b/modules/catalogs/src/api/domain/payment-terms/__tests__/payment-term.aggregate.test.ts index a19d7014..197adc01 100644 --- a/modules/catalogs/src/api/domain/payment-terms/__tests__/payment-term.aggregate.test.ts +++ b/modules/catalogs/src/api/domain/payment-terms/__tests__/payment-term.aggregate.test.ts @@ -6,8 +6,8 @@ import { Maybe } from "@repo/rdx-utils"; import { PaymentTerm } from "../payment-term.aggregate"; import { PaymentTermDueDays } from "../payment-term-due-days"; -import { PaymentTermPercentage } from "../payment-term-percentage"; import { PaymentTermName } from "../payment-term-name"; +import { PaymentTermPercentage } from "../payment-term-percentage"; const createRuleProps = (params: { dueDays: number; percentageValue: number; scale?: number }) => { const dueDaysResult = PaymentTermDueDays.create(params.dueDays); @@ -68,7 +68,9 @@ describe("PaymentTerm aggregate", () => { }); test("creates immediate payment term with 0 days and 100%", () => { - const paymentTerm = createPaymentTerm([createRuleProps({ dueDays: 0, percentageValue: 10000 })]); + const paymentTerm = createPaymentTerm([ + createRuleProps({ dueDays: 0, percentageValue: 10000 }), + ]); assert.equal(paymentTerm.dueRules.getAll().length, 1); assert.equal(paymentTerm.dueRules.getAll()[0]?.dueDays.toPrimitive(), 0); @@ -76,7 +78,9 @@ describe("PaymentTerm aggregate", () => { }); test("creates payment term with 30 days and 100%", () => { - const paymentTerm = createPaymentTerm([createRuleProps({ dueDays: 30, percentageValue: 10000 })]); + const paymentTerm = createPaymentTerm([ + createRuleProps({ dueDays: 30, percentageValue: 10000 }), + ]); assert.equal(paymentTerm.dueRules.getAll()[0]?.dueDays.toPrimitive(), 30); }); diff --git a/modules/catalogs/src/api/domain/payment-terms/payment-term-percentage.ts b/modules/catalogs/src/api/domain/payment-terms/payment-term-percentage.ts index fe56fb43..ef54ae2d 100644 --- a/modules/catalogs/src/api/domain/payment-terms/payment-term-percentage.ts +++ b/modules/catalogs/src/api/domain/payment-terms/payment-term-percentage.ts @@ -25,7 +25,9 @@ export class PaymentTermPercentage extends Percentage { if (value > 10000) { return Result.fail( - new InvalidPaymentTermPercentageError("Payment term percentage cannot be greater than 100.00.") + new InvalidPaymentTermPercentageError( + "Payment term percentage cannot be greater than 100.00." + ) ); } diff --git a/modules/catalogs/src/api/infrastructure/payment-methods/payment-method-reference-usage-checker.ts b/modules/catalogs/src/api/infrastructure/payment-methods/payment-method-reference-usage-checker.ts index 246f84df..5c1f4280 100644 --- a/modules/catalogs/src/api/infrastructure/payment-methods/payment-method-reference-usage-checker.ts +++ b/modules/catalogs/src/api/infrastructure/payment-methods/payment-method-reference-usage-checker.ts @@ -8,25 +8,14 @@ import { } from "../../application/payment-methods/services"; type CountableModel = { - count: (params: { - where: Record; - transaction?: Transaction; - }) => Promise; + count: (params: { where: Record; transaction?: Transaction }) => Promise; }; type SequelizeModelsRegistry = Record; const buildCountUsageSource = - (params: { - database: Sequelize; - modelName: string; - source: PaymentMethodUsageSource; - }) => - async (input: { - companyId: UniqueID; - paymentMethodId: UniqueID; - transaction?: unknown; - }) => { + (params: { database: Sequelize; modelName: string; source: PaymentMethodUsageSource }) => + async (input: { companyId: UniqueID; paymentMethodId: UniqueID; transaction?: unknown }) => { const { database, modelName, source } = params; const { companyId, paymentMethodId, transaction } = input; diff --git a/modules/catalogs/src/api/infrastructure/tax-definitions/index.ts b/modules/catalogs/src/api/infrastructure/tax-definitions/index.ts index d1dab3b3..3e34d105 100644 --- a/modules/catalogs/src/api/infrastructure/tax-definitions/index.ts +++ b/modules/catalogs/src/api/infrastructure/tax-definitions/index.ts @@ -1,3 +1,3 @@ -export * from "./express"; export * from "./di"; +export * from "./express"; export * from "./persistence"; diff --git a/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/mappers/sequelize-tax-definition-domain.mapper.ts b/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/mappers/sequelize-tax-definition-domain.mapper.ts index 344bb124..7a20ca29 100644 --- a/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/mappers/sequelize-tax-definition-domain.mapper.ts +++ b/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/mappers/sequelize-tax-definition-domain.mapper.ts @@ -29,7 +29,7 @@ export class SequelizeTaxDefinitionDomainMapper extends SequelizeDomainMapper< > { public mapToDomain( raw: TaxDefinitionModel, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const errors: ValidationErrorDetail[] = []; @@ -81,14 +81,16 @@ export class SequelizeTaxDefinitionDomainMapper extends SequelizeDomainMapper< const allowedSurchargeCodes = extractOrPushError( maybeFromNullableResult(raw.allowed_surcharge_codes, (v) => { const values = v.split(";"); - if (!Array.isArray(values)) + if (!Array.isArray(values)) { return Result.fail(new Error("Invalid allowed_surcharge_codes")); + } const arr: any[] = []; for (const el of values) { const _result = TaxDefinitionCodeVO.create(String(el)); - if (_result.isFailure) + if (_result.isFailure) { return Result.fail(new Error("Invalid allowed_surcharge_codes element")); + } arr.push(_result.data); } @@ -145,7 +147,7 @@ export class SequelizeTaxDefinitionDomainMapper extends SequelizeDomainMapper< public mapToPersistence( source: TaxDefinition, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const dto: TaxDefinitionCreationAttributes = { id: source.id.toPrimitive(), diff --git a/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/repositories/sequelize-tax-definition.repository.ts b/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/repositories/sequelize-tax-definition.repository.ts index f10638e6..3bb70092 100644 --- a/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/repositories/sequelize-tax-definition.repository.ts +++ b/modules/catalogs/src/api/infrastructure/tax-definitions/persistence/sequelize/repositories/sequelize-tax-definition.repository.ts @@ -35,7 +35,9 @@ export class SequelizeTaxDefinitionRepository ): Promise> { try { const dtoResult = this.domainMapper.mapToPersistence(taxDefinition); - if (dtoResult.isFailure) return Result.fail(dtoResult.error); + if (dtoResult.isFailure) { + return Result.fail(dtoResult.error); + } await TaxDefinitionModel.create(dtoResult.data, { transaction }); return Result.ok(); @@ -50,7 +52,9 @@ export class SequelizeTaxDefinitionRepository ): Promise> { try { const dtoResult = this.domainMapper.mapToPersistence(taxDefinition); - if (dtoResult.isFailure) return Result.fail(dtoResult.error); + if (dtoResult.isFailure) { + return Result.fail(dtoResult.error); + } const { id, ...payload } = dtoResult.data as any; const [affected] = await TaxDefinitionModel.update(payload, { @@ -97,7 +101,9 @@ export class SequelizeTaxDefinitionRepository where: { id: id.toString(), company_id: companyId.toString() }, transaction, }); - if (!row) return Result.fail(new EntityNotFoundError("TaxDefinition", "id", id.toString())); + if (!row) { + return Result.fail(new EntityNotFoundError("TaxDefinition", "id", id.toString())); + } return this.domainMapper.mapToDomain(row); } catch (err: unknown) { @@ -115,8 +121,9 @@ export class SequelizeTaxDefinitionRepository where: { code: code.toString(), company_id: companyId.toString() }, transaction, }); - if (!row) + if (!row) { return Result.fail(new EntityNotFoundError("TaxDefinition", "code", code.toString())); + } return this.domainMapper.mapToDomain(row); } catch (err: unknown) { return Result.fail(translateSequelizeError(err)); @@ -251,8 +258,9 @@ export class SequelizeTaxDefinitionRepository where: { id: id.toString(), company_id: companyId.toString() }, transaction, }); - if (deleted === 0) + if (deleted === 0) { return Result.fail(new EntityNotFoundError("TaxDefinition", "id", id.toString())); + } return Result.ok(true); } catch (err: unknown) { return Result.fail(translateSequelizeError(err)); diff --git a/modules/catalogs/src/api/module.test.ts b/modules/catalogs/src/api/module.test.ts index 26f8da16..7f940b36 100644 --- a/modules/catalogs/src/api/module.test.ts +++ b/modules/catalogs/src/api/module.test.ts @@ -12,6 +12,8 @@ test("catalogsModule compone el finder público de payment methods sin HTTP", as logger: { info: () => undefined }, } as never); - const paymentMethods = setup?.services?.paymentMethods as { finder: { constructor: { name: string } } }; + const paymentMethods = setup?.services?.paymentMethods as { + finder: { constructor: { name: string } }; + }; assert.equal(paymentMethods.finder.constructor.name, "PaymentMethodPublicFinder"); }); diff --git a/modules/catalogs/src/common/dto/tax-definitions/response/get-tax-definition-by-id.response.dto.ts b/modules/catalogs/src/common/dto/tax-definitions/response/get-tax-definition-by-id.response.dto.ts index f1df0fbe..b3b7e8f0 100644 --- a/modules/catalogs/src/common/dto/tax-definitions/response/get-tax-definition-by-id.response.dto.ts +++ b/modules/catalogs/src/common/dto/tax-definitions/response/get-tax-definition-by-id.response.dto.ts @@ -3,6 +3,4 @@ import type { z } from "zod/v4"; import { TaxDefinitionDetailSchema } from "../shared"; export const GetTaxDefinitionByIdResponseSchema = TaxDefinitionDetailSchema; -export type GetTaxDefinitionByIdResponseDTO = z.infer< - typeof GetTaxDefinitionByIdResponseSchema ->; +export type GetTaxDefinitionByIdResponseDTO = z.infer; diff --git a/modules/catalogs/src/web/catalogs-routes.tsx b/modules/catalogs/src/web/catalogs-routes.tsx index 47069bbd..b63609cc 100644 --- a/modules/catalogs/src/web/catalogs-routes.tsx +++ b/modules/catalogs/src/web/catalogs-routes.tsx @@ -1,6 +1,5 @@ -import { lazy } from "react"; - import type { ModuleClientParams, ModuleRouteObject } from "@erp/core/client"; +import { lazy } from "react"; const PaymentMethodsSettingsPage = lazy(() => import("./settings/payment-methods").then((module) => ({ diff --git a/modules/catalogs/src/web/payment-methods/shared/adapters/index.ts b/modules/catalogs/src/web/payment-methods/shared/adapters/index.ts index 34283ba4..b6eccc07 100644 --- a/modules/catalogs/src/web/payment-methods/shared/adapters/index.ts +++ b/modules/catalogs/src/web/payment-methods/shared/adapters/index.ts @@ -1,2 +1,2 @@ -export * from "./list-payment-methods.adapter"; export * from "./get-payment-method-by-id.adapter"; +export * from "./list-payment-methods.adapter"; diff --git a/modules/catalogs/src/web/payment-methods/shared/api/create-payment-method.api.ts b/modules/catalogs/src/web/payment-methods/shared/api/create-payment-method.api.ts index 7e086bd0..75e6605e 100644 --- a/modules/catalogs/src/web/payment-methods/shared/api/create-payment-method.api.ts +++ b/modules/catalogs/src/web/payment-methods/shared/api/create-payment-method.api.ts @@ -1,6 +1,9 @@ import type { IDataSource } from "@erp/core/client"; -import type { CreatePaymentMethodRequestDTO, CreatePaymentMethodResponseDTO } from "../../../../common"; +import type { + CreatePaymentMethodRequestDTO, + CreatePaymentMethodResponseDTO, +} from "../../../../common"; export interface CreatePaymentMethodParams { id: string; diff --git a/modules/catalogs/src/web/payment-methods/shared/api/index.ts b/modules/catalogs/src/web/payment-methods/shared/api/index.ts index b58193f8..53015963 100644 --- a/modules/catalogs/src/web/payment-methods/shared/api/index.ts +++ b/modules/catalogs/src/web/payment-methods/shared/api/index.ts @@ -1,8 +1,8 @@ -export * from "./list-payment-methods-by-criteria.api"; -export * from "./get-payment-method-by-id.api"; export * from "./create-payment-method.api"; -export * from "./update-payment-method.api"; export * from "./delete-payment-method.api"; -export * from "./enable-payment-method.api"; export * from "./disable-payment-method.api"; +export * from "./enable-payment-method.api"; +export * from "./get-payment-method-by-id.api"; +export * from "./list-payment-methods-by-criteria.api"; export * from "./set-default-payment-method.api"; +export * from "./update-payment-method.api"; diff --git a/modules/catalogs/src/web/payment-methods/shared/api/update-payment-method.api.ts b/modules/catalogs/src/web/payment-methods/shared/api/update-payment-method.api.ts index 031ea13a..6ddead86 100644 --- a/modules/catalogs/src/web/payment-methods/shared/api/update-payment-method.api.ts +++ b/modules/catalogs/src/web/payment-methods/shared/api/update-payment-method.api.ts @@ -1,6 +1,9 @@ import type { IDataSource } from "@erp/core/client"; -import type { UpdatePaymentMethodByIdRequestDTO, UpdatePaymentMethodByIdResponseDTO } from "../../../../common"; +import type { + UpdatePaymentMethodByIdRequestDTO, + UpdatePaymentMethodByIdResponseDTO, +} from "../../../../common"; export interface UpdatePaymentMethodParams { paymentMethodId: string; @@ -11,9 +14,8 @@ export function updatePaymentMethod( dataSource: IDataSource, params: UpdatePaymentMethodParams ): Promise { - return dataSource.updateOne( - "catalogs/payment-methods", - params.paymentMethodId, - params.data - ); + return dataSource.updateOne< + UpdatePaymentMethodByIdRequestDTO, + UpdatePaymentMethodByIdResponseDTO + >("catalogs/payment-methods", params.paymentMethodId, params.data); } diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/index.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/index.ts index e6a8b294..b4efd058 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/index.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/index.ts @@ -1,8 +1,8 @@ -export * from "./use-payment-method-list-query"; -export * from "./use-payment-method-detail-query"; export * from "./use-payment-method-create-mutation"; -export * from "./use-payment-method-update-mutation"; export * from "./use-payment-method-delete-mutation"; -export * from "./use-payment-method-enable-mutation"; +export * from "./use-payment-method-detail-query"; export * from "./use-payment-method-disable-mutation"; +export * from "./use-payment-method-enable-mutation"; +export * from "./use-payment-method-list-query"; export * from "./use-payment-method-set-default-mutation"; +export * from "./use-payment-method-update-mutation"; diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-create-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-create-mutation.ts index 23fa9bd2..84c8f01b 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-create-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-create-mutation.ts @@ -1,6 +1,6 @@ +import { useDataSource } from "@erp/core/hooks"; import { ValidationErrorCollection } from "@repo/rdx-ddd"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; -import { useDataSource } from "@erp/core/hooks"; import { CreatePaymentMethodRequestSchema } from "../../../../common"; import { GetPaymentMethodByIdAdapter } from "../adapters"; diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-delete-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-delete-mutation.ts index 5e77be74..c6361349 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-delete-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-delete-mutation.ts @@ -1,7 +1,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; -import { deletePaymentMethod, type DeletePaymentMethodParams } from "../api"; +import { type DeletePaymentMethodParams, deletePaymentMethod } from "../api"; import { DELETE_PAYMENT_METHOD_MUTATION_KEY, diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-disable-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-disable-mutation.ts index 8e520a4d..e9ddf281 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-disable-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-disable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetPaymentMethodByIdAdapter } from "../adapters"; -import { disablePaymentMethod, type DisablePaymentMethodParams } from "../api"; +import { type DisablePaymentMethodParams, disablePaymentMethod } from "../api"; import type { PaymentMethod } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-enable-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-enable-mutation.ts index af4b251c..7e26a47b 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-enable-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-enable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetPaymentMethodByIdAdapter } from "../adapters"; -import { enablePaymentMethod, type EnablePaymentMethodParams } from "../api"; +import { type EnablePaymentMethodParams, enablePaymentMethod } from "../api"; import type { PaymentMethod } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-set-default-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-set-default-mutation.ts index 8b1d1544..d18e7c25 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-set-default-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-set-default-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetPaymentMethodByIdAdapter } from "../adapters"; -import { setDefaultPaymentMethod, type SetDefaultPaymentMethodParams } from "../api"; +import { type SetDefaultPaymentMethodParams, setDefaultPaymentMethod } from "../api"; import type { PaymentMethod } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-update-mutation.ts b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-update-mutation.ts index 313bfda7..296f378b 100644 --- a/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-update-mutation.ts +++ b/modules/catalogs/src/web/payment-methods/shared/hooks/use-payment-method-update-mutation.ts @@ -1,6 +1,6 @@ +import { useDataSource } from "@erp/core/hooks"; import { ValidationErrorCollection } from "@repo/rdx-ddd"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; -import { useDataSource } from "@erp/core/hooks"; import { UpdatePaymentMethodByIdRequestSchema } from "../../../../common"; import { GetPaymentMethodByIdAdapter } from "../adapters"; diff --git a/modules/catalogs/src/web/payment-methods/shared/index.ts b/modules/catalogs/src/web/payment-methods/shared/index.ts index d2bc2683..01b46929 100644 --- a/modules/catalogs/src/web/payment-methods/shared/index.ts +++ b/modules/catalogs/src/web/payment-methods/shared/index.ts @@ -1,4 +1,4 @@ +export * from "./adapters"; +export * from "./api"; export * from "./entities"; export * from "./hooks"; -export * from "./api"; -export * from "./adapters"; diff --git a/modules/catalogs/src/web/payment-methods/utils/index.ts b/modules/catalogs/src/web/payment-methods/utils/index.ts index e154ef11..f7e77c64 100644 --- a/modules/catalogs/src/web/payment-methods/utils/index.ts +++ b/modules/catalogs/src/web/payment-methods/utils/index.ts @@ -1 +1 @@ -export * from './payment-method-options.utils'; +export * from "./payment-method-options.utils"; diff --git a/modules/catalogs/src/web/payment-terms/shared/adapters/index.ts b/modules/catalogs/src/web/payment-terms/shared/adapters/index.ts index 73870d6a..e593515c 100644 --- a/modules/catalogs/src/web/payment-terms/shared/adapters/index.ts +++ b/modules/catalogs/src/web/payment-terms/shared/adapters/index.ts @@ -1,2 +1,2 @@ -export * from "./list-payment-terms.adapter"; export * from "./get-payment-term-by-id.adapter"; +export * from "./list-payment-terms.adapter"; diff --git a/modules/catalogs/src/web/payment-terms/shared/api/index.ts b/modules/catalogs/src/web/payment-terms/shared/api/index.ts index c633faf2..0983cbb5 100644 --- a/modules/catalogs/src/web/payment-terms/shared/api/index.ts +++ b/modules/catalogs/src/web/payment-terms/shared/api/index.ts @@ -1,7 +1,7 @@ -export * from "./list-payment-terms-by-criteria.api"; -export * from "./get-payment-term-by-id.api"; export * from "./create-payment-term.api"; -export * from "./update-payment-term.api"; export * from "./delete-payment-term.api"; -export * from "./enable-payment-term.api"; export * from "./disable-payment-term.api"; +export * from "./enable-payment-term.api"; +export * from "./get-payment-term-by-id.api"; +export * from "./list-payment-terms-by-criteria.api"; +export * from "./update-payment-term.api"; diff --git a/modules/catalogs/src/web/payment-terms/shared/api/update-payment-term.api.ts b/modules/catalogs/src/web/payment-terms/shared/api/update-payment-term.api.ts index 37240bdf..e50e64a7 100644 --- a/modules/catalogs/src/web/payment-terms/shared/api/update-payment-term.api.ts +++ b/modules/catalogs/src/web/payment-terms/shared/api/update-payment-term.api.ts @@ -1,6 +1,9 @@ import type { IDataSource } from "@erp/core/client"; -import type { UpdatePaymentTermByIdRequestDTO, UpdatePaymentTermByIdResponseDTO } from "../../../../common"; +import type { + UpdatePaymentTermByIdRequestDTO, + UpdatePaymentTermByIdResponseDTO, +} from "../../../../common"; export interface UpdatePaymentTermParams { paymentTermId: string; diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/index.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/index.ts index 1fb5b3fd..2ea3e81f 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/index.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/index.ts @@ -1,7 +1,7 @@ -export * from "./use-payment-term-list-query"; -export * from "./use-payment-term-detail-query"; export * from "./use-payment-term-create-mutation"; -export * from "./use-payment-term-update-mutation"; export * from "./use-payment-term-delete-mutation"; -export * from "./use-payment-term-enable-mutation"; +export * from "./use-payment-term-detail-query"; export * from "./use-payment-term-disable-mutation"; +export * from "./use-payment-term-enable-mutation"; +export * from "./use-payment-term-list-query"; +export * from "./use-payment-term-update-mutation"; diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-create-mutation.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-create-mutation.ts index c767b351..c711ff4c 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-create-mutation.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-create-mutation.ts @@ -4,7 +4,7 @@ import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react- import { CreatePaymentTermRequestSchema } from "../../../../common"; import { GetPaymentTermByIdAdapter } from "../adapters"; -import { createPaymentTerm, type CreatePaymentTermParams } from "../api"; +import { type CreatePaymentTermParams, createPaymentTerm } from "../api"; import type { PaymentTerm } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-delete-mutation.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-delete-mutation.ts index b59e6934..487f42db 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-delete-mutation.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-delete-mutation.ts @@ -1,7 +1,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; -import { deletePaymentTerm, type DeletePaymentTermParams } from "../api"; +import { type DeletePaymentTermParams, deletePaymentTerm } from "../api"; import { DELETE_PAYMENT_TERM_MUTATION_KEY, diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-disable-mutation.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-disable-mutation.ts index 718e482b..fadf6261 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-disable-mutation.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-disable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetPaymentTermByIdAdapter } from "../adapters"; -import { disablePaymentTerm, type DisablePaymentTermParams } from "../api"; +import { type DisablePaymentTermParams, disablePaymentTerm } from "../api"; import type { PaymentTerm } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-enable-mutation.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-enable-mutation.ts index 7cad9bac..084f4331 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-enable-mutation.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-enable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetPaymentTermByIdAdapter } from "../adapters"; -import { enablePaymentTerm, type EnablePaymentTermParams } from "../api"; +import { type EnablePaymentTermParams, enablePaymentTerm } from "../api"; import type { PaymentTerm } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-update-mutation.ts b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-update-mutation.ts index 295268e8..a9599532 100644 --- a/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-update-mutation.ts +++ b/modules/catalogs/src/web/payment-terms/shared/hooks/use-payment-term-update-mutation.ts @@ -4,7 +4,7 @@ import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react- import { UpdatePaymentTermByIdRequestSchema } from "../../../../common"; import { GetPaymentTermByIdAdapter } from "../adapters"; -import { updatePaymentTerm, type UpdatePaymentTermParams } from "../api"; +import { type UpdatePaymentTermParams, updatePaymentTerm } from "../api"; import type { PaymentTerm } from "../entities"; import { diff --git a/modules/catalogs/src/web/payment-terms/shared/index.ts b/modules/catalogs/src/web/payment-terms/shared/index.ts index d2bc2683..01b46929 100644 --- a/modules/catalogs/src/web/payment-terms/shared/index.ts +++ b/modules/catalogs/src/web/payment-terms/shared/index.ts @@ -1,4 +1,4 @@ +export * from "./adapters"; +export * from "./api"; export * from "./entities"; export * from "./hooks"; -export * from "./api"; -export * from "./adapters"; diff --git a/modules/catalogs/src/web/payment-terms/utils/index.ts b/modules/catalogs/src/web/payment-terms/utils/index.ts index 834739eb..6ccc710b 100644 --- a/modules/catalogs/src/web/payment-terms/utils/index.ts +++ b/modules/catalogs/src/web/payment-terms/utils/index.ts @@ -1 +1 @@ -export * from './payment-term-options.utils'; +export * from "./payment-term-options.utils"; diff --git a/modules/catalogs/src/web/settings/payment-methods/controllers/use-payment-methods-settings-controller.ts b/modules/catalogs/src/web/settings/payment-methods/controllers/use-payment-methods-settings-controller.ts index befa7879..ab78d44f 100644 --- a/modules/catalogs/src/web/settings/payment-methods/controllers/use-payment-methods-settings-controller.ts +++ b/modules/catalogs/src/web/settings/payment-methods/controllers/use-payment-methods-settings-controller.ts @@ -1,19 +1,24 @@ import { applyValidationErrorCollection } from "@erp/core"; import { getUserErrorMessage, isApiClientError } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; -import { UniqueID, ValidationErrorCollection, isValidationErrorCollection } from "@repo/rdx-ddd"; +import { + UniqueID, + type ValidationErrorCollection, + isValidationErrorCollection, +} from "@repo/rdx-ddd"; import { focusFirstInputFormError, showErrorToast, showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; -import { useCallback, useEffect, useId, useState } from "react"; import type { FormEvent } from "react"; +import { useCallback, useEffect, useId, useState } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../../i18n"; import { + type PaymentMethodListRow, usePaymentMethodCreateMutation, usePaymentMethodDeleteMutation, usePaymentMethodDetailQuery, @@ -21,7 +26,6 @@ import { usePaymentMethodEnableMutation, usePaymentMethodSetDefaultMutation, usePaymentMethodUpdateMutation, - type PaymentMethodListRow, } from "../../../payment-methods/shared"; import type { PaymentMethodForm } from "../entities"; import { PaymentMethodFormSchema } from "../entities"; @@ -71,9 +75,7 @@ export const usePaymentMethodsSettingsController = () => { resolverSchema: PaymentMethodFormSchema, initialValues: buildPaymentMethodFormDefault(), disabled: - createMutation.isPending || - updateMutation.isPending || - editingPaymentMethodQuery.isLoading, + createMutation.isPending || updateMutation.isPending || editingPaymentMethodQuery.isLoading, }); const isSaving = @@ -151,52 +153,49 @@ export const usePaymentMethodsSettingsController = () => { [form, t] ); - const submitPaymentMethod = form.handleSubmit( - async (values) => { - try { - if (editingPaymentMethod) { - const updated = await updateMutation.mutateAsync( - buildUpdatePaymentMethodParams(editingPaymentMethod.id, values) - ); - - showSuccessToast( - t("settings.paymentMethods.messages.updatedTitle"), - t("settings.paymentMethods.messages.updated") - ); - - setEditingPaymentMethodId(updated.id); - closeEditDialog(); - return; - } - - const id = UniqueID.generateNewID().toString(); - await createMutation.mutateAsync(buildCreatePaymentMethodParams(id, values)); - - showSuccessToast( - t("settings.paymentMethods.messages.createdTitle"), - t("settings.paymentMethods.messages.created") + const submitPaymentMethod = form.handleSubmit(async (values) => { + try { + if (editingPaymentMethod) { + const updated = await updateMutation.mutateAsync( + buildUpdatePaymentMethodParams(editingPaymentMethod.id, values) ); - closeCreateDialog(); - } catch (error: unknown) { - const normalizedError = normalizeFormSubmitError(error); + showSuccessToast( + t("settings.paymentMethods.messages.updatedTitle"), + t("settings.paymentMethods.messages.updated") + ); - if (isValidationErrorCollection(normalizedError)) { - applyValidationErrorCollection(form, normalizedError); - focusFirstInputFormError(form.formState.errors, form); - showWarningToast( - t("settings.paymentMethods.messages.validationTitle"), - t("settings.paymentMethods.messages.validationDescription") - ); - return; - } - - const userMessage = getUserErrorMessage(normalizedError); - showErrorToast(userMessage.title, userMessage.description); + setEditingPaymentMethodId(updated.id); + closeEditDialog(); + return; } - }, - onInvalidSubmit - ); + + const id = UniqueID.generateNewID().toString(); + await createMutation.mutateAsync(buildCreatePaymentMethodParams(id, values)); + + showSuccessToast( + t("settings.paymentMethods.messages.createdTitle"), + t("settings.paymentMethods.messages.created") + ); + + closeCreateDialog(); + } catch (error: unknown) { + const normalizedError = normalizeFormSubmitError(error); + + if (isValidationErrorCollection(normalizedError)) { + applyValidationErrorCollection(form, normalizedError); + focusFirstInputFormError(form.formState.errors, form); + showWarningToast( + t("settings.paymentMethods.messages.validationTitle"), + t("settings.paymentMethods.messages.validationDescription") + ); + return; + } + + const userMessage = getUserErrorMessage(normalizedError); + showErrorToast(userMessage.title, userMessage.description); + } + }, onInvalidSubmit); const onSubmit = useCallback( (event: FormEvent) => { diff --git a/modules/catalogs/src/web/settings/payment-methods/entities/index.ts b/modules/catalogs/src/web/settings/payment-methods/entities/index.ts index 585a1ed1..e71724a4 100644 --- a/modules/catalogs/src/web/settings/payment-methods/entities/index.ts +++ b/modules/catalogs/src/web/settings/payment-methods/entities/index.ts @@ -1,4 +1,4 @@ -export * from "./payment-method-settings-row.entity"; export * from "./payment-method-form.entity"; export * from "./payment-method-form.schema"; +export * from "./payment-method-settings-row.entity"; export * from "./payment-methods-settings-page-error-view-model.entity"; diff --git a/modules/catalogs/src/web/settings/payment-methods/ui/components/index.ts b/modules/catalogs/src/web/settings/payment-methods/ui/components/index.ts index 2b5273bd..94022205 100644 --- a/modules/catalogs/src/web/settings/payment-methods/ui/components/index.ts +++ b/modules/catalogs/src/web/settings/payment-methods/ui/components/index.ts @@ -1,5 +1,5 @@ -export * from "./payment-method-form-dialog"; export * from "./delete-payment-method-dialog"; +export * from "./payment-method-form-dialog"; export * from "./payment-method-row-actions"; export * from "./payment-methods-empty-state"; export * from "./payment-methods-settings-skeleton"; diff --git a/modules/catalogs/src/web/settings/payment-methods/ui/components/payment-method-form-dialog.tsx b/modules/catalogs/src/web/settings/payment-methods/ui/components/payment-method-form-dialog.tsx index 1fdc4d1e..bfe6fa68 100644 --- a/modules/catalogs/src/web/settings/payment-methods/ui/components/payment-method-form-dialog.tsx +++ b/modules/catalogs/src/web/settings/payment-methods/ui/components/payment-method-form-dialog.tsx @@ -1,5 +1,3 @@ -import { FormProvider } from "react-hook-form"; -import type { FormEventHandler } from "react"; import { RdxFormGrid, SwitchField, TextAreaField, TextField } from "@repo/rdx-ui/components"; import { Button, @@ -11,8 +9,10 @@ import { DialogTitle, Spinner, } from "@repo/shadcn-ui/components"; +import type { FormEventHandler } from "react"; import { useEffect } from "react"; import type { UseFormReturn } from "react-hook-form"; +import { FormProvider } from "react-hook-form"; import { useTranslation } from "../../../../i18n"; import type { PaymentMethodForm } from "../../entities"; diff --git a/modules/catalogs/src/web/settings/payment-methods/ui/index.ts b/modules/catalogs/src/web/settings/payment-methods/ui/index.ts index 566fb90c..90d54b57 100644 --- a/modules/catalogs/src/web/settings/payment-methods/ui/index.ts +++ b/modules/catalogs/src/web/settings/payment-methods/ui/index.ts @@ -1,2 +1,2 @@ -export * from "./pages"; export * from "./components"; +export * from "./pages"; diff --git a/modules/catalogs/src/web/settings/payment-methods/utils/build-payment-method-form.ts b/modules/catalogs/src/web/settings/payment-methods/utils/build-payment-method-form.ts index e034f578..c132aa2b 100644 --- a/modules/catalogs/src/web/settings/payment-methods/utils/build-payment-method-form.ts +++ b/modules/catalogs/src/web/settings/payment-methods/utils/build-payment-method-form.ts @@ -7,7 +7,9 @@ export const buildPaymentMethodFormDefault = (): PaymentMethodForm => ({ isActive: true, }); -export const buildPaymentMethodFormFromEntity = (paymentMethod: PaymentMethod): PaymentMethodForm => ({ +export const buildPaymentMethodFormFromEntity = ( + paymentMethod: PaymentMethod +): PaymentMethodForm => ({ name: paymentMethod.name, description: paymentMethod.description ?? "", isActive: paymentMethod.isActive, diff --git a/modules/catalogs/src/web/settings/payment-terms/controllers/use-payment-terms-settings-controller.ts b/modules/catalogs/src/web/settings/payment-terms/controllers/use-payment-terms-settings-controller.ts index 33c78dfa..1e9f023b 100644 --- a/modules/catalogs/src/web/settings/payment-terms/controllers/use-payment-terms-settings-controller.ts +++ b/modules/catalogs/src/web/settings/payment-terms/controllers/use-payment-terms-settings-controller.ts @@ -1,15 +1,19 @@ import { applyValidationErrorCollection } from "@erp/core"; import { getUserErrorMessage, isApiClientError } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; -import { UniqueID, type ValidationErrorCollection, isValidationErrorCollection } from "@repo/rdx-ddd"; +import { + UniqueID, + type ValidationErrorCollection, + isValidationErrorCollection, +} from "@repo/rdx-ddd"; import { focusFirstInputFormError, showErrorToast, showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; -import { useCallback, useEffect, useId, useState } from "react"; import type { FormEvent } from "react"; +import { useCallback, useEffect, useId, useState } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../../i18n"; @@ -144,51 +148,48 @@ export const usePaymentTermsSettingsController = () => { [form, t] ); - const submitPaymentTerm = form.handleSubmit( - async (values) => { - try { - if (editingPaymentTerm) { - await updateMutation.mutateAsync( - buildUpdatePaymentTermParams(editingPaymentTerm.id, values) - ); - - showSuccessToast( - t("settings.paymentTerms.messages.updatedTitle"), - t("settings.paymentTerms.messages.updated") - ); - - closeEditDialog(); - return; - } - - const id = UniqueID.generateNewID().toString(); - await createMutation.mutateAsync(buildCreatePaymentTermParams(id, values)); - - showSuccessToast( - t("settings.paymentTerms.messages.createdTitle"), - t("settings.paymentTerms.messages.created") + const submitPaymentTerm = form.handleSubmit(async (values) => { + try { + if (editingPaymentTerm) { + await updateMutation.mutateAsync( + buildUpdatePaymentTermParams(editingPaymentTerm.id, values) ); - closeCreateDialog(); - } catch (error: unknown) { - const normalizedError = normalizeFormSubmitError(error); + showSuccessToast( + t("settings.paymentTerms.messages.updatedTitle"), + t("settings.paymentTerms.messages.updated") + ); - if (isValidationErrorCollection(normalizedError)) { - applyValidationErrorCollection(form, normalizedError); - focusFirstInputFormError(form.formState.errors, form); - showWarningToast( - t("settings.paymentTerms.messages.validationTitle"), - t("settings.paymentTerms.messages.validationDescription") - ); - return; - } - - const userMessage = getUserErrorMessage(normalizedError); - showErrorToast(userMessage.title, userMessage.description); + closeEditDialog(); + return; } - }, - onInvalidSubmit - ); + + const id = UniqueID.generateNewID().toString(); + await createMutation.mutateAsync(buildCreatePaymentTermParams(id, values)); + + showSuccessToast( + t("settings.paymentTerms.messages.createdTitle"), + t("settings.paymentTerms.messages.created") + ); + + closeCreateDialog(); + } catch (error: unknown) { + const normalizedError = normalizeFormSubmitError(error); + + if (isValidationErrorCollection(normalizedError)) { + applyValidationErrorCollection(form, normalizedError); + focusFirstInputFormError(form.formState.errors, form); + showWarningToast( + t("settings.paymentTerms.messages.validationTitle"), + t("settings.paymentTerms.messages.validationDescription") + ); + return; + } + + const userMessage = getUserErrorMessage(normalizedError); + showErrorToast(userMessage.title, userMessage.description); + } + }, onInvalidSubmit); const onSubmit = useCallback( (event: FormEvent) => { diff --git a/modules/catalogs/src/web/settings/payment-terms/entities/payment-term-form.schema.ts b/modules/catalogs/src/web/settings/payment-terms/entities/payment-term-form.schema.ts index 939f3bdc..e99733d6 100644 --- a/modules/catalogs/src/web/settings/payment-terms/entities/payment-term-form.schema.ts +++ b/modules/catalogs/src/web/settings/payment-terms/entities/payment-term-form.schema.ts @@ -28,9 +28,7 @@ export const PaymentTermFormSchema = z name: z.string().trim().min(1, "El nombre es obligatorio."), description: z.string().trim(), isActive: z.boolean(), - dueRules: z - .array(PaymentTermDueRuleFormSchema) - .min(1, "Debes añadir al menos un vencimiento."), + dueRules: z.array(PaymentTermDueRuleFormSchema).min(1, "Debes añadir al menos un vencimiento."), }) .superRefine((value, ctx) => { const seenDueDays = new Map(); diff --git a/modules/catalogs/src/web/settings/payment-terms/ui/components/index.ts b/modules/catalogs/src/web/settings/payment-terms/ui/components/index.ts index f338f710..06a5794c 100644 --- a/modules/catalogs/src/web/settings/payment-terms/ui/components/index.ts +++ b/modules/catalogs/src/web/settings/payment-terms/ui/components/index.ts @@ -1,5 +1,5 @@ -export * from "./payment-term-form-dialog"; export * from "./delete-payment-term-dialog"; +export * from "./payment-term-form-dialog"; export * from "./payment-term-list-item"; export * from "./payment-term-row-actions"; export * from "./payment-term-segmented-bar"; diff --git a/modules/catalogs/src/web/settings/payment-terms/ui/components/payment-terms-no-results-state.tsx b/modules/catalogs/src/web/settings/payment-terms/ui/components/payment-terms-no-results-state.tsx index 3cfbc0bb..cb96f728 100644 --- a/modules/catalogs/src/web/settings/payment-terms/ui/components/payment-terms-no-results-state.tsx +++ b/modules/catalogs/src/web/settings/payment-terms/ui/components/payment-terms-no-results-state.tsx @@ -2,11 +2,7 @@ import { Button } from "@repo/shadcn-ui/components"; import { useTranslation } from "../../../../i18n"; -export const PaymentTermsNoResultsState = ({ - onClear, -}: { - onClear: () => void; -}) => { +export const PaymentTermsNoResultsState = ({ onClear }: { onClear: () => void }) => { const { t } = useTranslation(); return ( diff --git a/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-form.ts b/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-form.ts index 740c27d4..6aea5407 100644 --- a/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-form.ts +++ b/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-form.ts @@ -1,5 +1,6 @@ import type { PaymentTerm } from "../../../payment-terms/shared"; import type { PaymentTermForm } from "../entities"; + import { formatPaymentTermPercentageInput, sortPaymentTermDueRules, diff --git a/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-params.ts b/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-params.ts index d046268a..adc87e24 100644 --- a/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-params.ts +++ b/modules/catalogs/src/web/settings/payment-terms/utils/build-payment-term-params.ts @@ -1,5 +1,9 @@ -import type { CreatePaymentTermParams, UpdatePaymentTermParams } from "../../../payment-terms/shared"; +import type { + CreatePaymentTermParams, + UpdatePaymentTermParams, +} from "../../../payment-terms/shared"; import type { PaymentTermForm } from "../entities"; + import { parsePaymentTermDueDays, parsePaymentTermPercentage, diff --git a/modules/catalogs/src/web/settings/payment-terms/utils/payment-term-due-rules.utils.test.ts b/modules/catalogs/src/web/settings/payment-terms/utils/payment-term-due-rules.utils.test.ts index 3c6a4d66..9cadfcc0 100644 --- a/modules/catalogs/src/web/settings/payment-terms/utils/payment-term-due-rules.utils.test.ts +++ b/modules/catalogs/src/web/settings/payment-terms/utils/payment-term-due-rules.utils.test.ts @@ -63,9 +63,7 @@ test("calcula 100,00% con 33,33 + 33,33 + 33,34", () => { test("calcula 100,00% con cuatro reglas al 25%", () => { assert.equal( - calculatePaymentTermDueRulesTotalPercentageInHundredths( - buildDueRules("25", "25", "25", "25") - ), + calculatePaymentTermDueRulesTotalPercentageInHundredths(buildDueRules("25", "25", "25", "25")), 10000 ); }); diff --git a/modules/catalogs/src/web/settings/tax-regimes/controllers/use-tax-regimes-settings-controller.ts b/modules/catalogs/src/web/settings/tax-regimes/controllers/use-tax-regimes-settings-controller.ts index 2a22b45e..5ee84eca 100644 --- a/modules/catalogs/src/web/settings/tax-regimes/controllers/use-tax-regimes-settings-controller.ts +++ b/modules/catalogs/src/web/settings/tax-regimes/controllers/use-tax-regimes-settings-controller.ts @@ -1,15 +1,19 @@ import { applyValidationErrorCollection } from "@erp/core"; import { getUserErrorMessage } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; -import { UniqueID, ValidationErrorCollection, isValidationErrorCollection } from "@repo/rdx-ddd"; +import { + UniqueID, + type ValidationErrorCollection, + isValidationErrorCollection, +} from "@repo/rdx-ddd"; import { focusFirstInputFormError, showErrorToast, showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; -import { useCallback, useEffect, useId, useState } from "react"; import type { FormEvent } from "react"; +import { useCallback, useEffect, useId, useState } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../../i18n"; @@ -25,7 +29,10 @@ import { import type { TaxRegimeForm } from "../entities"; import { TaxRegimeFormSchema } from "../entities"; import { buildTaxRegimeFormDefault, buildTaxRegimeFormFromEntity } from "../utils"; -import { buildCreateTaxRegimeParams, buildUpdateTaxRegimeParams } from "../utils/build-tax-regime-params"; +import { + buildCreateTaxRegimeParams, + buildUpdateTaxRegimeParams, +} from "../utils/build-tax-regime-params"; const normalizeFormSubmitError = (error: unknown): Error | ValidationErrorCollection => { if (isValidationErrorCollection(error)) { @@ -132,49 +139,46 @@ export const useTaxRegimesSettingsController = () => { [form, t] ); - const submitTaxRegime = form.handleSubmit( - async (values) => { - try { - if (editingTaxRegime) { - await updateMutation.mutateAsync(buildUpdateTaxRegimeParams(editingTaxRegime.id, values)); - - showSuccessToast( - t("settings.taxRegimes.messages.updatedTitle"), - t("settings.taxRegimes.messages.updated") - ); - - closeEditDialog(); - return; - } - - const id = UniqueID.generateNewID().toString(); - await createMutation.mutateAsync(buildCreateTaxRegimeParams(id, values)); + const submitTaxRegime = form.handleSubmit(async (values) => { + try { + if (editingTaxRegime) { + await updateMutation.mutateAsync(buildUpdateTaxRegimeParams(editingTaxRegime.id, values)); showSuccessToast( - t("settings.taxRegimes.messages.createdTitle"), - t("settings.taxRegimes.messages.created") + t("settings.taxRegimes.messages.updatedTitle"), + t("settings.taxRegimes.messages.updated") ); - closeCreateDialog(); - } catch (error: unknown) { - const normalizedError = normalizeFormSubmitError(error); - - if (isValidationErrorCollection(normalizedError)) { - applyValidationErrorCollection(form, normalizedError); - focusFirstInputFormError(form.formState.errors, form); - showWarningToast( - t("settings.taxRegimes.messages.validationTitle"), - t("settings.taxRegimes.messages.validationDescription") - ); - return; - } - - const userMessage = getUserErrorMessage(normalizedError); - showErrorToast(userMessage.title, userMessage.description); + closeEditDialog(); + return; } - }, - onInvalidSubmit - ); + + const id = UniqueID.generateNewID().toString(); + await createMutation.mutateAsync(buildCreateTaxRegimeParams(id, values)); + + showSuccessToast( + t("settings.taxRegimes.messages.createdTitle"), + t("settings.taxRegimes.messages.created") + ); + + closeCreateDialog(); + } catch (error: unknown) { + const normalizedError = normalizeFormSubmitError(error); + + if (isValidationErrorCollection(normalizedError)) { + applyValidationErrorCollection(form, normalizedError); + focusFirstInputFormError(form.formState.errors, form); + showWarningToast( + t("settings.taxRegimes.messages.validationTitle"), + t("settings.taxRegimes.messages.validationDescription") + ); + return; + } + + const userMessage = getUserErrorMessage(normalizedError); + showErrorToast(userMessage.title, userMessage.description); + } + }, onInvalidSubmit); const onSubmit = useCallback( (event: FormEvent) => { diff --git a/modules/catalogs/src/web/settings/tax-regimes/entities/tax-regime-form.schema.ts b/modules/catalogs/src/web/settings/tax-regimes/entities/tax-regime-form.schema.ts index 437dd500..f60ac6df 100644 --- a/modules/catalogs/src/web/settings/tax-regimes/entities/tax-regime-form.schema.ts +++ b/modules/catalogs/src/web/settings/tax-regimes/entities/tax-regime-form.schema.ts @@ -1,7 +1,10 @@ import { z } from "zod/v4"; export const TaxRegimeFormSchema = z.object({ - code: z.string().trim().regex(/^\d{2}$/), + code: z + .string() + .trim() + .regex(/^\d{2}$/), description: z.string().trim().min(1), isActive: z.boolean(), }); diff --git a/modules/catalogs/src/web/settings/tax-regimes/ui/components/index.ts b/modules/catalogs/src/web/settings/tax-regimes/ui/components/index.ts index 94009a8f..b8082ed9 100644 --- a/modules/catalogs/src/web/settings/tax-regimes/ui/components/index.ts +++ b/modules/catalogs/src/web/settings/tax-regimes/ui/components/index.ts @@ -1,5 +1,5 @@ -export * from "./tax-regime-form-dialog"; export * from "./delete-tax-regime-dialog"; +export * from "./tax-regime-form-dialog"; export * from "./tax-regime-row-actions"; export * from "./tax-regimes-empty-state"; export * from "./tax-regimes-settings-skeleton"; diff --git a/modules/catalogs/src/web/settings/tax-regimes/ui/components/tax-regime-form-dialog.tsx b/modules/catalogs/src/web/settings/tax-regimes/ui/components/tax-regime-form-dialog.tsx index 53279917..70c03aee 100644 --- a/modules/catalogs/src/web/settings/tax-regimes/ui/components/tax-regime-form-dialog.tsx +++ b/modules/catalogs/src/web/settings/tax-regimes/ui/components/tax-regime-form-dialog.tsx @@ -1,8 +1,3 @@ -import { useEffect } from "react"; -import type { FormEventHandler } from "react"; -import { FormProvider } from "react-hook-form"; -import type { UseFormReturn } from "react-hook-form"; - import { RdxFormGrid, SwitchField, TextField } from "@repo/rdx-ui/components"; import { Button, @@ -14,6 +9,10 @@ import { DialogTitle, Spinner, } from "@repo/shadcn-ui/components"; +import type { FormEventHandler } from "react"; +import { useEffect } from "react"; +import type { UseFormReturn } from "react-hook-form"; +import { FormProvider } from "react-hook-form"; import { useTranslation } from "../../../../i18n"; import type { TaxRegimeForm } from "../../entities"; diff --git a/modules/catalogs/src/web/settings/tax-regimes/ui/index.ts b/modules/catalogs/src/web/settings/tax-regimes/ui/index.ts index 566fb90c..90d54b57 100644 --- a/modules/catalogs/src/web/settings/tax-regimes/ui/index.ts +++ b/modules/catalogs/src/web/settings/tax-regimes/ui/index.ts @@ -1,2 +1,2 @@ -export * from "./pages"; export * from "./components"; +export * from "./pages"; diff --git a/modules/catalogs/src/web/tax-regimes/shared/adapters/index.ts b/modules/catalogs/src/web/tax-regimes/shared/adapters/index.ts index 167d9fe1..7862db7d 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/adapters/index.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/adapters/index.ts @@ -1,2 +1,2 @@ -export * from "./list-tax-regimes.adapter"; export * from "./get-tax-regime-by-id.adapter"; +export * from "./list-tax-regimes.adapter"; diff --git a/modules/catalogs/src/web/tax-regimes/shared/api/get-tax-regime-by-id.api.ts b/modules/catalogs/src/web/tax-regimes/shared/api/get-tax-regime-by-id.api.ts index 4a62c32a..ae4233cd 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/api/get-tax-regime-by-id.api.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/api/get-tax-regime-by-id.api.ts @@ -11,7 +11,11 @@ export function getTaxRegimeById( dataSource: IDataSource, params: GetTaxRegimeByIdParams ): Promise { - return dataSource.getOne("catalogs/tax-regimes", params.taxRegimeId, { - signal: params.signal, - }); + return dataSource.getOne( + "catalogs/tax-regimes", + params.taxRegimeId, + { + signal: params.signal, + } + ); } diff --git a/modules/catalogs/src/web/tax-regimes/shared/api/index.ts b/modules/catalogs/src/web/tax-regimes/shared/api/index.ts index 98d183b5..530fec4c 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/api/index.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/api/index.ts @@ -1,7 +1,7 @@ -export * from "./list-tax-regimes-by-criteria.api"; -export * from "./get-tax-regime-by-id.api"; export * from "./create-tax-regime.api"; -export * from "./update-tax-regime.api"; export * from "./delete-tax-regime.api"; -export * from "./enable-tax-regime.api"; export * from "./disable-tax-regime.api"; +export * from "./enable-tax-regime.api"; +export * from "./get-tax-regime-by-id.api"; +export * from "./list-tax-regimes-by-criteria.api"; +export * from "./update-tax-regime.api"; diff --git a/modules/catalogs/src/web/tax-regimes/shared/api/update-tax-regime.api.ts b/modules/catalogs/src/web/tax-regimes/shared/api/update-tax-regime.api.ts index f4600218..e70c90f7 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/api/update-tax-regime.api.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/api/update-tax-regime.api.ts @@ -1,6 +1,9 @@ import type { IDataSource } from "@erp/core/client"; -import type { UpdateTaxRegimeByIdRequestDTO, UpdateTaxRegimeByIdResponseDTO } from "../../../../common"; +import type { + UpdateTaxRegimeByIdRequestDTO, + UpdateTaxRegimeByIdResponseDTO, +} from "../../../../common"; export interface UpdateTaxRegimeParams { taxRegimeId: string; diff --git a/modules/catalogs/src/web/tax-regimes/shared/hooks/index.ts b/modules/catalogs/src/web/tax-regimes/shared/hooks/index.ts index 0d4e6c4e..01589aab 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/hooks/index.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/hooks/index.ts @@ -1,7 +1,7 @@ -export * from "./use-tax-regime-list-query"; -export * from "./use-tax-regime-detail-query"; export * from "./use-tax-regime-create-mutation"; -export * from "./use-tax-regime-update-mutation"; export * from "./use-tax-regime-delete-mutation"; -export * from "./use-tax-regime-enable-mutation"; +export * from "./use-tax-regime-detail-query"; export * from "./use-tax-regime-disable-mutation"; +export * from "./use-tax-regime-enable-mutation"; +export * from "./use-tax-regime-list-query"; +export * from "./use-tax-regime-update-mutation"; diff --git a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-create-mutation.ts b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-create-mutation.ts index d9cac9de..6f8e281b 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-create-mutation.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-create-mutation.ts @@ -4,7 +4,7 @@ import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react- import { CreateTaxRegimeRequestSchema } from "../../../../common"; import { GetTaxRegimeByIdAdapter } from "../adapters"; -import { createTaxRegime, type CreateTaxRegimeParams } from "../api"; +import { type CreateTaxRegimeParams, createTaxRegime } from "../api"; import type { TaxRegime } from "../entities"; import { diff --git a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-delete-mutation.ts b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-delete-mutation.ts index 4f1b07d0..2acca6cc 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-delete-mutation.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-delete-mutation.ts @@ -1,7 +1,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; -import { deleteTaxRegime, type DeleteTaxRegimeParams } from "../api"; +import { type DeleteTaxRegimeParams, deleteTaxRegime } from "../api"; import { DELETE_TAX_REGIME_MUTATION_KEY, diff --git a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-disable-mutation.ts b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-disable-mutation.ts index eb72466b..b0051cd8 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-disable-mutation.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-disable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetTaxRegimeByIdAdapter } from "../adapters"; -import { disableTaxRegime, type DisableTaxRegimeParams } from "../api"; +import { type DisableTaxRegimeParams, disableTaxRegime } from "../api"; import type { TaxRegime } from "../entities"; import { diff --git a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-enable-mutation.ts b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-enable-mutation.ts index 28f545f5..2ebff195 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-enable-mutation.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/hooks/use-tax-regime-enable-mutation.ts @@ -2,7 +2,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { GetTaxRegimeByIdAdapter } from "../adapters"; -import { enableTaxRegime, type EnableTaxRegimeParams } from "../api"; +import { type EnableTaxRegimeParams, enableTaxRegime } from "../api"; import type { TaxRegime } from "../entities"; import { diff --git a/modules/catalogs/src/web/tax-regimes/shared/index.ts b/modules/catalogs/src/web/tax-regimes/shared/index.ts index d2bc2683..01b46929 100644 --- a/modules/catalogs/src/web/tax-regimes/shared/index.ts +++ b/modules/catalogs/src/web/tax-regimes/shared/index.ts @@ -1,4 +1,4 @@ +export * from "./adapters"; +export * from "./api"; export * from "./entities"; export * from "./hooks"; -export * from "./api"; -export * from "./adapters"; diff --git a/modules/companies/package.json b/modules/companies/package.json index f7db99fe..0bfcc4ba 100644 --- a/modules/companies/package.json +++ b/modules/companies/package.json @@ -51,4 +51,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/companies/src/api/application/companies/index.ts b/modules/companies/src/api/application/companies/index.ts index 9a94be2f..475871b1 100644 --- a/modules/companies/src/api/application/companies/index.ts +++ b/modules/companies/src/api/application/companies/index.ts @@ -1,4 +1,4 @@ export * from "./contracts"; +export * from "./public"; export * from "./services"; export * from "./use-cases"; -export * from "./public"; diff --git a/modules/companies/src/api/application/companies/mappers/company-address.mapper.ts b/modules/companies/src/api/application/companies/mappers/company-address.mapper.ts index b02e92db..685ac02e 100644 --- a/modules/companies/src/api/application/companies/mappers/company-address.mapper.ts +++ b/modules/companies/src/api/application/companies/mappers/company-address.mapper.ts @@ -13,17 +13,29 @@ import type { CompanyAddressRequestDTO } from "../../../../common"; export function toPostalAddress(dto: CompanyAddressRequestDTO): Result { const streetResult = Street.create(dto.street); - if (streetResult.isFailure) return Result.fail(streetResult.error); + if (streetResult.isFailure) { + return Result.fail(streetResult.error); + } const street2Result = maybeFromNullableResult(dto.street_2, (value) => Street.create(value)); - if (street2Result.isFailure) return Result.fail(street2Result.error); + if (street2Result.isFailure) { + return Result.fail(street2Result.error); + } const cityResult = City.create(dto.city); - if (cityResult.isFailure) return Result.fail(cityResult.error); + if (cityResult.isFailure) { + return Result.fail(cityResult.error); + } const postalCodeResult = PostalCode.create(dto.postal_code); - if (postalCodeResult.isFailure) return Result.fail(postalCodeResult.error); + if (postalCodeResult.isFailure) { + return Result.fail(postalCodeResult.error); + } const provinceResult = Province.create(dto.province); - if (provinceResult.isFailure) return Result.fail(provinceResult.error); + if (provinceResult.isFailure) { + return Result.fail(provinceResult.error); + } const countryCodeResult = CountryCode.create(dto.country_code); - if (countryCodeResult.isFailure) return Result.fail(countryCodeResult.error); + if (countryCodeResult.isFailure) { + return Result.fail(countryCodeResult.error); + } return PostalAddress.create({ street: Maybe.some(streetResult.data), street2: street2Result.data, diff --git a/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.test.ts b/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.test.ts index 68cbb741..a58ccdcb 100644 --- a/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.test.ts +++ b/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.test.ts @@ -25,7 +25,9 @@ const dto = { test("maps complete create input and optional address", () => { const result = mapper.map(dto); assert.equal(result.isSuccess, true); - if (result.isFailure) return; + if (result.isFailure) { + return; + } assert.equal(result.data.id.toPrimitive(), dto.id); assert.equal(result.data.props.legalName.toPrimitive(), dto.legal_name); assert.equal(result.data.props.tradeName.unwrap().toPrimitive(), dto.trade_name); @@ -36,7 +38,9 @@ test("maps absent and null address to none", () => { for (const address of [undefined, null]) { const result = mapper.map({ ...dto, address }); assert.equal(result.isSuccess, true); - if (result.isSuccess) assert.equal(result.data.props.address.isNone(), true); + if (result.isSuccess) { + assert.equal(result.data.props.address.isNone(), true); + } } }); test("returns failure for invalid value object", () => { diff --git a/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.ts b/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.ts index 04b54ca6..4fa10372 100644 --- a/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.ts +++ b/modules/companies/src/api/application/companies/mappers/create-company-input.mapper.ts @@ -25,31 +25,49 @@ export type CreateCompanyInput = { id: UniqueID; props: ICompanyCreateProps }; export class CreateCompanyInputMapper implements ICreateCompanyInputMapper { public map(dto: CreateCompanyRequestDTO): Result { const idResult = UniqueID.create(dto.id); - if (idResult.isFailure) return Result.fail(idResult.error); + if (idResult.isFailure) { + return Result.fail(idResult.error); + } const legalNameResult = Name.create(dto.legal_name); - if (legalNameResult.isFailure) return Result.fail(legalNameResult.error); + if (legalNameResult.isFailure) { + return Result.fail(legalNameResult.error); + } const tradeNameResult = maybeFromNullableResult(dto.trade_name ?? null, (value) => Name.create(value) ); - if (tradeNameResult.isFailure) return Result.fail(tradeNameResult.error); + if (tradeNameResult.isFailure) { + return Result.fail(tradeNameResult.error); + } const tinResult = maybeFromNullableResult(dto.tin ?? null, (value) => TextValue.create(value)); - if (tinResult.isFailure) return Result.fail(tinResult.error); + if (tinResult.isFailure) { + return Result.fail(tinResult.error); + } const slugResult = CompanySlug.create(dto.slug); - if (slugResult.isFailure) return Result.fail(slugResult.error); + if (slugResult.isFailure) { + return Result.fail(slugResult.error); + } const emailResult = maybeFromNullableResult(dto.email ?? null, (value) => EmailAddress.create(value) ); - if (emailResult.isFailure) return Result.fail(emailResult.error); + if (emailResult.isFailure) { + return Result.fail(emailResult.error); + } const phoneResult = maybeFromNullableResult(dto.phone ?? null, (value) => PhoneNumber.create(value) ); - if (phoneResult.isFailure) return Result.fail(phoneResult.error); + if (phoneResult.isFailure) { + return Result.fail(phoneResult.error); + } const websiteResult = maybeFromNullableResult(dto.website ?? null, (value) => URLAddress.create(value) ); - if (websiteResult.isFailure) return Result.fail(websiteResult.error); + if (websiteResult.isFailure) { + return Result.fail(websiteResult.error); + } const addressResult = this.mapAddress(dto); - if (addressResult.isFailure) return Result.fail(addressResult.error); + if (addressResult.isFailure) { + return Result.fail(addressResult.error); + } const now = UtcDateTime.now(); return Result.ok({ id: idResult.data, @@ -71,7 +89,9 @@ export class CreateCompanyInputMapper implements ICreateCompanyInputMapper { } private mapAddress(dto: CreateCompanyRequestDTO): Result, Error> { - if (dto.address === undefined || dto.address === null) return Result.ok(Maybe.none()); + if (dto.address === undefined || dto.address === null) { + return Result.ok(Maybe.none()); + } const result = toPostalAddress(dto.address); return result.isFailure ? Result.fail(result.error) : Result.ok(Maybe.some(result.data)); } diff --git a/modules/companies/src/api/application/companies/mappers/update-company-input.mapper.ts b/modules/companies/src/api/application/companies/mappers/update-company-input.mapper.ts index 386f104f..c2f4abdb 100644 --- a/modules/companies/src/api/application/companies/mappers/update-company-input.mapper.ts +++ b/modules/companies/src/api/application/companies/mappers/update-company-input.mapper.ts @@ -38,7 +38,11 @@ export class UpdateCompanyInputMapper implements IUpdateCompanyInputMapper { this.mapAddress(patchProps, dto), this.mapPaymentTermPatch(patchProps, dto), ]; - for (const result of fieldResults) if (result.isFailure) return Result.fail(result.error); + for (const result of fieldResults) { + if (result.isFailure) { + return Result.fail(result.error); + } + } const paymentTermResult = this.mapPaymentTermId(dto); return paymentTermResult.isFailure ? Result.fail(paymentTermResult.error) @@ -46,62 +50,94 @@ export class UpdateCompanyInputMapper implements IUpdateCompanyInputMapper { } private mapLegalName(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.legal_name === undefined) return Result.ok(undefined); + if (d.legal_name === undefined) { + return Result.ok(undefined); + } const r = Name.create(d.legal_name); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.legalName = r.data; return Result.ok(undefined); } private mapTradeName(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.trade_name === undefined) return Result.ok(undefined); + if (d.trade_name === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.trade_name, Name.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.tradeName = r.data; return Result.ok(undefined); } private mapTin(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.tin === undefined) return Result.ok(undefined); + if (d.tin === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.tin, TextValue.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.tin = r.data; return Result.ok(undefined); } private mapSlug(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.slug === undefined) return Result.ok(undefined); + if (d.slug === undefined) { + return Result.ok(undefined); + } const r = CompanySlug.create(d.slug); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.slug = r.data; return Result.ok(undefined); } private mapEmail(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.email === undefined) return Result.ok(undefined); + if (d.email === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.email, EmailAddress.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.email = r.data; return Result.ok(undefined); } private mapPhone(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.phone === undefined) return Result.ok(undefined); + if (d.phone === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.phone, PhoneNumber.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.phone = r.data; return Result.ok(undefined); } private mapWebsite(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.website === undefined) return Result.ok(undefined); + if (d.website === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.website, URLAddress.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.website = r.data; return Result.ok(undefined); } private mapAddress(p: CompanyPatchProps, d: UpdateCompanyRequestDTO): Result { - if (d.address === undefined) return Result.ok(undefined); + if (d.address === undefined) { + return Result.ok(undefined); + } if (d.address === null) { p.address = Maybe.none(); return Result.ok(undefined); } const r = toPostalAddress(d.address); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.address = Maybe.some(r.data); return Result.ok(undefined); } @@ -109,15 +145,23 @@ export class UpdateCompanyInputMapper implements IUpdateCompanyInputMapper { p: CompanyPatchProps, d: UpdateCompanyRequestDTO ): Result { - if (d.default_payment_term_id === undefined) return Result.ok(undefined); + if (d.default_payment_term_id === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.default_payment_term_id, UniqueID.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.defaultPaymentTermId = r.data; return Result.ok(undefined); } private mapPaymentTermId(d: UpdateCompanyRequestDTO): Result | undefined, Error> { - if (d.default_payment_term_id === undefined) return Result.ok(undefined); - if (d.default_payment_term_id === null) return Result.ok(Maybe.none()); + if (d.default_payment_term_id === undefined) { + return Result.ok(undefined); + } + if (d.default_payment_term_id === null) { + return Result.ok(Maybe.none()); + } const r = UniqueID.create(d.default_payment_term_id); return r.isFailure ? Result.fail(r.error) : Result.ok(Maybe.some(r.data)); } diff --git a/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.test.ts b/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.test.ts index c631e30b..dc967db2 100644 --- a/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.test.ts +++ b/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.test.ts @@ -15,10 +15,14 @@ const address = { test("maps current-company PATCH tri-state without slug", () => { const omitted = mapper.map({}); assert.equal(omitted.isSuccess, true); - if (omitted.isSuccess) assert.equal(omitted.data.patchProps.address, undefined); + if (omitted.isSuccess) { + assert.equal(omitted.data.patchProps.address, undefined); + } const cleared = mapper.map({ address: null, default_payment_term_id: null }); assert.equal(cleared.isSuccess, true); - if (cleared.isSuccess) assert.equal(cleared.data.patchProps.address?.isNone(), true); + if (cleared.isSuccess) { + assert.equal(cleared.data.patchProps.address?.isNone(), true); + } const value = mapper.map({ address, default_payment_term_id: "11111111-1111-4111-8111-111111111111", diff --git a/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.ts b/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.ts index eca9add7..5ee4c750 100644 --- a/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.ts +++ b/modules/companies/src/api/application/companies/mappers/update-current-company-input.mapper.ts @@ -35,62 +35,94 @@ export class UpdateCurrentCompanyInputMapper implements IUpdateCurrentCompanyInp this.address(patchProps, dto), this.paymentTermPatch(patchProps, dto), ]; - for (const result of results) if (result.isFailure) return Result.fail(result.error); + for (const result of results) { + if (result.isFailure) { + return Result.fail(result.error); + } + } const paymentTerm = this.paymentTermId(dto); return paymentTerm.isFailure ? Result.fail(paymentTerm.error) : Result.ok({ patchProps, defaultPaymentTermId: paymentTerm.data }); } private legalName(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.legal_name === undefined) return Result.ok(undefined); + if (d.legal_name === undefined) { + return Result.ok(undefined); + } const r = Name.create(d.legal_name); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.legalName = r.data; return Result.ok(undefined); } private tradeName(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.trade_name === undefined) return Result.ok(undefined); + if (d.trade_name === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.trade_name, Name.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.tradeName = r.data; return Result.ok(undefined); } private tin(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.tin === undefined) return Result.ok(undefined); + if (d.tin === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.tin, TextValue.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.tin = r.data; return Result.ok(undefined); } private email(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.email === undefined) return Result.ok(undefined); + if (d.email === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.email, EmailAddress.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.email = r.data; return Result.ok(undefined); } private phone(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.phone === undefined) return Result.ok(undefined); + if (d.phone === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.phone, PhoneNumber.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.phone = r.data; return Result.ok(undefined); } private website(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.website === undefined) return Result.ok(undefined); + if (d.website === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.website, URLAddress.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.website = r.data; return Result.ok(undefined); } private address(p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO): Result { - if (d.address === undefined) return Result.ok(undefined); + if (d.address === undefined) { + return Result.ok(undefined); + } if (d.address === null) { p.address = Maybe.none(); return Result.ok(undefined); } const r = toPostalAddress(d.address); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.address = Maybe.some(r.data); return Result.ok(undefined); } @@ -98,17 +130,25 @@ export class UpdateCurrentCompanyInputMapper implements IUpdateCurrentCompanyInp p: CompanyPatchProps, d: UpdateCurrentCompanyRequestDTO ): Result { - if (d.default_payment_term_id === undefined) return Result.ok(undefined); + if (d.default_payment_term_id === undefined) { + return Result.ok(undefined); + } const r = maybeFromNullableResult(d.default_payment_term_id, UniqueID.create); - if (r.isFailure) return Result.fail(r.error); + if (r.isFailure) { + return Result.fail(r.error); + } p.defaultPaymentTermId = r.data; return Result.ok(undefined); } private paymentTermId( d: UpdateCurrentCompanyRequestDTO ): Result | undefined, Error> { - if (d.default_payment_term_id === undefined) return Result.ok(undefined); - if (d.default_payment_term_id === null) return Result.ok(Maybe.none()); + if (d.default_payment_term_id === undefined) { + return Result.ok(undefined); + } + if (d.default_payment_term_id === null) { + return Result.ok(Maybe.none()); + } const r = UniqueID.create(d.default_payment_term_id); return r.isFailure ? Result.fail(r.error) : Result.ok(Maybe.some(r.data)); } diff --git a/modules/companies/src/api/application/companies/use-cases/create-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/create-company.use-case.ts index 94ac992e..6a482e1c 100644 --- a/modules/companies/src/api/application/companies/use-cases/create-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/create-company.use-case.ts @@ -19,7 +19,9 @@ export class CreateCompanyUseCase { dto: CreateCompanyRequestDTO; }): Promise> { const input = this.deps.inputMapper.map(params.dto); - if (input.isFailure) return Promise.resolve(Result.fail(input.error)); + if (input.isFailure) { + return Promise.resolve(Result.fail(input.error)); + } return this.deps.transactionManager.complete(async (transaction) => { try { const result = await this.deps.creator.create({ diff --git a/modules/companies/src/api/application/companies/use-cases/disable-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/disable-company.use-case.ts index e371ed8a..1ef6492a 100644 --- a/modules/companies/src/api/application/companies/use-cases/disable-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/disable-company.use-case.ts @@ -25,7 +25,10 @@ export class DisableCompanyUseCase { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const findResult = await this.deps.finder.findCompanyById(companyIdResult.data, transaction); + const findResult = await this.deps.finder.findCompanyById( + companyIdResult.data, + transaction + ); if (findResult.isFailure) { return Result.fail(findResult.error); } @@ -40,7 +43,9 @@ export class DisableCompanyUseCase { return Result.fail(changeResult.error); } - return Result.ok(toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(changeResult.data))); + return Result.ok( + toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(changeResult.data)) + ); } catch (error: unknown) { return Result.fail(error as Error); } diff --git a/modules/companies/src/api/application/companies/use-cases/enable-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/enable-company.use-case.ts index 73dddaeb..cc56536c 100644 --- a/modules/companies/src/api/application/companies/use-cases/enable-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/enable-company.use-case.ts @@ -25,7 +25,10 @@ export class EnableCompanyUseCase { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const findResult = await this.deps.finder.findCompanyById(companyIdResult.data, transaction); + const findResult = await this.deps.finder.findCompanyById( + companyIdResult.data, + transaction + ); if (findResult.isFailure) { return Result.fail(findResult.error); } @@ -40,7 +43,9 @@ export class EnableCompanyUseCase { return Result.fail(changeResult.error); } - return Result.ok(toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(changeResult.data))); + return Result.ok( + toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(changeResult.data)) + ); } catch (error: unknown) { return Result.fail(error as Error); } diff --git a/modules/companies/src/api/application/companies/use-cases/get-company-by-id.use-case.ts b/modules/companies/src/api/application/companies/use-cases/get-company-by-id.use-case.ts index 5a04849f..5dbeb6ec 100644 --- a/modules/companies/src/api/application/companies/use-cases/get-company-by-id.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/get-company-by-id.use-case.ts @@ -24,12 +24,17 @@ export class GetCompanyByIdUseCase { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const companyResult = await this.deps.finder.findCompanyById(companyIdResult.data, transaction); + const companyResult = await this.deps.finder.findCompanyById( + companyIdResult.data, + transaction + ); if (companyResult.isFailure) { return Result.fail(companyResult.error); } - return Result.ok(toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(companyResult.data))); + return Result.ok( + toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(companyResult.data)) + ); } catch (error: unknown) { return Result.fail(error as Error); } diff --git a/modules/companies/src/api/application/companies/use-cases/get-current-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/get-current-company.use-case.ts index 3e151c52..a9d874b2 100644 --- a/modules/companies/src/api/application/companies/use-cases/get-current-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/get-current-company.use-case.ts @@ -24,12 +24,17 @@ export class GetCurrentCompanyUseCase { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const companyResult = await this.deps.finder.findCompanyById(companyIdResult.data, transaction); + const companyResult = await this.deps.finder.findCompanyById( + companyIdResult.data, + transaction + ); if (companyResult.isFailure) { return Result.fail(companyResult.error); } - return Result.ok(toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(companyResult.data))); + return Result.ok( + toCompanyResponseDTO(this.deps.responseMapper.toPublicModel(companyResult.data)) + ); } catch (error: unknown) { return Result.fail(error as Error); } diff --git a/modules/companies/src/api/application/companies/use-cases/index.ts b/modules/companies/src/api/application/companies/use-cases/index.ts index a6865658..1be6b719 100644 --- a/modules/companies/src/api/application/companies/use-cases/index.ts +++ b/modules/companies/src/api/application/companies/use-cases/index.ts @@ -4,5 +4,5 @@ export * from "./enable-company.use-case"; export * from "./find-available-companies-for-account.use-case"; export * from "./get-company-by-id.use-case"; export * from "./get-current-company.use-case"; -export * from "./update-current-company.use-case"; export * from "./update-company.use-case"; +export * from "./update-current-company.use-case"; diff --git a/modules/companies/src/api/application/companies/use-cases/update-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/update-company.use-case.ts index ce7f7b86..802b7d1f 100644 --- a/modules/companies/src/api/application/companies/use-cases/update-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/update-company.use-case.ts @@ -22,9 +22,13 @@ export class UpdateCompanyUseCase { dto: UpdateCompanyRequestDTO; }): Promise> { const companyId = UniqueID.create(params.company_id); - if (companyId.isFailure) return Promise.resolve(Result.fail(companyId.error)); + if (companyId.isFailure) { + return Promise.resolve(Result.fail(companyId.error)); + } const input = this.deps.inputMapper.map(params.dto); - if (input.isFailure) return Promise.resolve(Result.fail(input.error)); + if (input.isFailure) { + return Promise.resolve(Result.fail(input.error)); + } return this.deps.transactionManager.complete(async (transaction) => { try { if (input.data.defaultPaymentTermId !== undefined) { @@ -33,7 +37,9 @@ export class UpdateCompanyUseCase { id: input.data.defaultPaymentTermId, transaction, }); - if (result.isFailure) return Result.fail(result.error); + if (result.isFailure) { + return Result.fail(result.error); + } } const result = await this.deps.updater.update({ id: companyId.data, diff --git a/modules/companies/src/api/application/companies/use-cases/update-current-company.use-case.ts b/modules/companies/src/api/application/companies/use-cases/update-current-company.use-case.ts index eacc38e2..927ebef3 100644 --- a/modules/companies/src/api/application/companies/use-cases/update-current-company.use-case.ts +++ b/modules/companies/src/api/application/companies/use-cases/update-current-company.use-case.ts @@ -22,9 +22,13 @@ export class UpdateCurrentCompanyUseCase { dto: UpdateCurrentCompanyRequestDTO; }): Promise> { const companyId = UniqueID.create(params.companyId); - if (companyId.isFailure) return Promise.resolve(Result.fail(companyId.error)); + if (companyId.isFailure) { + return Promise.resolve(Result.fail(companyId.error)); + } const input = this.deps.inputMapper.map(params.dto); - if (input.isFailure) return Promise.resolve(Result.fail(input.error)); + if (input.isFailure) { + return Promise.resolve(Result.fail(input.error)); + } return this.deps.transactionManager.complete(async (transaction) => { try { if (input.data.defaultPaymentTermId !== undefined) { @@ -33,7 +37,9 @@ export class UpdateCurrentCompanyUseCase { id: input.data.defaultPaymentTermId, transaction, }); - if (result.isFailure) return Result.fail(result.error); + if (result.isFailure) { + return Result.fail(result.error); + } } const result = await this.deps.updater.update({ id: companyId.data, diff --git a/modules/companies/src/api/domain/companies/company-status.vo.ts b/modules/companies/src/api/domain/companies/company-status.vo.ts index f9cf90fc..5ea4cdce 100644 --- a/modules/companies/src/api/domain/companies/company-status.vo.ts +++ b/modules/companies/src/api/domain/companies/company-status.vo.ts @@ -11,12 +11,23 @@ export enum COMPANY_STATUS { } export class CompanyStatus extends ValueObject { - private static readonly ALLOWED_STATUSES = [COMPANY_STATUS.ACTIVE, COMPANY_STATUS.DISABLED] as const; + private static readonly ALLOWED_STATUSES = [ + COMPANY_STATUS.ACTIVE, + COMPANY_STATUS.DISABLED, + ] as const; static create(value: string): Result { - if (!CompanyStatus.ALLOWED_STATUSES.includes(value as (typeof CompanyStatus.ALLOWED_STATUSES)[number])) { + if ( + !CompanyStatus.ALLOWED_STATUSES.includes( + value as (typeof CompanyStatus.ALLOWED_STATUSES)[number] + ) + ) { return Result.fail( - new DomainValidationError("INVALID_COMPANY_STATUS", "status", `Status value not valid: ${value}`) + new DomainValidationError( + "INVALID_COMPANY_STATUS", + "status", + `Status value not valid: ${value}` + ) ); } diff --git a/modules/companies/src/api/domain/companies/index.ts b/modules/companies/src/api/domain/companies/index.ts index 5ca9e4f2..1e2f8650 100644 --- a/modules/companies/src/api/domain/companies/index.ts +++ b/modules/companies/src/api/domain/companies/index.ts @@ -1,4 +1,4 @@ -export * from "./company-status.vo"; -export * from "./company-slug.vo"; export * from "./company.aggregate"; +export * from "./company-slug.vo"; +export * from "./company-status.vo"; export * from "./errors"; diff --git a/modules/companies/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-domain.mapper.ts b/modules/companies/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-domain.mapper.ts index d2dd5204..a6f8674b 100644 --- a/modules/companies/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-domain.mapper.ts +++ b/modules/companies/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-domain.mapper.ts @@ -112,7 +112,9 @@ export class SequelizeCompanyDomainMapper extends SequelizeDomainMapper< errors ); const address = mapAddress(source); - if (address.isFailure) return Result.fail(address.error); + if (address.isFailure) { + return Result.fail(address.error); + } const status = extractOrPushError(CompanyStatus.create(source.status), "status", errors); const createdAt = extractOrPushError( UtcDateTime.createFromISO(toUtcIsoString(source.created_at)), diff --git a/modules/companies/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company.repository.ts b/modules/companies/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company.repository.ts index 8af6d87a..f0e8b020 100644 --- a/modules/companies/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company.repository.ts +++ b/modules/companies/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company.repository.ts @@ -1,8 +1,8 @@ import { SequelizeRepository, translateSequelizeError } from "@erp/core/api"; -import { Op } from "sequelize"; import type { UniqueID } from "@repo/rdx-ddd"; import { Collection, Maybe, Result } from "@repo/rdx-utils"; import type { Sequelize, Transaction } from "sequelize"; +import { Op } from "sequelize"; import type { ICompanyRepository } from "../../../../application"; import { COMPANY_STATUS, type Company } from "../../../../domain"; @@ -137,7 +137,9 @@ export class SequelizeCompanyRepository transaction?: unknown; }): Promise, Error>> { try { - const ids = [...new Set(params.companyIds.getAll().map((companyId) => companyId.toPrimitive()))]; + const ids = [ + ...new Set(params.companyIds.getAll().map((companyId) => companyId.toPrimitive())), + ]; if (ids.length === 0) { return Result.ok(new Collection([])); diff --git a/modules/companies/src/api/module.test.ts b/modules/companies/src/api/module.test.ts index 540cdc81..59a4b1e6 100644 --- a/modules/companies/src/api/module.test.ts +++ b/modules/companies/src/api/module.test.ts @@ -26,8 +26,12 @@ test("companiesModule compone el finder público de compañías sin HTTP", async transactionContext: {}, audit: { recordMutation: async () => ({ isFailure: false }) }, getService: (name: string) => { - if (name === "identity:general") return { companyAccess: {} }; - if (name === "catalogs:paymentTerms") return { finder: {} }; + if (name === "identity:general") { + return { companyAccess: {} }; + } + if (name === "catalogs:paymentTerms") { + return { finder: {} }; + } throw new Error(`Unexpected public service: ${name}`); }, logger: { info: () => undefined }, diff --git a/modules/companies/src/common/dto/request/index.ts b/modules/companies/src/common/dto/request/index.ts index 2b147080..afc8e8cb 100644 --- a/modules/companies/src/common/dto/request/index.ts +++ b/modules/companies/src/common/dto/request/index.ts @@ -1,5 +1,5 @@ export * from "./change-company-status.request.dto"; export * from "./create-company.request.dto"; export * from "./get-company-by-id.request.dto"; -export * from "./update-current-company.request.dto"; export * from "./update-company.request.dto"; +export * from "./update-current-company.request.dto"; diff --git a/modules/companies/src/common/dto/response/company.response.dto.test.ts b/modules/companies/src/common/dto/response/company.response.dto.test.ts index 5df2e845..1982a315 100644 --- a/modules/companies/src/common/dto/response/company.response.dto.test.ts +++ b/modules/companies/src/common/dto/response/company.response.dto.test.ts @@ -3,14 +3,35 @@ import test from "node:test"; import { CompanyResponseSchema } from "./company.response.dto"; -const response = (address: unknown) => ({ id: "550e8400-e29b-41d4-a716-446655440000", legal_name: "Rodax", trade_name: null, tin: null, slug: "rodax", email: null, phone: null, website: null, address, default_payment_term_id: null, status: "active" }); -const complete = { street: "Calle Mayor 1", street_2: null, city: "Madrid", postal_code: "28001", province: "Madrid", country_code: "ES" }; +const response = (address: unknown) => ({ + id: "550e8400-e29b-41d4-a716-446655440000", + legal_name: "Rodax", + trade_name: null, + tin: null, + slug: "rodax", + email: null, + phone: null, + website: null, + address, + default_payment_term_id: null, + status: "active", +}); +const complete = { + street: "Calle Mayor 1", + street_2: null, + city: "Madrid", + postal_code: "28001", + province: "Madrid", + country_code: "ES", +}; test("CompanyResponseSchema requires a complete nullable address", () => { assert.equal(CompanyResponseSchema.safeParse(response(null)).success, true); assert.equal(CompanyResponseSchema.safeParse(response(complete)).success, true); - const omitted = response(null); delete (omitted as { address?: unknown }).address; + const omitted = response(null); + (omitted as { address?: unknown }).address = undefined; assert.equal(CompanyResponseSchema.safeParse(omitted).success, false); - const partial = { ...complete }; delete (partial as { country_code?: unknown }).country_code; + const partial = { ...complete }; + (partial as { country_code?: unknown }).country_code = undefined; assert.equal(CompanyResponseSchema.safeParse(response(partial)).success, false); }); diff --git a/modules/companies/src/common/dto/response/company.response.dto.ts b/modules/companies/src/common/dto/response/company.response.dto.ts index 7a7f7391..fd42603e 100644 --- a/modules/companies/src/common/dto/response/company.response.dto.ts +++ b/modules/companies/src/common/dto/response/company.response.dto.ts @@ -1,4 +1,10 @@ -import { CountryCodeSchema, EmailSchema, PostalCodeSchema, TinSchema, WebsiteSchema } from "@erp/core"; +import { + CountryCodeSchema, + EmailSchema, + PostalCodeSchema, + TinSchema, + WebsiteSchema, +} from "@erp/core"; import { z } from "zod/v4"; export const CompanyAddressResponseSchema = z.object({ diff --git a/modules/companies/src/web/company-routes.tsx b/modules/companies/src/web/company-routes.tsx index 8fbc34ea..408d6677 100644 --- a/modules/companies/src/web/company-routes.tsx +++ b/modules/companies/src/web/company-routes.tsx @@ -1,6 +1,5 @@ -import { lazy } from "react"; - import type { ModuleClientParams, ModuleRouteObject } from "@erp/core/client"; +import { lazy } from "react"; const CompanyDetailsSettingsPage = lazy(() => import("./settings/company-details").then((module) => ({ diff --git a/modules/companies/src/web/settings/company-details/entities/company-settings-form.schema.ts b/modules/companies/src/web/settings/company-details/entities/company-settings-form.schema.ts index 1a0ab2f7..4b8f6349 100644 --- a/modules/companies/src/web/settings/company-details/entities/company-settings-form.schema.ts +++ b/modules/companies/src/web/settings/company-details/entities/company-settings-form.schema.ts @@ -25,20 +25,30 @@ const AddressFormSchema = z address.countryCode, ]; const empty = required.every((value) => value.trim() === ""); - if (empty) return; - if (address.street.trim() === "") + if (empty) { + return; + } + if (address.street.trim() === "") { ctx.addIssue({ code: "custom", path: ["street"], message: "Required" }); - if (address.city.trim() === "") + } + if (address.city.trim() === "") { ctx.addIssue({ code: "custom", path: ["city"], message: "Required" }); - if (address.postalCode.trim() === "" || !PostalCodeSchema.safeParse(address.postalCode).success) + } + if ( + address.postalCode.trim() === "" || + !PostalCodeSchema.safeParse(address.postalCode).success + ) { ctx.addIssue({ code: "custom", path: ["postalCode"], message: "Invalid postal code" }); - if (address.province.trim() === "") + } + if (address.province.trim() === "") { ctx.addIssue({ code: "custom", path: ["province"], message: "Required" }); + } if ( address.countryCode.trim() === "" || !CountryCodeSchema.safeParse(address.countryCode).success - ) + ) { ctx.addIssue({ code: "custom", path: ["countryCode"], message: "Invalid country code" }); + } }); export const CompanySettingsFormSchema = z.object({ diff --git a/modules/companies/src/web/settings/company-details/shared/hooks/keys.ts b/modules/companies/src/web/settings/company-details/shared/hooks/keys.ts index 63e76f76..a2a665b8 100644 --- a/modules/companies/src/web/settings/company-details/shared/hooks/keys.ts +++ b/modules/companies/src/web/settings/company-details/shared/hooks/keys.ts @@ -1,7 +1,3 @@ export const COMPANY_SETTINGS_QUERY_KEY = () => ["companies", "settings", "current"] as const; -export const UPDATE_COMPANY_SETTINGS_MUTATION_KEY = [ - "companies", - "settings", - "update", -] as const; +export const UPDATE_COMPANY_SETTINGS_MUTATION_KEY = ["companies", "settings", "update"] as const; diff --git a/modules/companies/src/web/settings/company-details/shared/hooks/use-company-settings-update-mutation.ts b/modules/companies/src/web/settings/company-details/shared/hooks/use-company-settings-update-mutation.ts index 45b1eaff..4cf0089e 100644 --- a/modules/companies/src/web/settings/company-details/shared/hooks/use-company-settings-update-mutation.ts +++ b/modules/companies/src/web/settings/company-details/shared/hooks/use-company-settings-update-mutation.ts @@ -3,8 +3,8 @@ import { ValidationErrorCollection } from "@repo/rdx-ddd"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import { - UpdateCurrentCompanyRequestSchema, type UpdateCurrentCompanyRequestDTO, + UpdateCurrentCompanyRequestSchema, } from "../../../../../common"; import { CompanySettingsAdapter } from "../adapters"; import { updateCompanySettings } from "../api"; @@ -28,7 +28,10 @@ export const useCompanySettingsUpdateMutation = () => { const result = UpdateCurrentCompanyRequestSchema.safeParse(params.data); if (!result.success) { - throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error.issues)); + throw new ValidationErrorCollection( + "Validation failed", + toValidationErrors(result.error.issues) + ); } const dto = await updateCompanySettings(dataSource, params); diff --git a/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.test.ts b/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.test.ts index 77292dc6..4c5f25da 100644 --- a/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.test.ts +++ b/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.test.ts @@ -10,12 +10,10 @@ const translate = (key: string) => "settings.companyDetails.pageError.notFound.title": "No se encontro la empresa activa", "settings.companyDetails.pageError.notFound.description": "La empresa seleccionada ya no existe o no esta disponible.", - "settings.companyDetails.pageError.forbidden.title": - "No tienes acceso a la empresa activa", + "settings.companyDetails.pageError.forbidden.title": "No tienes acceso a la empresa activa", "settings.companyDetails.pageError.forbidden.description": "No dispones de permisos para consultar esta configuracion.", - "settings.companyDetails.pageError.network.title": - "No se pudo conectar con el servidor", + "settings.companyDetails.pageError.network.title": "No se pudo conectar con el servidor", "settings.companyDetails.pageError.network.description": "Comprueba tu conexion e intentalo de nuevo.", "settings.companyDetails.pageError.timeout.title": "La carga esta tardando demasiado", @@ -24,8 +22,7 @@ const translate = (key: string) => "settings.companyDetails.pageError.server.title": "No se pudo cargar la empresa activa", "settings.companyDetails.pageError.server.description": "Se produjo un error interno al recuperar la configuracion de empresa.", - "settings.companyDetails.pageError.unavailable.title": - "Servicio temporalmente no disponible", + "settings.companyDetails.pageError.unavailable.title": "Servicio temporalmente no disponible", "settings.companyDetails.pageError.unavailable.description": "El servicio de empresas no esta disponible en este momento.", "settings.companyDetails.pageError.unknown.title": "No se pudo cargar la empresa activa", diff --git a/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.ts b/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.ts index f27b19a1..7fd2680c 100644 --- a/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.ts +++ b/modules/companies/src/web/settings/company-details/utils/build-company-details-settings-page-error-view-model.ts @@ -2,9 +2,7 @@ import { isApiClientError } from "@erp/core/client"; import type { CompanyDetailsSettingsPageErrorViewModel } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildErrorVariant = ( error: unknown diff --git a/modules/core/package.json b/modules/core/package.json index a37665d3..a887eab5 100644 --- a/modules/core/package.json +++ b/modules/core/package.json @@ -59,4 +59,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/core/src/api/application/documents/services/document-generation-service.ts b/modules/core/src/api/application/documents/services/document-generation-service.ts index edf0ff9e..c2f24c17 100644 --- a/modules/core/src/api/application/documents/services/document-generation-service.ts +++ b/modules/core/src/api/application/documents/services/document-generation-service.ts @@ -76,7 +76,7 @@ export class DocumentGenerationService { if (cachedDoc) { return Result.ok(cachedDoc); } - } catch (error) { + } catch (_error) { // best-effort: ignorar y continuar } } diff --git a/modules/core/src/api/application/mappers/patch-collector.ts b/modules/core/src/api/application/mappers/patch-collector.ts index 5cc98575..5316c3b7 100644 --- a/modules/core/src/api/application/mappers/patch-collector.ts +++ b/modules/core/src/api/application/mappers/patch-collector.ts @@ -69,7 +69,7 @@ export class PatchCollector { } public setBoolean( - path: string, + _path: string, input: boolean | undefined, setter: (value: boolean) => void ): void { diff --git a/modules/core/src/api/application/presenters/presenter-registry.ts b/modules/core/src/api/application/presenters/presenter-registry.ts index 9fb7d3d1..5b95d868 100644 --- a/modules/core/src/api/application/presenters/presenter-registry.ts +++ b/modules/core/src/api/application/presenters/presenter-registry.ts @@ -21,7 +21,7 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry { return [ resource.toLowerCase(), projection.toLowerCase(), - format!.toLowerCase(), + format?.toLowerCase(), version ?? "latest", locale ?? "default", ].join("::"); diff --git a/modules/core/src/api/application/renderers/renderer-registry.ts b/modules/core/src/api/application/renderers/renderer-registry.ts index c618d36f..28b99049 100644 --- a/modules/core/src/api/application/renderers/renderer-registry.ts +++ b/modules/core/src/api/application/renderers/renderer-registry.ts @@ -20,7 +20,7 @@ export class InMemoryRendererRegistry implements IRendererRegistry { const { resource, format, version, locale } = this._normalizeKey(key); return [ resource.toLowerCase(), - format!.toLowerCase(), + format?.toLowerCase(), version ?? "latest", locale ?? "default", ].join("::"); diff --git a/modules/core/src/api/infrastructure/audit/sequelize/audit-change.model.ts b/modules/core/src/api/infrastructure/audit/sequelize/audit-change.model.ts index baa8e8bf..2efe3dce 100644 --- a/modules/core/src/api/infrastructure/audit/sequelize/audit-change.model.ts +++ b/modules/core/src/api/infrastructure/audit/sequelize/audit-change.model.ts @@ -1,12 +1,74 @@ -import { DataTypes, type InferAttributes, type InferCreationAttributes, Model, type Sequelize } from "sequelize"; +import { + DataTypes, + type InferAttributes, + type InferCreationAttributes, + Model, + type Sequelize, +} from "sequelize"; + import { AuditEventModel } from "./audit-event.model"; -export class AuditChangeModel extends Model, InferCreationAttributes> { - declare audit_event_id: string; declare position: number; declare path: string; declare kind: string; declare before_value: unknown | null; declare after_value: unknown | null; - static associate(_database: Sequelize): void { AuditChangeModel.belongsTo(AuditEventModel, { as: "event", foreignKey: "audit_event_id", targetKey: "id" }); } +export class AuditChangeModel extends Model< + InferAttributes, + InferCreationAttributes +> { + declare audit_event_id: string; + declare position: number; + declare path: string; + declare kind: string; + declare before_value: unknown | null; + declare after_value: unknown | null; + static associate(_database: Sequelize): void { + AuditChangeModel.belongsTo(AuditEventModel, { + as: "event", + foreignKey: "audit_event_id", + targetKey: "id", + }); + } } export function initializeAuditChangeModel(database: Sequelize): typeof AuditChangeModel { - AuditChangeModel.init({ audit_event_id: { type: DataTypes.STRING(64), allowNull: false, primaryKey: true, references: { model: "audit_events", key: "id" }, onUpdate: "RESTRICT", onDelete: "CASCADE" }, position: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, primaryKey: true }, path: { type: DataTypes.STRING(255), allowNull: false }, kind: { type: DataTypes.STRING(32), allowNull: false }, before_value: { type: DataTypes.JSON, allowNull: true, defaultValue: null, get(this: AuditChangeModel): unknown | null { return deserialize(this.getDataValue("before_value")); } }, after_value: { type: DataTypes.JSON, allowNull: true, defaultValue: null, get(this: AuditChangeModel): unknown | null { return deserialize(this.getDataValue("after_value")); } } }, { sequelize: database, modelName: "AuditChangeModel", tableName: "audit_changes", charset: "utf8mb4", collate: "utf8mb4_unicode_ci", underscored: true, timestamps: false }); + AuditChangeModel.init( + { + audit_event_id: { + type: DataTypes.STRING(64), + allowNull: false, + primaryKey: true, + references: { model: "audit_events", key: "id" }, + onUpdate: "RESTRICT", + onDelete: "CASCADE", + }, + position: { type: DataTypes.INTEGER.UNSIGNED, allowNull: false, primaryKey: true }, + path: { type: DataTypes.STRING(255), allowNull: false }, + kind: { type: DataTypes.STRING(32), allowNull: false }, + before_value: { + type: DataTypes.JSON, + allowNull: true, + defaultValue: null, + get(this: AuditChangeModel): unknown | null { + return deserialize(this.getDataValue("before_value")); + }, + }, + after_value: { + type: DataTypes.JSON, + allowNull: true, + defaultValue: null, + get(this: AuditChangeModel): unknown | null { + return deserialize(this.getDataValue("after_value")); + }, + }, + }, + { + sequelize: database, + modelName: "AuditChangeModel", + tableName: "audit_changes", + charset: "utf8mb4", + collate: "utf8mb4_unicode_ci", + underscored: true, + timestamps: false, + } + ); return AuditChangeModel; } -function deserialize(value: unknown): unknown | null { return typeof value === "string" ? JSON.parse(value) : value; } +function deserialize(value: unknown): unknown | null { + return typeof value === "string" ? JSON.parse(value) : value; +} diff --git a/modules/core/src/api/infrastructure/audit/sequelize/audit-event.model.ts b/modules/core/src/api/infrastructure/audit/sequelize/audit-event.model.ts index 7cf3b662..2deda5f3 100644 --- a/modules/core/src/api/infrastructure/audit/sequelize/audit-event.model.ts +++ b/modules/core/src/api/infrastructure/audit/sequelize/audit-event.model.ts @@ -1,24 +1,92 @@ -import { DataTypes, type InferAttributes, type InferCreationAttributes, Model, type Sequelize } from "sequelize"; +import { + DataTypes, + type InferAttributes, + type InferCreationAttributes, + Model, + type Sequelize, +} from "sequelize"; + import { AuditChangeModel } from "./audit-change.model"; -export class AuditEventModel extends Model, InferCreationAttributes> { - declare id: string; declare kind: string; declare occurred_at: Date; declare source_type: string; declare source_id: string; - declare operation_id: string; declare correlation_id: string; declare origin: string; declare actor_type: string; - declare actor_id: string | null; declare actor_label: string | null; declare scope_type: string | null; declare scope_id: string | null; - declare scope_label: string | null; declare action: string; declare target_type: string; declare target_id: string | null; declare target_label: string | null; - static associate(_database: Sequelize): void { AuditEventModel.hasMany(AuditChangeModel, { as: "changes", foreignKey: "audit_event_id", sourceKey: "id" }); } +export class AuditEventModel extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: string; + declare kind: string; + declare occurred_at: Date; + declare source_type: string; + declare source_id: string; + declare operation_id: string; + declare correlation_id: string; + declare origin: string; + declare actor_type: string; + declare actor_id: string | null; + declare actor_label: string | null; + declare scope_type: string | null; + declare scope_id: string | null; + declare scope_label: string | null; + declare action: string; + declare target_type: string; + declare target_id: string | null; + declare target_label: string | null; + static associate(_database: Sequelize): void { + AuditEventModel.hasMany(AuditChangeModel, { + as: "changes", + foreignKey: "audit_event_id", + sourceKey: "id", + }); + } } export function initializeAuditEventModel(database: Sequelize): typeof AuditEventModel { - AuditEventModel.init({ - id: { type: DataTypes.STRING(64), allowNull: false, primaryKey: true }, kind: { type: DataTypes.STRING(32), allowNull: false }, occurred_at: { type: DataTypes.DATE(3), allowNull: false }, - source_type: { type: DataTypes.STRING(64), allowNull: false }, source_id: { type: DataTypes.STRING(128), allowNull: false }, operation_id: { type: DataTypes.STRING(128), allowNull: false }, - correlation_id: { type: DataTypes.STRING(128), allowNull: false }, origin: { type: DataTypes.STRING(64), allowNull: false }, actor_type: { type: DataTypes.STRING(64), allowNull: false }, - actor_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, actor_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, - scope_type: { type: DataTypes.STRING(64), allowNull: true, defaultValue: null }, scope_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, - scope_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, action: { type: DataTypes.STRING(191), allowNull: false }, target_type: { type: DataTypes.STRING(64), allowNull: false }, - target_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, target_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, - }, { sequelize: database, modelName: "AuditEventModel", tableName: "audit_events", charset: "utf8mb4", collate: "utf8mb4_unicode_ci", underscored: true, timestamps: false, - indexes: [{ name: "idx_audit_events_occurred_at_id", fields: ["occurred_at", "id"] }, { name: "idx_audit_events_scope_occurred_at_id", fields: ["scope_type", "scope_id", "occurred_at", "id"] }, { name: "idx_audit_events_target_occurred_at_id", fields: ["target_type", "target_id", "occurred_at", "id"] }, { name: "idx_audit_events_actor_occurred_at_id", fields: ["actor_type", "actor_id", "occurred_at", "id"] }, { name: "idx_audit_events_operation_id", fields: ["operation_id"] }, { name: "idx_audit_events_correlation_id", fields: ["correlation_id"] }] }); + AuditEventModel.init( + { + id: { type: DataTypes.STRING(64), allowNull: false, primaryKey: true }, + kind: { type: DataTypes.STRING(32), allowNull: false }, + occurred_at: { type: DataTypes.DATE(3), allowNull: false }, + source_type: { type: DataTypes.STRING(64), allowNull: false }, + source_id: { type: DataTypes.STRING(128), allowNull: false }, + operation_id: { type: DataTypes.STRING(128), allowNull: false }, + correlation_id: { type: DataTypes.STRING(128), allowNull: false }, + origin: { type: DataTypes.STRING(64), allowNull: false }, + actor_type: { type: DataTypes.STRING(64), allowNull: false }, + actor_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, + actor_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, + scope_type: { type: DataTypes.STRING(64), allowNull: true, defaultValue: null }, + scope_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, + scope_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, + action: { type: DataTypes.STRING(191), allowNull: false }, + target_type: { type: DataTypes.STRING(64), allowNull: false }, + target_id: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null }, + target_label: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null }, + }, + { + sequelize: database, + modelName: "AuditEventModel", + tableName: "audit_events", + charset: "utf8mb4", + collate: "utf8mb4_unicode_ci", + underscored: true, + timestamps: false, + indexes: [ + { name: "idx_audit_events_occurred_at_id", fields: ["occurred_at", "id"] }, + { + name: "idx_audit_events_scope_occurred_at_id", + fields: ["scope_type", "scope_id", "occurred_at", "id"], + }, + { + name: "idx_audit_events_target_occurred_at_id", + fields: ["target_type", "target_id", "occurred_at", "id"], + }, + { + name: "idx_audit_events_actor_occurred_at_id", + fields: ["actor_type", "actor_id", "occurred_at", "id"], + }, + { name: "idx_audit_events_operation_id", fields: ["operation_id"] }, + { name: "idx_audit_events_correlation_id", fields: ["correlation_id"] }, + ], + } + ); return AuditEventModel; } diff --git a/modules/core/src/api/infrastructure/audit/sequelize/sequelize-audit-store.ts b/modules/core/src/api/infrastructure/audit/sequelize/sequelize-audit-store.ts index a9d0da71..60826108 100644 --- a/modules/core/src/api/infrastructure/audit/sequelize/sequelize-audit-store.ts +++ b/modules/core/src/api/infrastructure/audit/sequelize/sequelize-audit-store.ts @@ -19,10 +19,11 @@ export class SequelizeAuditStore implements IAuditStore { options?: AuditRecordOptions ): Promise> { const transaction = (options?.transaction as Transaction) ?? this.transactionContext.current(); - if (entry.kind === "mutation" && !transaction) + if (entry.kind === "mutation" && !transaction) { return Result.fail( new AuditStoreError("An active transaction is required for audit mutations.") ); + } try { await this.eventModel.create( { @@ -47,7 +48,7 @@ export class SequelizeAuditStore implements IAuditStore { }, { transaction: transaction ?? undefined } ); - if (entry.kind === "mutation") + if (entry.kind === "mutation") { await this.changeModel.bulkCreate( entry.changes.map((change, position) => ({ audit_event_id: entry.id, @@ -59,6 +60,7 @@ export class SequelizeAuditStore implements IAuditStore { })), { transaction } ); + } return Result.ok(); } catch { return Result.fail(new AuditStoreError("Unable to append audit entry.")); diff --git a/modules/core/src/api/infrastructure/context/operation-audit-context-provider.ts b/modules/core/src/api/infrastructure/context/operation-audit-context-provider.ts index 80ed6a9c..33a78044 100644 --- a/modules/core/src/api/infrastructure/context/operation-audit-context-provider.ts +++ b/modules/core/src/api/infrastructure/context/operation-audit-context-provider.ts @@ -8,7 +8,9 @@ export class OperationAuditContextProvider implements IAuditContextProvider { get(): AuditContext | null { const context = this.contextStore.get(); - if (!context) return null; + if (!context) { + return null; + } return { operation: { diff --git a/modules/core/src/api/infrastructure/context/operation-context-store.ts b/modules/core/src/api/infrastructure/context/operation-context-store.ts index 8cdb3e7d..ef72f349 100644 --- a/modules/core/src/api/infrastructure/context/operation-context-store.ts +++ b/modules/core/src/api/infrastructure/context/operation-context-store.ts @@ -45,7 +45,9 @@ export class AsyncLocalStorageOperationContextStore implements IOperationContext assertActor(actor); const currentActor = holder.context.actor; - if (sameActor(currentActor, actor)) return; + if (sameActor(currentActor, actor)) { + return; + } if (!isAnonymous(currentActor)) { throw new OperationContextEnrichmentError( "[OperationContext] A concrete actor cannot be replaced." @@ -60,7 +62,9 @@ export class AsyncLocalStorageOperationContextStore implements IOperationContext assertScope(scope); const currentScope = holder.context.scope; - if (currentScope && sameScope(currentScope, scope)) return; + if (currentScope && sameScope(currentScope, scope)) { + return; + } if (currentScope) { throw new OperationContextEnrichmentError( "[OperationContext] A concrete scope cannot be replaced." @@ -72,7 +76,9 @@ export class AsyncLocalStorageOperationContextStore implements IOperationContext private getHolder(): ContextHolder { const holder = this.storage.getStore(); - if (!holder) throw new OperationContextUnavailableError(); + if (!holder) { + throw new OperationContextUnavailableError(); + } return holder; } } @@ -82,7 +88,9 @@ function cloneContext(context: OperationContext): OperationContext { assertNonEmpty(context.correlationId, "correlationId"); assertNonEmpty(context.origin, "origin"); assertActor(context.actor); - if (context.scope) assertScope(context.scope); + if (context.scope) { + assertScope(context.scope); + } return { operationId: context.operationId, @@ -106,7 +114,9 @@ function assertScope(scope: OperationScope): void { } function assertNullableValue(value: string | null, name: string): void { - if (value !== null) assertNonEmpty(value, name); + if (value !== null) { + assertNonEmpty(value, name); + } } function assertNonEmpty(value: string, name: string): void { diff --git a/modules/core/src/api/infrastructure/documents/renderers/handlebars/handlebars-template-resolver.ts b/modules/core/src/api/infrastructure/documents/renderers/handlebars/handlebars-template-resolver.ts index 011c820a..dba63f28 100644 --- a/modules/core/src/api/infrastructure/documents/renderers/handlebars/handlebars-template-resolver.ts +++ b/modules/core/src/api/infrastructure/documents/renderers/handlebars/handlebars-template-resolver.ts @@ -18,7 +18,9 @@ export class HandlebarsTemplateResolver extends RendererTemplateResolver { */ protected registerAssetHelper(templateDir: string) { // Si ya está registrado, no hacer nada - if (this.registered) return; + if (this.registered) { + return; + } this.hbs.registerHelper("asset", (resource: string) => { const assetPath = this.resolveAssetPath(templateDir, resource); diff --git a/modules/core/src/api/infrastructure/errors/infrastructure-repository-error.ts b/modules/core/src/api/infrastructure/errors/infrastructure-repository-error.ts index 3d8bd20f..1bf8464e 100644 --- a/modules/core/src/api/infrastructure/errors/infrastructure-repository-error.ts +++ b/modules/core/src/api/infrastructure/errors/infrastructure-repository-error.ts @@ -1,4 +1,5 @@ import { DatabaseError } from "sequelize"; + import { InfrastructureError } from "./infrastructure-errors"; export class InfrastructureRepositoryError extends InfrastructureError { diff --git a/modules/core/src/api/infrastructure/express/express-controller.ts b/modules/core/src/api/infrastructure/express/express-controller.ts index cc004444..42fcd144 100644 --- a/modules/core/src/api/infrastructure/express/express-controller.ts +++ b/modules/core/src/api/infrastructure/express/express-controller.ts @@ -55,7 +55,9 @@ export abstract class ExpressController { // Ejecutar guards (auth/tenant/otros). Si alguno falla, se responde y no se entra al impl. const ok = await this.runGuards(); - if (!ok) return; + if (!ok) { + return; + } await this.executeImpl(); } catch (error: unknown) { @@ -94,12 +96,16 @@ export abstract class ExpressController { protected ok(dto?: T, headers?: Record) { // biome-ignore lint/suspicious/useIterableCallbackReturn: - if (headers) Object.entries(headers).forEach(([k, v]) => this.res.setHeader(k, v)); + if (headers) { + Object.entries(headers).forEach(([k, v]) => this.res.setHeader(k, v)); + } return dto ? this.res.status(httpStatus.OK).json(dto) : this.res.sendStatus(httpStatus.OK); } protected created(dto?: T, location?: string) { - if (location) this.res.setHeader("Location", location); + if (location) { + this.res.setHeader("Location", location); + } return dto ? this.res.status(httpStatus.CREATED).json(dto) : this.res.sendStatus(httpStatus.CREATED); diff --git a/modules/core/src/api/infrastructure/express/express-guards.ts b/modules/core/src/api/infrastructure/express/express-guards.ts index 247973f8..54a5896f 100644 --- a/modules/core/src/api/infrastructure/express/express-guards.ts +++ b/modules/core/src/api/infrastructure/express/express-guards.ts @@ -73,7 +73,9 @@ export function forbidQueryFieldGuard(field = "companyId"): RequestHandler { export function injectTenantIntoBodyGuard(field = "companyId"): GuardFn { return ({ controller }: GuardContext) => { const tenantId = controller.getTenantId(); - if (!tenantId) return guardFail(new ForbiddenApiError("Tenant not found for user")); + if (!tenantId) { + return guardFail(new ForbiddenApiError("Tenant not found for user")); + } const body = (controller as any).req.body ?? {}; (controller as any).req.body = { ...body, [field]: tenantId }; diff --git a/modules/core/src/api/infrastructure/express/middlewares/authenticate-request.ts b/modules/core/src/api/infrastructure/express/middlewares/authenticate-request.ts index 841c3b6a..40c35acd 100644 --- a/modules/core/src/api/infrastructure/express/middlewares/authenticate-request.ts +++ b/modules/core/src/api/infrastructure/express/middlewares/authenticate-request.ts @@ -34,18 +34,26 @@ export type AuthenticateRequestOptions = { function getHeader(req: any, name: string): string | undefined { const value = typeof req.get === "function" ? req.get(name) : undefined; - if (typeof value === "string" && value.trim()) return value.trim(); + if (typeof value === "string" && value.trim()) { + return value.trim(); + } const raw = req.headers?.[name.toLowerCase()]; - if (typeof raw === "string" && raw.trim()) return raw.trim(); + if (typeof raw === "string" && raw.trim()) { + return raw.trim(); + } return undefined; } function parseBearerToken(authorization?: string): string | undefined { - if (!authorization) return undefined; + if (!authorization) { + return undefined; + } const [scheme, token] = authorization.split(" "); - if (scheme?.toLowerCase() !== "bearer") return undefined; + if (scheme?.toLowerCase() !== "bearer") { + return undefined; + } return token?.trim() || undefined; } diff --git a/modules/core/src/api/infrastructure/express/middlewares/operation-context.middleware.ts b/modules/core/src/api/infrastructure/express/middlewares/operation-context.middleware.ts index 1061ff81..7cc7d9ee 100644 --- a/modules/core/src/api/infrastructure/express/middlewares/operation-context.middleware.ts +++ b/modules/core/src/api/infrastructure/express/middlewares/operation-context.middleware.ts @@ -39,12 +39,16 @@ export function createOperationContextMiddleware( } function getCorrelationId(headerValue: string | undefined): string { - if (isValidCorrelationId(headerValue)) return headerValue.trim(); + if (isValidCorrelationId(headerValue)) { + return headerValue.trim(); + } return randomUUID(); } function isValidCorrelationId(value: string | undefined): value is string { - if (!value) return false; + if (!value) { + return false; + } const trimmed = value.trim(); return ( diff --git a/modules/core/src/api/infrastructure/index.ts b/modules/core/src/api/infrastructure/index.ts index 399abfb9..352e7ba6 100644 --- a/modules/core/src/api/infrastructure/index.ts +++ b/modules/core/src/api/infrastructure/index.ts @@ -1,5 +1,5 @@ -export * from "./config"; export * from "./audit"; +export * from "./config"; export * from "./context"; export * from "./database"; export * from "./di"; diff --git a/modules/core/src/api/infrastructure/persistence/sequelize/index.ts b/modules/core/src/api/infrastructure/persistence/sequelize/index.ts index 33218d42..0188258d 100644 --- a/modules/core/src/api/infrastructure/persistence/sequelize/index.ts +++ b/modules/core/src/api/infrastructure/persistence/sequelize/index.ts @@ -1,8 +1,8 @@ -export * from "./mappers"; export * from "./database-config"; -export * from "./sequelize-factory"; +export * from "./mappers"; export * from "./sequelize-error-translator"; +export * from "./sequelize-factory"; export * from "./sequelize-func"; export * from "./sequelize-repository"; -export * from "./sequelize-transaction-manager"; export * from "./sequelize-transaction-context"; +export * from "./sequelize-transaction-manager"; diff --git a/modules/core/src/api/infrastructure/persistence/sequelize/mappers/sequelize-domain-mapper.ts b/modules/core/src/api/infrastructure/persistence/sequelize/mappers/sequelize-domain-mapper.ts index f1212aca..25087d2a 100644 --- a/modules/core/src/api/infrastructure/persistence/sequelize/mappers/sequelize-domain-mapper.ts +++ b/modules/core/src/api/infrastructure/persistence/sequelize/mappers/sequelize-domain-mapper.ts @@ -49,7 +49,9 @@ export abstract class SequelizeDomainMapper this.mapToPersistence(domain, { index, ...params })) ); - if (results.hasSomeFaultyResult()) return results.getFirstFaultyResult(); + if (results.hasSomeFaultyResult()) { + return results.getFirstFaultyResult(); + } return Result.ok(results.objects); } diff --git a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-error-translator.ts b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-error-translator.ts index f13649aa..62ab751b 100644 --- a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-error-translator.ts +++ b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-error-translator.ts @@ -69,7 +69,9 @@ export function translateSequelizeError(err: unknown): Error { } // 6) Fallback: deja pasar si ya es un Error tipado de tu app, si no wrap - if (err instanceof Error) return err; + if (err instanceof Error) { + return err; + } return new InfrastructureRepositoryError("Unknown persistence error", { cause: err as unknown }); } diff --git a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-context.test.ts b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-context.test.ts index 7fcaa2ae..4b7dd708 100644 --- a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-context.test.ts +++ b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-context.test.ts @@ -5,6 +5,7 @@ import { Result } from "@repo/rdx-utils"; import type { Sequelize, Transaction } from "sequelize"; import { NestedTransactionNotAllowedError } from "../../errors"; + import { SequelizeTransactionContext } from "./sequelize-transaction-context"; import { SequelizeTransactionManager } from "./sequelize-transaction-manager"; diff --git a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-manager.ts b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-manager.ts index ad9df10e..10801fac 100644 --- a/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-manager.ts +++ b/modules/core/src/api/infrastructure/persistence/sequelize/sequelize-transaction-manager.ts @@ -17,7 +17,7 @@ export class SequelizeTransactionManager extends TransactionManager { protected async _startTransaction(): Promise { // Sequelize adquiere una conexión del pool y la asocia a la transacción. - return await this._database!.transaction({ + return await this._database?.transaction({ isolationLevel: this.isolationLevel, }); } diff --git a/modules/core/src/api/modules/module-runtime.ts b/modules/core/src/api/modules/module-runtime.ts index 41f3579d..f83e0add 100644 --- a/modules/core/src/api/modules/module-runtime.ts +++ b/modules/core/src/api/modules/module-runtime.ts @@ -19,13 +19,19 @@ const usedDependencies = new Map>(); let modelsInitialized = false; export function registerModule(module: IModuleServer): void { - if (!module.name) throw new Error('Invalid module: missing "name" property'); - if (modules.has(module.name)) throw new Error(`Module "${module.name}" already registered`); + if (!module.name) { + throw new Error('Invalid module: missing "name" property'); + } + if (modules.has(module.name)) { + throw new Error(`Module "${module.name}" already registered`); + } modules.set(module.name, module); } export function registerModels(initializers: ModelInitializer[], moduleName: string): void { - for (const initializer of initializers) models.push({ initializer, moduleName }); + for (const initializer of initializers) { + models.push({ initializer, moduleName }); + } } /** Inicializa únicamente infraestructura backend: setup, modelos y warmups. */ @@ -37,7 +43,9 @@ export async function initModules( databaseSyncMode: string; } ): Promise { - for (const name of modules.keys()) await setupModule(name, params, []); + for (const name of modules.keys()) { + await setupModule(name, params, []); + } await initializeModels(params.database, params.databaseSyncMode, params.logger); await warmupModules(params); validateDependencies(); @@ -48,8 +56,9 @@ export async function initModules( } | undefined; - if (!customerInvoices) + if (!customerInvoices) { throw new Error('Module "customer-invoices" did not expose recurring runtime capabilities'); + } return { customerInvoices: customerInvoices.proformas.recurringProformas }; } @@ -58,7 +67,9 @@ export async function initModules( export async function startModules(params: ModuleParams & { logger: ILogger }): Promise { for (const name of setupOrder) { const module = modules.get(name); - if (!module?.start) continue; + if (!module?.start) { + continue; + } await module.start({ ...params, getService: makeGetService(name, module), @@ -163,7 +174,9 @@ async function warmupModules( ): Promise { for (const name of setupOrder) { const module = modules.get(name); - if (!module?.warmup) continue; + if (!module?.warmup) { + continue; + } try { await withTimeout( Promise.resolve( @@ -178,7 +191,9 @@ async function warmupModules( `${name}.warmup` ); } catch (error) { - if (params.warmup.strict) throw error; + if (params.warmup.strict) { + throw error; + } params.logger.warn("Module warmup failed but continuing", { label: "moduleRuntime", module: name, @@ -189,7 +204,9 @@ async function warmupModules( } function registerService(name: string, service: unknown): void { - if (services[name]) throw new Error(`Service "${name}" already registered`); + if (services[name]) { + throw new Error(`Service "${name}" already registered`); + } services[name] = service; } @@ -197,10 +214,11 @@ function makeGetService(moduleName: string, module: IModuleServer) { return (name: string): T => { const [dependency, ...key] = name.split(":"); if (dependency !== "self") { - if (!(module.dependencies ?? []).includes(dependency)) + if (!(module.dependencies ?? []).includes(dependency)) { throw new Error( `Module "${moduleName}" tried to access service "${name}" without declaring dependency on "${dependency}"` ); + } trackDependency(moduleName, dependency); return getService(name); } @@ -210,8 +228,9 @@ function makeGetService(moduleName: string, module: IModuleServer) { function makeGetInternal(moduleName: string) { return (requestedModule: string, key?: string): T => { - if (requestedModule !== moduleName) + if (requestedModule !== moduleName) { throw new Error(`Module "${moduleName}" attempted to access private internal state`); + } const internal = internals.get(moduleName) ?? {}; return (key ? internal[key] : internal) as T; }; @@ -219,7 +238,9 @@ function makeGetInternal(moduleName: string) { function getService(name: string): T { const service = services[name]; - if (!service) throw new Error(`Service "${name}" not found`); + if (!service) { + throw new Error(`Service "${name}" not found`); + } return service as T; } @@ -234,8 +255,9 @@ function validateDependencies(): void { const declared = new Set(module.dependencies ?? []); const used = usedDependencies.get(name) ?? new Set(); const undeclared = [...used].filter((dependency) => !declared.has(dependency)); - if (undeclared.length) + if (undeclared.length) { throw new Error(`Module "${name}" used undeclared dependencies: ${undeclared.join(", ")}`); + } } } @@ -248,7 +270,9 @@ function withTimeout(promise: Promise, timeoutMs: number, label: string): ); }); return Promise.race([promise, timeoutPromise]).finally(() => { - if (timeout) clearTimeout(timeout); + if (timeout) { + clearTimeout(timeout); + } }) as Promise; } @@ -262,11 +286,14 @@ export function getServiceScoped( name: string ): T { const [moduleName, ...key] = name.split(":"); - if (moduleName === "self") return getService(`${requesterModule}:${key.join(":")}`); - if (!allowedDependencies.includes(moduleName)) + if (moduleName === "self") { + return getService(`${requesterModule}:${key.join(":")}`); + } + if (!allowedDependencies.includes(moduleName)) { throw new Error( `Module "${requesterModule}" tried to access service "${name}" without declaring dependency on "${moduleName}"` ); + } return getService(name); } diff --git a/modules/core/src/common/catalogs/payments/factuges-payment-catalog.json b/modules/core/src/common/catalogs/payments/factuges-payment-catalog.json index 23bc0c43..1c1f6326 100644 --- a/modules/core/src/common/catalogs/payments/factuges-payment-catalog.json +++ b/modules/core/src/common/catalogs/payments/factuges-payment-catalog.json @@ -27,4 +27,4 @@ "description": "30/60/90/120", "group": "General" } -] \ No newline at end of file +] diff --git a/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json b/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json index c892bd42..0b1363e5 100644 --- a/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json +++ b/modules/core/src/common/catalogs/taxes/spain-tax-catalog.json @@ -7,7 +7,7 @@ "group": "IVA", "description": "IVA general. Tipo estándar nacional.", "aeat_code": "01", - "req_equals": ["rec_5_2"] + "req_equals": ["rec_5_2"] }, { "name": "IVA 10%", @@ -351,4 +351,4 @@ "description": "Operación exenta de IPSI.", "aeat_code": null } -] \ No newline at end of file +] diff --git a/modules/core/src/common/dto/critera.dto.ts b/modules/core/src/common/dto/critera.dto.ts index 26148df8..5e06d90d 100644 --- a/modules/core/src/common/dto/critera.dto.ts +++ b/modules/core/src/common/dto/critera.dto.ts @@ -47,8 +47,8 @@ export const CriteriaSchema = z.object({ .int() .min(1, { message: "pageSize must be a positive integer" }) .optional(), - pageNumber: z - .coerce.number() + pageNumber: z.coerce + .number() .int() .min(0, { message: "pageNumber must be a non-negative integer" }) .optional(), diff --git a/modules/core/src/common/dto/website.dto.ts b/modules/core/src/common/dto/website.dto.ts index 14060839..b830df51 100644 --- a/modules/core/src/common/dto/website.dto.ts +++ b/modules/core/src/common/dto/website.dto.ts @@ -2,7 +2,9 @@ import { z } from "zod/v4"; export const WebsiteSchema = z.preprocess( (value) => { - if (typeof value !== "string") return value; + if (typeof value !== "string") { + return value; + } const trimmed = value.trim(); @@ -12,7 +14,9 @@ export const WebsiteSchema = z.preprocess( .string() .refine( (value) => { - if (!value) return true; + if (!value) { + return true; + } const candidate = /^https?:\/\//i.test(value) ? value : `https://${value}`; @@ -29,7 +33,9 @@ export const WebsiteSchema = z.preprocess( } ) .transform((value) => { - if (!value) return ""; + if (!value) { + return ""; + } return /^https?:\/\//i.test(value) ? value : `https://${value}`; }) diff --git a/modules/core/src/common/helpers/apply-validation-error-collection.ts b/modules/core/src/common/helpers/apply-validation-error-collection.ts index 79548380..799bb190 100644 --- a/modules/core/src/common/helpers/apply-validation-error-collection.ts +++ b/modules/core/src/common/helpers/apply-validation-error-collection.ts @@ -55,7 +55,9 @@ export const applyValidationErrorCollection = ( const message = detail.message?.trim(); const rawPath = detail.path?.trim(); - if (!message) continue; + if (!message) { + continue; + } if (!rawPath) { globalMessages.push(message); diff --git a/modules/core/src/common/helpers/money-dto-helper.ts b/modules/core/src/common/helpers/money-dto-helper.ts index c9f2bf13..cf575f91 100644 --- a/modules/core/src/common/helpers/money-dto-helper.ts +++ b/modules/core/src/common/helpers/money-dto-helper.ts @@ -12,8 +12,8 @@ const toNumber = (dto?: MoneyDTO | null, fallbackScale = 2): number => { if (isEmptyMoneyDTO(dto)) { return 0; } - const scale = Number(dto!.scale || fallbackScale); - return Number(dto!.value || 0) / 10 ** scale; + const scale = Number(dto?.scale || fallbackScale); + return Number(dto?.value || 0) / 10 ** scale; }; /** diff --git a/modules/core/src/common/helpers/percentage-dto-helper.ts b/modules/core/src/common/helpers/percentage-dto-helper.ts index 317d2853..249fa0d8 100644 --- a/modules/core/src/common/helpers/percentage-dto-helper.ts +++ b/modules/core/src/common/helpers/percentage-dto-helper.ts @@ -10,8 +10,8 @@ const toNumber = (dto?: PercentageDTO | null, fallbackScale = 2): number => { if (isEmptyPercentageDTO(dto)) { return 0; } - const scale = Number(dto!.scale || fallbackScale); - return Number(dto!.value || 0) / 10 ** scale; + const scale = Number(dto?.scale || fallbackScale); + return Number(dto?.value || 0) / 10 ** scale; }; /** @@ -67,7 +67,9 @@ const format = ( const absolute = toNumber(dto, fallbackScale); // ej. 12.5 - if (absolute === 0 && options?.hideZeros) return ""; + if (absolute === 0 && options?.hideZeros) { + return ""; + } const fraction = absolute / 100; // ej. 0.125 para Intl percent diff --git a/modules/core/src/common/helpers/quantity-dto-helper.ts b/modules/core/src/common/helpers/quantity-dto-helper.ts index b8bc87bf..77fd58ae 100644 --- a/modules/core/src/common/helpers/quantity-dto-helper.ts +++ b/modules/core/src/common/helpers/quantity-dto-helper.ts @@ -10,8 +10,8 @@ const toNumber = (dto?: QuantityDTO | null, fallbackScale = 2): number => { if (isEmptyQuantityDTO(dto)) { return 0; } - const scale = Number(dto!.scale || fallbackScale); - return Number(dto!.value || 0) / 10 ** scale; + const scale = Number(dto?.scale || fallbackScale); + return Number(dto?.value || 0) / 10 ** scale; }; /** diff --git a/modules/core/src/web/components/__tests__/page-error-state.test.tsx b/modules/core/src/web/components/__tests__/page-error-state.test.tsx index c2e4c362..ef97e2e3 100644 --- a/modules/core/src/web/components/__tests__/page-error-state.test.tsx +++ b/modules/core/src/web/components/__tests__/page-error-state.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { PageErrorState } from "../page-error-state"; diff --git a/modules/core/src/web/components/error-state.tsx b/modules/core/src/web/components/error-state.tsx index 8b1d5f51..9e71e478 100644 --- a/modules/core/src/web/components/error-state.tsx +++ b/modules/core/src/web/components/error-state.tsx @@ -1,4 +1,5 @@ import type React from "react"; + import { PageErrorState } from "./page-error-state"; export interface ErrorStateProps extends Omit, "title"> { diff --git a/modules/core/src/web/components/form/form-actions/page-keyboard-shortcuts-button.tsx b/modules/core/src/web/components/form/form-actions/page-keyboard-shortcuts-button.tsx index 33f9c9d2..a44776bf 100644 --- a/modules/core/src/web/components/form/form-actions/page-keyboard-shortcuts-button.tsx +++ b/modules/core/src/web/components/form/form-actions/page-keyboard-shortcuts-button.tsx @@ -18,7 +18,9 @@ export const PageKeyboardShortcutsButton = ({ shortcuts, className, }: PageKeyboardShortcutsButtonProps) => { - if (shortcuts.length === 0) return null; + if (shortcuts.length === 0) { + return null; + } return ( diff --git a/modules/core/src/web/components/form/form-debug.tsx b/modules/core/src/web/components/form/form-debug.tsx index c6d2fc10..c8397d5b 100644 --- a/modules/core/src/web/components/form/form-debug.tsx +++ b/modules/core/src/web/components/form/form-debug.tsx @@ -30,7 +30,7 @@ function FormDebugInner({ } // Renderiza una propiedad recursivamente con expansión -function DebugField({ +function _DebugField({ label, oldValue, newValue, @@ -65,7 +65,12 @@ function DebugField({ {open && (
    {Object.keys(newValue).map((key) => ( - + <_DebugField + key={key} + label={key} + newValue={newValue[key]} + oldValue={oldValue?.[key]} + /> ))}
)} diff --git a/modules/core/src/web/components/form/simple-search-input.tsx b/modules/core/src/web/components/form/simple-search-input.tsx index 6d3d5d8f..8714b4d6 100644 --- a/modules/core/src/web/components/form/simple-search-input.tsx +++ b/modules/core/src/web/components/form/simple-search-input.tsx @@ -40,7 +40,7 @@ export const SimpleSearchInput = ({ const [searchValue, setSearchValue] = useState(value); const [lastSearch, setLastSearch] = useState(""); const [history, setHistory] = useState([]); - const [open, setOpen] = useState(false); + const [_open, setOpen] = useState(false); const inputRef = useRef(null); const onSearchChangeRef = useRef(onSearchChange); @@ -68,7 +68,9 @@ export const SimpleSearchInput = ({ useEffect(() => { const stored = localStorage.getItem(SEARCH_HISTORY_KEY); - if (!stored) return; + if (!stored) { + return; + } try { const parsed = JSON.parse(stored) as string[]; @@ -89,7 +91,9 @@ export const SimpleSearchInput = ({ const saveHistory = (term: string) => { const cleaned = normalizeSearchValue(term); - if (!cleaned) return; + if (!cleaned) { + return; + } setHistory((prev) => { const nextHistory = [cleaned, ...prev.filter((item) => item !== cleaned)].slice( @@ -132,7 +136,9 @@ export const SimpleSearchInput = ({ if (e.key === "Enter" && e.shiftKey) { e.preventDefault(); - if (!lastSearch) return; + if (!lastSearch) { + return; + } handleImmediateSearch(lastSearch); return; @@ -144,7 +150,7 @@ export const SimpleSearchInput = ({ } }; - const handleSelectHistory = (term: string) => { + const _handleSelectHistory = (term: string) => { handleImmediateSearch(term); }; @@ -157,42 +163,42 @@ export const SimpleSearchInput = ({ isSearching && "ring-2 ring-primary/15" )} > - history.length > 0 && setOpen(true)} - onKeyDown={handleKeyDown} - placeholder={ - placeholder || t("components.simple_search_input.search_placeholder", "Search...") - } - ref={inputRef} - spellCheck={false} - value={searchValue} - /> + history.length > 0 && setOpen(true)} + onKeyDown={handleKeyDown} + placeholder={ + placeholder || t("components.simple_search_input.search_placeholder", "Search...") + } + ref={inputRef} + spellCheck={false} + value={searchValue} + /> - - {isSearching ? ( - - ) : ( - - )} - + + {isSearching ? ( + + ) : ( + + )} + - - {isTypingPendingSearch ? ( - - {t("components.simple_search_input.searching", "Searching...")} - - ) : null} - + + {isTypingPendingSearch ? ( + + {t("components.simple_search_input.searching", "Searching...")} + + ) : null} + {!(searchValue || isSearching) && ( @@ -226,10 +232,7 @@ export const SimpleSearchInput = ({ : t("components.simple_search_input.searching", "Searching...")}

) : hasActiveSearch ? ( -
+

diff --git a/modules/core/src/web/components/page-error-state.tsx b/modules/core/src/web/components/page-error-state.tsx index 6ec312ea..01eec2f4 100644 --- a/modules/core/src/web/components/page-error-state.tsx +++ b/modules/core/src/web/components/page-error-state.tsx @@ -81,7 +81,11 @@ export const PageErrorState = ({ {primaryAction || secondaryAction ? (

{primaryAction ? ( - ) : null} diff --git a/modules/core/src/web/hooks/use-unsaved-changes-notifier/components/unsaved-changes-cancel-action-button.tsx b/modules/core/src/web/hooks/use-unsaved-changes-notifier/components/unsaved-changes-cancel-action-button.tsx index 4426d9db..d006d38a 100644 --- a/modules/core/src/web/hooks/use-unsaved-changes-notifier/components/unsaved-changes-cancel-action-button.tsx +++ b/modules/core/src/web/hooks/use-unsaved-changes-notifier/components/unsaved-changes-cancel-action-button.tsx @@ -18,7 +18,9 @@ export const UnsavedChangesCancelActionButton = ({ try { const ok = requestConfirm ? await requestConfirm() : true; - if (!ok) return; + if (!ok) { + return; + } await onCancel(); } finally { diff --git a/modules/core/src/web/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx b/modules/core/src/web/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx index eb0e9381..506d5b5c 100644 --- a/modules/core/src/web/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx +++ b/modules/core/src/web/hooks/use-unsaved-changes-notifier/use-unsaved-changes-notifier.tsx @@ -39,9 +39,13 @@ export const UnsavedChangesProvider = ({ isDirty, children }: UnsavedChangesProv const [open, setOpen] = useState(false); const requestConfirm = useCallback(() => { - if (!isDirty) return Promise.resolve(true); + if (!isDirty) { + return Promise.resolve(true); + } - if (resolverRef.current) return Promise.resolve(false); + if (resolverRef.current) { + return Promise.resolve(false); + } return new Promise((resolve) => { resolverRef.current = resolve; @@ -60,19 +64,25 @@ export const UnsavedChangesProvider = ({ isDirty, children }: UnsavedChangesProv }, []); const blocker = useBlocker(() => { - if (allowNavigationRef.current) return false; + if (allowNavigationRef.current) { + return false; + } return isDirty; }); useEffect(() => { - if (blocker.state !== "blocked") return; + if (blocker.state !== "blocked") { + return; + } let active = true; const _ = (async () => { const discardChanges = await requestConfirm(); - if (!active) return; + if (!active) { + return; + } if (discardChanges) { allowNavigationRef.current = true; @@ -95,10 +105,14 @@ export const UnsavedChangesProvider = ({ isDirty, children }: UnsavedChangesProv }, []); useEffect(() => { - if (!isDirty) return; + if (!isDirty) { + return; + } const handleBeforeUnload = (event: BeforeUnloadEvent) => { - if (allowNavigationRef.current) return; + if (allowNavigationRef.current) { + return; + } event.preventDefault(); diff --git a/modules/core/src/web/lib/data-source/axios/__tests__/normalize-axios-error.test.ts b/modules/core/src/web/lib/data-source/axios/__tests__/normalize-axios-error.test.ts index 7a95823c..2cab2233 100644 --- a/modules/core/src/web/lib/data-source/axios/__tests__/normalize-axios-error.test.ts +++ b/modules/core/src/web/lib/data-source/axios/__tests__/normalize-axios-error.test.ts @@ -1,8 +1,7 @@ -import { AxiosError, AxiosHeaders } from "axios"; import type { AxiosResponse, InternalAxiosRequestConfig } from "axios"; +import { AxiosError, AxiosHeaders } from "axios"; import { isApiClientError } from "../../errors"; - import { normalizeAxiosError } from "../normalize-axios-error"; const createAxiosConfig = (): InternalAxiosRequestConfig => { diff --git a/modules/core/src/web/lib/data-source/axios/create-axios-instance.ts b/modules/core/src/web/lib/data-source/axios/create-axios-instance.ts index 4a536926..3eec36c2 100644 --- a/modules/core/src/web/lib/data-source/axios/create-axios-instance.ts +++ b/modules/core/src/web/lib/data-source/axios/create-axios-instance.ts @@ -1,6 +1,6 @@ import axios, { type AxiosInstance, type CreateAxiosDefaults } from "axios"; -import { setupInterceptors, type RefreshSessionHandler } from "./setup-interceptors"; +import { type RefreshSessionHandler, setupInterceptors } from "./setup-interceptors"; /** * Configuración necesaria para crear una instancia de Axios personalizada. diff --git a/modules/core/src/web/lib/data-source/axios/normalize-axios-error.ts b/modules/core/src/web/lib/data-source/axios/normalize-axios-error.ts index 02ed52ac..03b1798d 100644 --- a/modules/core/src/web/lib/data-source/axios/normalize-axios-error.ts +++ b/modules/core/src/web/lib/data-source/axios/normalize-axios-error.ts @@ -1,12 +1,12 @@ -import axios, { AxiosError } from "axios"; import type { AxiosError as AxiosErrorType } from "axios"; -import type { ApiProblemDetails } from "../errors"; +import axios, { AxiosError } from "axios"; +import type { ApiProblemDetails } from "../errors"; import { ApiClientError, type ApiClientErrorKind, - isApiProblemDetails, isApiClientError, + isApiProblemDetails, } from "../errors"; const getErrorKind = (status?: number): ApiClientErrorKind => { @@ -94,9 +94,10 @@ export const normalizeAxiosError = (error: unknown): ApiClientError => { if (!axios.isAxiosError(error)) { return new ApiClientError({ kind: "unknown", - message: error instanceof Error && error.message.trim().length > 0 - ? error.message - : "An unexpected error occurred.", + message: + error instanceof Error && error.message.trim().length > 0 + ? error.message + : "An unexpected error occurred.", cause: error, }); } diff --git a/modules/core/src/web/lib/data-source/build-text-filters.tsx b/modules/core/src/web/lib/data-source/build-text-filters.tsx index c682dc96..9448840b 100644 --- a/modules/core/src/web/lib/data-source/build-text-filters.tsx +++ b/modules/core/src/web/lib/data-source/build-text-filters.tsx @@ -3,7 +3,9 @@ * para los campos especificados. */ export function buildTextFilters(fields: string[], value: string) { - if (!value.trim()) return []; + if (!value.trim()) { + return []; + } return fields.map((field) => ({ field, diff --git a/modules/core/src/web/lib/data-source/errors/get-user-error-message.ts b/modules/core/src/web/lib/data-source/errors/get-user-error-message.ts index 1c1b4e54..621d6f20 100644 --- a/modules/core/src/web/lib/data-source/errors/get-user-error-message.ts +++ b/modules/core/src/web/lib/data-source/errors/get-user-error-message.ts @@ -98,9 +98,6 @@ export const getUserErrorMessage = (error: unknown): UserErrorMessage => { description: "Inténtalo de nuevo dentro de unos instantes.", reference, }; - - case "server": - case "unknown": default: return { title: "No se ha podido completar la operación", diff --git a/modules/core/src/web/lib/data-source/errors/is-api-problem-details.ts b/modules/core/src/web/lib/data-source/errors/is-api-problem-details.ts index 55593626..021cf7c4 100644 --- a/modules/core/src/web/lib/data-source/errors/is-api-problem-details.ts +++ b/modules/core/src/web/lib/data-source/errors/is-api-problem-details.ts @@ -12,10 +12,7 @@ const isPathSegment = (value: unknown): value is ApiProblemPathSegment => typeof value === "string" || typeof value === "number"; const isProblemSource = (value: unknown): value is ApiProblemSource => - value === "request" || - value === "response" || - value === "domain" || - value === "infrastructure"; + value === "request" || value === "response" || value === "domain" || value === "infrastructure"; const isApiProblemIssue = (value: unknown): value is ApiProblemIssue => { if (!isRecord(value)) { @@ -86,7 +83,10 @@ export const isApiProblemDetails = (value: unknown): value is ApiProblemDetails return false; } - if (value.errors !== undefined && !(Array.isArray(value.errors) && value.errors.every(isApiProblemIssue))) { + if ( + value.errors !== undefined && + !(Array.isArray(value.errors) && value.errors.every(isApiProblemIssue)) + ) { return false; } diff --git a/modules/core/src/web/lib/helpers/build-top-level-form-dirty-patch.ts b/modules/core/src/web/lib/helpers/build-top-level-form-dirty-patch.ts index 83c62ab0..64347ca0 100644 --- a/modules/core/src/web/lib/helpers/build-top-level-form-dirty-patch.ts +++ b/modules/core/src/web/lib/helpers/build-top-level-form-dirty-patch.ts @@ -200,10 +200,14 @@ export const buildTopLevelFormDirtyPatch = = {}; for (const key of Object.keys(spec) as Array) { - if (!isTopLevelDirty(dirtyFields, key)) continue; + if (!isTopLevelDirty(dirtyFields, key)) { + continue; + } const normalize = spec[key]; - if (!normalize) continue; + if (!normalize) { + continue; + } patch[key] = normalize(formData[key]) as TPatch[typeof key]; } diff --git a/modules/core/src/web/lib/helpers/form-utils.ts b/modules/core/src/web/lib/helpers/form-utils.ts index 98cb9f4b..fc99061e 100644 --- a/modules/core/src/web/lib/helpers/form-utils.ts +++ b/modules/core/src/web/lib/helpers/form-utils.ts @@ -20,7 +20,9 @@ export function pickFormDirtyValues>( const replaceTopLevelArrayKeys = options?.replaceTopLevelArrayKeys ?? []; for (const key in dirtyFields) { - if (!Object.hasOwn(dirtyFields, key)) continue; + if (!Object.hasOwn(dirtyFields, key)) { + continue; + } const typedKey = key as keyof T; const isDirty = dirtyFields[typedKey]; @@ -59,8 +61,12 @@ export function pickFormDirtyValues>( * Devuelve true si hay al menos un campo dirty en cualquier nivel. */ export function formHasAnyDirty(dirtyFields: Partial> | boolean): boolean { - if (dirtyFields === true) return true; - if (dirtyFields === false || dirtyFields == null) return false; + if (dirtyFields === true) { + return true; + } + if (dirtyFields === false || dirtyFields == null) { + return false; + } if (typeof dirtyFields === "object") { return Object.values(dirtyFields).some((value) => formHasAnyDirty(value)); diff --git a/modules/core/src/web/lib/helpers/http-url-utils.ts b/modules/core/src/web/lib/helpers/http-url-utils.ts index 3b7426b8..9ec6037d 100644 --- a/modules/core/src/web/lib/helpers/http-url-utils.ts +++ b/modules/core/src/web/lib/helpers/http-url-utils.ts @@ -1,5 +1,9 @@ export function safeHTTPUrl(url: string) { - if (!url) return "#"; - if (/^https?:\/\//i.test(url)) return url; + if (!url) { + return "#"; + } + if (/^https?:\/\//i.test(url)) { + return url; + } return `https://${url}`; } diff --git a/modules/core/src/web/manifest.ts b/modules/core/src/web/manifest.ts index 64a2b60a..65e0c029 100644 --- a/modules/core/src/web/manifest.ts +++ b/modules/core/src/web/manifest.ts @@ -10,7 +10,7 @@ export const CoreModuleManifest: IModuleClient = { protected: true, layout: "app-sidebar", - routes: (params: ModuleClientParams) => { + routes: (_params: ModuleClientParams) => { return []; }, }; diff --git a/modules/customer-invoices/package.json b/modules/customer-invoices/package.json index 2ea406a0..2c541f28 100644 --- a/modules/customer-invoices/package.json +++ b/modules/customer-invoices/package.json @@ -72,4 +72,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/customer-invoices/src/api/application/company-verifactu-profile/use-cases/get-company-verifactu-profile.use-case.ts b/modules/customer-invoices/src/api/application/company-verifactu-profile/use-cases/get-company-verifactu-profile.use-case.ts index 6edd9b3b..99049c2a 100644 --- a/modules/customer-invoices/src/api/application/company-verifactu-profile/use-cases/get-company-verifactu-profile.use-case.ts +++ b/modules/customer-invoices/src/api/application/company-verifactu-profile/use-cases/get-company-verifactu-profile.use-case.ts @@ -23,7 +23,10 @@ export class GetCompanyVerifactuProfileUseCase { params: GetCompanyVerifactuProfileUseCaseInput ): Promise> { return this.deps.transactionManager.complete(async (transaction: unknown) => { - const profileResult = await this.deps.repository.findByCompanyId(params.companyId, transaction); + const profileResult = await this.deps.repository.findByCompanyId( + params.companyId, + transaction + ); if (profileResult.isFailure) { return Result.fail(profileResult.error); diff --git a/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-inactive.error.ts b/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-inactive.error.ts index 4d7356b5..c56e694f 100644 --- a/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-inactive.error.ts +++ b/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-inactive.error.ts @@ -5,6 +5,5 @@ export class InvoiceSeriesInactiveError extends Error { } } -export const isInvoiceSeriesInactiveError = ( - error: unknown -): error is InvoiceSeriesInactiveError => error instanceof InvoiceSeriesInactiveError; +export const isInvoiceSeriesInactiveError = (error: unknown): error is InvoiceSeriesInactiveError => + error instanceof InvoiceSeriesInactiveError; diff --git a/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-not-found.error.ts b/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-not-found.error.ts index cfeb1720..ddcba0de 100644 --- a/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-not-found.error.ts +++ b/modules/customer-invoices/src/api/application/invoice-series/errors/invoice-series-not-found.error.ts @@ -5,6 +5,5 @@ export class InvoiceSeriesNotFoundError extends Error { } } -export const isInvoiceSeriesNotFoundError = ( - error: unknown -): error is InvoiceSeriesNotFoundError => error instanceof InvoiceSeriesNotFoundError; +export const isInvoiceSeriesNotFoundError = (error: unknown): error is InvoiceSeriesNotFoundError => + error instanceof InvoiceSeriesNotFoundError; diff --git a/modules/customer-invoices/src/api/application/issued-invoices/audit/issued-invoice-audit.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/audit/issued-invoice-audit.test.ts index 88358fd7..b7944ee2 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/audit/issued-invoice-audit.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/audit/issued-invoice-audit.test.ts @@ -40,7 +40,9 @@ import { import { toIssuedInvoiceAuditState } from "./issued-invoice-audit"; const required = (result: Result): T => { - if (result.isFailure) throw result.error; + if (result.isFailure) { + throw result.error; + } return result.data; }; const amount = (value: number) => required(ItemAmount.create({ value, currency_code: "EUR" })); diff --git a/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts b/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts index 96777f3c..6e85fdf9 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/models/proforma-issue-read.model.ts @@ -1,10 +1,11 @@ +import type { Maybe } from "@repo/rdx-utils"; + import type { Proforma } from "../../../domain"; import type { InvocieTaxRegimeFullReadModel, InvoicePaymentMethodReadModel, InvoicePaymentTermReadModel, } from "../../common/models"; -import type { Maybe } from "@repo/rdx-utils"; /** * Modelo de lectura usado exclusivamente durante la emisión de una proforma. diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts index 8c248ffd..99708b71 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/proforma-to-issued-invoice-props-converter.test.ts @@ -6,6 +6,7 @@ import { CurrencyCode, LanguageCode, UniqueID, UtcDate } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { InvoiceAmount } from "../../../domain"; + import { ProformaToIssuedInvoiceConverter } from "./proforma-to-issued-invoice-props-converter"; test("toCreateProps no materializa VerifactuRecord ni policy en el converter", () => { @@ -163,16 +164,24 @@ test("resolveTax copia los nombres preservados por Proforma.taxes sin reconstrui const amount = InvoiceAmount.create({ value: 2200, currency_code: "EUR" }).data; const taxableAmount = InvoiceAmount.create({ value: 10000, currency_code: "EUR" }).data; - const result = (converter as unknown as { - resolveTax: (tax: unknown) => Result<{ ivaName: string; ivaCode: string }, Error>; - }).resolveTax({ + const result = ( + converter as unknown as { + resolveTax: (tax: unknown) => Result<{ ivaName: string; ivaCode: string }, Error>; + } + ).resolveTax({ taxableAmount, ivaCode: Maybe.some("iva_21"), ivaName: Maybe.some("IVA actualizado"), ivaPercentage: Maybe.some(percentage), ivaAmount: amount, - recCode: Maybe.none(), recName: Maybe.none(), recPercentage: Maybe.none(), recAmount: amount, - retentionCode: Maybe.none(), retentionName: Maybe.none(), retentionPercentage: Maybe.none(), retentionAmount: amount, + recCode: Maybe.none(), + recName: Maybe.none(), + recPercentage: Maybe.none(), + recAmount: amount, + retentionCode: Maybe.none(), + retentionName: Maybe.none(), + retentionPercentage: Maybe.none(), + retentionAmount: amount, taxesAmount: amount, }); diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts index 3d2eab1f..ae049246 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.test.ts @@ -4,10 +4,8 @@ import test from "node:test"; import { UniqueID } from "@repo/rdx-ddd"; import { Maybe } from "@repo/rdx-utils"; -import { - CompanyVerifactuProfile, - CompanyVerifactuProfileStatus, -} from "../../../domain"; +import { CompanyVerifactuProfile, CompanyVerifactuProfileStatus } from "../../../domain"; + import { resolveProformaIssueFiscalContext } from "./resolve-proforma-issue-fiscal-context"; function buildCompanyProfile(params?: { @@ -20,16 +18,14 @@ function buildCompanyProfile(params?: { const result = CompanyVerifactuProfile.create( { companyId: UniqueID.generateNewID(), - profileStatus: - params?.profileStatus ?? CompanyVerifactuProfileStatus.ObligatedWithVerifactu, + profileStatus: params?.profileStatus ?? CompanyVerifactuProfileStatus.ObligatedWithVerifactu, exemptionReasonCode: Maybe.none(), exemptionReasonDetail: Maybe.none(), verifactuEffectiveAt: params?.verifactuEffectiveAt == null ? Maybe.none() : Maybe.some(params.verifactuEffectiveAt), - autoRenewMode: - params?.verifactuEffectiveAt == null ? Maybe.none() : Maybe.some("enabled"), + autoRenewMode: params?.verifactuEffectiveAt == null ? Maybe.none() : Maybe.some("enabled"), defaults: { taxRegimeCode: params?.taxRegimeCode == null ? Maybe.none() : Maybe.some(params.taxRegimeCode), diff --git a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts index 4ef27830..db03472b 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/services/resolve-proforma-issue-fiscal-context.ts @@ -34,12 +34,11 @@ export function resolveProformaIssueFiscalContext( ? input.proformaTaxRegimeCode : input.customerTaxRegimeCode.isSome() ? input.customerTaxRegimeCode - : companyProfile?.defaults.taxRegimeCode ?? Maybe.none(); + : (companyProfile?.defaults.taxRegimeCode ?? Maybe.none()); const operationQualificationCode = companyProfile?.defaults.operationQualificationCode ?? Maybe.none(); - const exemptionReasonCode = - companyProfile?.defaults.exemptionReasonCode ?? Maybe.none(); + const exemptionReasonCode = companyProfile?.defaults.exemptionReasonCode ?? Maybe.none(); return { taxRegimeCode, diff --git a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-items-full-snapshot-builder.ts b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-items-full-snapshot-builder.ts index 9af329c1..46f174c9 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-items-full-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/full/issued-invoice-items-full-snapshot-builder.ts @@ -7,7 +7,7 @@ import { maybeToNullable, } from "@repo/rdx-ddd"; -import { type IssuedInvoiceItem, type IssuedInvoiceItems, ItemAmount } from "../../../../domain"; +import type { IssuedInvoiceItem, IssuedInvoiceItems } from "../../../../domain"; import type { IIssuedInvoiceItemFullSnapshot } from "./issued-invoice-item-full-snapshot.interface"; @@ -42,9 +42,7 @@ export class IssuedInvoiceItemsFullSnapshotBuilder currency_code: invoiceItem.currencyCode.toString(), })), - subtotal_amount: isValued - ? invoiceItem.subtotalAmount.toObjectString() - : emptyMoneyObject, + subtotal_amount: isValued ? invoiceItem.subtotalAmount.toObjectString() : emptyMoneyObject, item_discount_percentage: maybeToEmptyPercentageObjectString( invoiceItem.itemDiscountPercentage @@ -63,9 +61,7 @@ export class IssuedInvoiceItemsFullSnapshotBuilder ? invoiceItem.totalDiscountAmount.toObjectString() : emptyMoneyObject, - taxable_amount: isValued - ? invoiceItem.taxableAmount.toObjectString() - : emptyMoneyObject, + taxable_amount: isValued ? invoiceItem.taxableAmount.toObjectString() : emptyMoneyObject, iva_code: maybeToEmptyString(invoiceItem.ivaCode), iva_name: maybeToEmptyString(invoiceItem.ivaName), @@ -80,16 +76,10 @@ export class IssuedInvoiceItemsFullSnapshotBuilder retention_code: maybeToEmptyString(invoiceItem.retentionCode), retention_name: maybeToEmptyString(invoiceItem.retentionName), retention_percentage: maybeToEmptyPercentageObjectString(invoiceItem.retentionPercentage), - retention_amount: isValued - ? invoiceItem.retentionAmount.toObjectString() - : emptyMoneyObject, + retention_amount: isValued ? invoiceItem.retentionAmount.toObjectString() : emptyMoneyObject, - taxes_amount: isValued - ? invoiceItem.taxesAmount.toObjectString() - : emptyMoneyObject, - total_amount: isValued - ? invoiceItem.totalAmount.toObjectString() - : emptyMoneyObject, + taxes_amount: isValued ? invoiceItem.taxesAmount.toObjectString() : emptyMoneyObject, + total_amount: isValued ? invoiceItem.totalAmount.toObjectString() : emptyMoneyObject, }; } diff --git a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.ts b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.ts index 27c7a463..0312e4b6 100644 --- a/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/issued-invoices/snapshot-builders/report/issued-invoice-report-snapshot-builder.ts @@ -76,12 +76,18 @@ export class IssuedInvoiceReportSnapshotBuilder implements IIssuedInvoiceReportS private formatAddress(recipient: IIssuedInvoiceFullSnapshot["recipient"]): string { const lines: string[] = []; - if (recipient.street) lines.push(recipient.street); - if (recipient.street2) lines.push(recipient.street2); + if (recipient.street) { + lines.push(recipient.street); + } + if (recipient.street2) { + lines.push(recipient.street2); + } const cityLine = [recipient.postal_code, recipient.city].filter(Boolean).join(" "); - if (cityLine) lines.push(cityLine); + if (cityLine) { + lines.push(cityLine); + } if (recipient.province && recipient.province !== recipient.city) { lines.push(recipient.province); } diff --git a/modules/customer-invoices/src/api/application/proformas/di/proforma-creator.di.ts b/modules/customer-invoices/src/api/application/proformas/di/proforma-creator.di.ts index 541452dd..d4371427 100644 --- a/modules/customer-invoices/src/api/application/proformas/di/proforma-creator.di.ts +++ b/modules/customer-invoices/src/api/application/proformas/di/proforma-creator.di.ts @@ -10,8 +10,8 @@ import { ProformaCreator, type ProformaSeriesValidator, type ProformaTargetInvoiceSeriesValidator, - type ProformaTaxResolver, ProformaTaxConfigResolver, + type ProformaTaxResolver, } from "../services"; export const buildProformaCreator = (deps: { diff --git a/modules/customer-invoices/src/api/application/proformas/di/proforma-finder.di.ts b/modules/customer-invoices/src/api/application/proformas/di/proforma-finder.di.ts index 36ecdbe3..514b891c 100644 --- a/modules/customer-invoices/src/api/application/proformas/di/proforma-finder.di.ts +++ b/modules/customer-invoices/src/api/application/proformas/di/proforma-finder.di.ts @@ -1,5 +1,5 @@ import type { IProformaRepository } from "../repositories"; -import { type IProformaFinder, type ProformaTaxResolver, ProformaFinder } from "../services"; +import { type IProformaFinder, ProformaFinder, type ProformaTaxResolver } from "../services"; export function buildProformaFinder(params: { repository: IProformaRepository; diff --git a/modules/customer-invoices/src/api/application/proformas/di/proforma-issuer.di.ts b/modules/customer-invoices/src/api/application/proformas/di/proforma-issuer.di.ts index d4dd27be..27e5eb04 100644 --- a/modules/customer-invoices/src/api/application/proformas/di/proforma-issuer.di.ts +++ b/modules/customer-invoices/src/api/application/proformas/di/proforma-issuer.di.ts @@ -5,8 +5,8 @@ import type { } from "@erp/catalogs/api"; import type { ICustomerPublicServices } from "@erp/customers/api"; -import type { IProformaToIssuedInvoiceConverter } from "../../issued-invoices"; import type { ICompanyVerifactuProfileRepository } from "../../company-verifactu-profile"; +import type { IProformaToIssuedInvoiceConverter } from "../../issued-invoices"; import { type IProformaIssuer, ProformaIssueReadModelAssembler, ProformaIssuer } from "../services"; export const buildProformaIssuer = (params: { diff --git a/modules/customer-invoices/src/api/application/proformas/di/proforma-status-changer.di.ts b/modules/customer-invoices/src/api/application/proformas/di/proforma-status-changer.di.ts index 4fe31dfd..464114d4 100644 --- a/modules/customer-invoices/src/api/application/proformas/di/proforma-status-changer.di.ts +++ b/modules/customer-invoices/src/api/application/proformas/di/proforma-status-changer.di.ts @@ -3,8 +3,8 @@ import type { IAudit } from "@repo/rdx-audit"; import type { IProformaRepository } from "../repositories"; import { type IProformaStatusChanger, - type ProformaTaxAvailabilityChecker, ProformaStatusChanger, + type ProformaTaxAvailabilityChecker, } from "../services"; export const buildProformaStatusChanger = (params: { diff --git a/modules/customer-invoices/src/api/application/proformas/errors/company-report-profile-not-found.error.ts b/modules/customer-invoices/src/api/application/proformas/errors/company-report-profile-not-found.error.ts index 3816d96a..1f7a52b2 100644 --- a/modules/customer-invoices/src/api/application/proformas/errors/company-report-profile-not-found.error.ts +++ b/modules/customer-invoices/src/api/application/proformas/errors/company-report-profile-not-found.error.ts @@ -11,5 +11,4 @@ export class CompanyReportProfileNotFoundError extends Error { export const isCompanyReportProfileNotFoundError = ( error: unknown -): error is CompanyReportProfileNotFoundError => - error instanceof CompanyReportProfileNotFoundError; +): error is CompanyReportProfileNotFoundError => error instanceof CompanyReportProfileNotFoundError; diff --git a/modules/customer-invoices/src/api/application/proformas/errors/index.ts b/modules/customer-invoices/src/api/application/proformas/errors/index.ts index d276bdea..ba267bda 100644 --- a/modules/customer-invoices/src/api/application/proformas/errors/index.ts +++ b/modules/customer-invoices/src/api/application/proformas/errors/index.ts @@ -1,3 +1,3 @@ export * from "./company-report-profile-not-found.error"; -export * from "./proforma-taxes-unavailable.error"; export * from "./proforma-not-ready-for-issuance.error"; +export * from "./proforma-taxes-unavailable.error"; diff --git a/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.test.ts b/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.test.ts index ec16a0a6..899b096a 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/assemblers/proforma-issue-read-model.assembler.test.ts @@ -7,14 +7,17 @@ import { Maybe, Result } from "@repo/rdx-utils"; import { ProformaIssueReadModelAssembler } from "./proforma-issue-read-model.assembler"; const buildAssembler = (overrides?: { - paymentMethodResult?: Result<{ - id: UniqueID; - companyId: UniqueID; - name: string; - description: Maybe; - isActive: boolean; - isDefault: boolean; - }, Error>; + paymentMethodResult?: Result< + { + id: UniqueID; + companyId: UniqueID; + name: string; + description: Maybe; + isActive: boolean; + isDefault: boolean; + }, + Error + >; paymentTermResult?: Maybe<{ id: UniqueID; companyId: UniqueID; diff --git a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/index.ts b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/index.ts index 76b98005..6ed700bb 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/index.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/index.ts @@ -1,4 +1,4 @@ export * from "./build-proforma-tax-resolver"; export * from "./proforma-payment-resolver"; -export * from "./proforma-tax-resolver"; export * from "./proforma-tax-config-resolver"; +export * from "./proforma-tax-resolver"; diff --git a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.test.ts b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.test.ts index a64bf2ff..c5750d21 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.test.ts @@ -14,14 +14,18 @@ const buildResolver = (options?: { missing?: boolean; onLookup?: (params: unknow new ProformaTaxConfigResolver({ findActiveTaxDefinitionByCode: async (params: unknown) => { options?.onLookup?.(params); - if (options?.missing) return Result.ok(Maybe.none()); + if (options?.missing) { + return Result.ok(Maybe.none()); + } return Result.ok(Maybe.some({ allowedSurchargeCodes: new Collection(["rec_5_2"]) })); }, } as never); test("resuelve defaults fiscales y conserva flags del customer", async () => { const result = await buildResolver().resolve({ - companyId, atDate, customer: { usesEquivalenceSurcharge: false, usesRetention: false }, + companyId, + atDate, + customer: { usesEquivalenceSurcharge: false, usesRetention: false }, }); assert.equal(result.isSuccess, true); assert.equal(result.data.defaultIvaCode.unwrap(), "iva_21"); @@ -31,8 +35,14 @@ test("resuelve defaults fiscales y conserva flags del customer", async () => { test("resuelve recargo, retención y override con los códigos vigentes", async () => { const result = await buildResolver().resolve({ - companyId, atDate, customer: { usesEquivalenceSurcharge: true, usesRetention: true }, - override: { defaultIvaCode: Maybe.some("iva_21"), defaultRecCode: Maybe.some("rec_5_2"), defaultRetentionCode: Maybe.some("retention_15") }, + companyId, + atDate, + customer: { usesEquivalenceSurcharge: true, usesRetention: true }, + override: { + defaultIvaCode: Maybe.some("iva_21"), + defaultRecCode: Maybe.some("rec_5_2"), + defaultRetentionCode: Maybe.some("retention_15"), + }, }); assert.equal(result.isSuccess, true); assert.equal(result.data.defaultRecCode.unwrap(), "rec_5_2"); @@ -42,9 +52,17 @@ test("resuelve recargo, retención y override con los códigos vigentes", async test("propaga transaction y devuelve error cuando una definición fiscal no está disponible", async () => { const transaction = {}; let receivedTransaction: unknown; - const missing = buildResolver({ missing: true, onLookup: (params) => { receivedTransaction = (params as { transaction?: unknown }).transaction; } }); + const missing = buildResolver({ + missing: true, + onLookup: (params) => { + receivedTransaction = (params as { transaction?: unknown }).transaction; + }, + }); const result = await missing.resolve({ - companyId, atDate, customer: { usesEquivalenceSurcharge: true, usesRetention: false }, transaction, + companyId, + atDate, + customer: { usesEquivalenceSurcharge: true, usesRetention: false }, + transaction, }); assert.equal(result.isFailure, true); assert.equal(receivedTransaction, transaction); diff --git a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.ts b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.ts index 829e2b1b..5c957712 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-config-resolver.ts @@ -3,6 +3,7 @@ import { Maybe, Result } from "@repo/rdx-utils"; import { ProformaTaxConfig } from "../../../../domain"; import type { ProformaTaxConfigInputProps } from "../../models"; + import type { ProformaTaxResolver } from "./proforma-tax-resolver"; export interface ProformaTaxConfigCustomerContext { @@ -33,7 +34,9 @@ export class ProformaTaxConfigResolver { requestedCode: params.override?.defaultRecCode, transaction: params.transaction, }); - if (defaultRecCode.isFailure) return Result.fail(defaultRecCode.error); + if (defaultRecCode.isFailure) { + return Result.fail(defaultRecCode.error); + } const defaultRetentionCode = await this.resolveRetentionCode({ companyId: params.companyId, atDate: params.atDate, @@ -41,7 +44,9 @@ export class ProformaTaxConfigResolver { requestedCode: params.override?.defaultRetentionCode, transaction: params.transaction, }); - if (defaultRetentionCode.isFailure) return Result.fail(defaultRetentionCode.error); + if (defaultRetentionCode.isFailure) { + return Result.fail(defaultRetentionCode.error); + } return ProformaTaxConfig.create({ taxMode, defaultIvaCode, @@ -60,16 +65,27 @@ export class ProformaTaxConfigResolver { requestedCode?: Maybe; transaction?: unknown; }): Promise, Error>> { - if (!params.usesEquivalenceSurcharge || params.defaultIvaCode.isNone()) return Result.ok(Maybe.none()); + if (!params.usesEquivalenceSurcharge || params.defaultIvaCode.isNone()) { + return Result.ok(Maybe.none()); + } const ivaCode = params.defaultIvaCode.unwrap(); const ivaDefinition = await this.taxResolver.findActiveTaxDefinitionByCode({ - companyId: params.companyId, atDate: params.atDate, code: ivaCode, transaction: params.transaction, + companyId: params.companyId, + atDate: params.atDate, + code: ivaCode, + transaction: params.transaction, }); - if (ivaDefinition.isFailure) return Result.fail(ivaDefinition.error); - if (ivaDefinition.data.isNone()) return Result.fail(new Error(`Tax definition not found: ${ivaCode}`)); + if (ivaDefinition.isFailure) { + return Result.fail(ivaDefinition.error); + } + if (ivaDefinition.data.isNone()) { + return Result.fail(new Error(`Tax definition not found: ${ivaCode}`)); + } const allowed = ivaDefinition.data.unwrap().allowedSurchargeCodes.getAll(); const requested = this.maybeValue(params.requestedCode) ?? this.defaultRecForIva(ivaCode); - if (requested && allowed.includes(requested)) return Result.ok(Maybe.some(requested)); + if (requested && allowed.includes(requested)) { + return Result.ok(Maybe.some(requested)); + } const fallback = allowed[0] ?? null; return Result.ok(fallback ? Maybe.some(fallback) : Maybe.none()); } @@ -81,18 +97,29 @@ export class ProformaTaxConfigResolver { requestedCode?: Maybe; transaction?: unknown; }): Promise, Error>> { - if (!params.usesRetention) return Result.ok(Maybe.none()); + if (!params.usesRetention) { + return Result.ok(Maybe.none()); + } const retentionCode = this.maybeValue(params.requestedCode) ?? "retention_15"; const definition = await this.taxResolver.findActiveTaxDefinitionByCode({ - companyId: params.companyId, atDate: params.atDate, code: retentionCode, transaction: params.transaction, + companyId: params.companyId, + atDate: params.atDate, + code: retentionCode, + transaction: params.transaction, }); - if (definition.isFailure) return Result.fail(definition.error); - if (definition.data.isNone()) return Result.fail(new Error(`Tax definition not found: ${retentionCode}`)); + if (definition.isFailure) { + return Result.fail(definition.error); + } + if (definition.data.isNone()) { + return Result.fail(new Error(`Tax definition not found: ${retentionCode}`)); + } return Result.ok(Maybe.some(retentionCode)); } private defaultRecForIva(ivaCode: string): string | null { - return { iva_21: "rec_5_2", iva_10: "rec_1_4", iva_4: "rec_0_5", iva_0: "rec_0" }[ivaCode] ?? null; + return ( + { iva_21: "rec_5_2", iva_10: "rec_1_4", iva_4: "rec_0_5", iva_0: "rec_0" }[ivaCode] ?? null + ); } private maybeValue(value: Maybe | undefined): string | null { diff --git a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.test.ts b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.test.ts index bc2509b7..b9474eb3 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.test.ts @@ -6,9 +6,8 @@ import { Tax, TaxPercentage } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Collection, Maybe, Result } from "@repo/rdx-utils"; -import type { TaxDefinitionToTaxMapper } from "../../../common/mappers"; - import { ProformaItemTax } from "../../../../domain"; +import type { TaxDefinitionToTaxMapper } from "../../../common/mappers"; import { ProformaTaxResolver } from "./proforma-tax-resolver"; diff --git a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.ts b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.ts index 75babaca..09b48818 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/catalog-resolver/proforma-tax-resolver.ts @@ -181,7 +181,9 @@ export class ProformaTaxResolver { return Result.fail(taxResult.error); } const proformaTaxResult = this.toProformaItemTax(taxResult.data); - if (proformaTaxResult.isFailure) return Result.fail(proformaTaxResult.error); + if (proformaTaxResult.isFailure) { + return Result.fail(proformaTaxResult.error); + } liveTaxesByCode.set(proformaTaxResult.data.code, proformaTaxResult.data); } @@ -226,7 +228,9 @@ export class ProformaTaxResolver { } const proformaTax = this.toProformaItemTax(tax.data); - if (proformaTax.isFailure) return Result.fail(proformaTax.error); + if (proformaTax.isFailure) { + return Result.fail(proformaTax.error); + } return Result.ok(Maybe.some(proformaTax.data)); } diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.test.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.test.ts index 2c4d907a..78868b2b 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.test.ts @@ -32,7 +32,9 @@ class FakeAudit implements IAudit { } async recordMutation(input: RecordAuditMutationShortcutInput) { this.recordMutationCalls.push(input); - if (this.failure) return Result.fail(this.failure); + if (this.failure) { + return Result.fail(this.failure); + } return Result.ok(undefined); } } @@ -97,7 +99,9 @@ test("create hereda payment term del customer cuando no llega explicito", async return Result.ok(undefined); }, getByIdInCompany: async () => { - if (!persistedProforma) throw new Error("Proforma was not persisted before reload"); + if (!persistedProforma) { + throw new Error("Proforma was not persisted before reload"); + } return Result.ok(persistedProforma); }, } as never, @@ -182,7 +186,9 @@ test("create usa fallback a company default cuando customer no tiene payment ter return Result.ok(undefined); }, getByIdInCompany: async () => { - if (!persistedProforma) throw new Error("Proforma was not persisted before reload"); + if (!persistedProforma) { + throw new Error("Proforma was not persisted before reload"); + } return Result.ok(persistedProforma); }, } as never, @@ -270,7 +276,9 @@ test("create con payment_term_id null explicito conserva el clear", async () => return Result.ok(undefined); }, getByIdInCompany: async () => { - if (!persistedProforma) throw new Error("Proforma was not persisted before reload"); + if (!persistedProforma) { + throw new Error("Proforma was not persisted before reload"); + } return Result.ok(persistedProforma); }, } as never, @@ -368,7 +376,9 @@ test("create propaga el fallo de Audit", async () => { return Result.ok(undefined); }, getByIdInCompany: async () => { - if (!persistedProforma) throw new Error("Proforma was not persisted before reload"); + if (!persistedProforma) { + throw new Error("Proforma was not persisted before reload"); + } return Result.ok(persistedProforma); }, } as never, diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.ts index 13f5d203..22deb89d 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-creator.ts @@ -20,8 +20,8 @@ import type { ProformaCreateInputProps } from "../models"; import type { IProformaRepository } from "../repositories"; import type { ProformaPaymentResolver } from "./catalog-resolver/proforma-payment-resolver"; -import type { ProformaTaxResolver } from "./catalog-resolver/proforma-tax-resolver"; import { ProformaTaxConfigResolver } from "./catalog-resolver/proforma-tax-config-resolver"; +import type { ProformaTaxResolver } from "./catalog-resolver/proforma-tax-resolver"; import type { IProformaNumberGenerator } from "./proforma-number-generator.interface"; import type { ProformaSeriesValidator } from "./proforma-series-validator.interface"; import type { ProformaTargetInvoiceSeriesValidator } from "./proforma-target-invoice-series-validator.interface"; @@ -232,7 +232,8 @@ export class ProformaCreator implements IProformaCreator { return Result.fail(paymentMethodResult.error); } - const taxConfigResolver = this.deps.taxConfigResolver ?? new ProformaTaxConfigResolver(this.deps.taxResolver); + const taxConfigResolver = + this.deps.taxConfigResolver ?? new ProformaTaxConfigResolver(this.deps.taxResolver); const taxConfigResult = await taxConfigResolver.resolve({ companyId: _newProps.companyId, atDate: _newProps.proformaDate, @@ -358,5 +359,4 @@ export class ProformaCreator implements IProformaCreator { return Result.ok(resolvedDefaultPaymentTermId); } - } diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.test.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.test.ts index cb8f72cd..ed93cb43 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.test.ts @@ -66,9 +66,13 @@ test("duplicate persiste y recarga una nueva Proforma", async () => { }, getByIdInCompany: async () => { getByIdCalls += 1; - if (!persistedProforma) return Result.fail(new Error("must persist before reload")); + if (!persistedProforma) { + return Result.fail(new Error("must persist before reload")); + } const recipientResult = persistedProforma.update({ recipient: source.recipient }); - if (recipientResult.isFailure) return Result.fail(recipientResult.error); + if (recipientResult.isFailure) { + return Result.fail(recipientResult.error); + } return Result.ok(persistedProforma); }, } as never, @@ -186,7 +190,9 @@ test("duplicate propaga el fallo de Audit después de persistence y reload", asy }, getByIdInCompany: async () => { getByIdCalls += 1; - if (!persistedProforma) return Result.fail(new Error("must persist before reload")); + if (!persistedProforma) { + return Result.fail(new Error("must persist before reload")); + } return Result.ok(persistedProforma); }, } as never, diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.ts index 35c45660..a6368330 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-duplicator.ts @@ -70,27 +70,34 @@ export class ProformaDuplicator implements IProformaDuplicator { params: IProformaDuplicatorParams ): Promise> { const dateResult = UtcDate.createFromISO(new Date().toISOString().slice(0, 10)); - if (dateResult.isFailure) return Result.fail(dateResult.error); + if (dateResult.isFailure) { + return Result.fail(dateResult.error); + } const proformaSeriesCodeResult = await this.resolveDuplicateSeriesCode({ companyId: params.companyId, sourceDocumentSeriesId: params.source.documentSeriesId, transaction: params.transaction, }); - if (proformaSeriesCodeResult.isFailure) return Result.fail(proformaSeriesCodeResult.error); + if (proformaSeriesCodeResult.isFailure) { + return Result.fail(proformaSeriesCodeResult.error); + } const targetInvoiceSeriesCodeResult = await this.resolveValidTargetInvoiceSeriesCode({ companyId: params.companyId, targetInvoiceSeriesCode: params.source.targetInvoiceSeriesCode, transaction: params.transaction, }); - if (targetInvoiceSeriesCodeResult.isFailure) + if (targetInvoiceSeriesCodeResult.isFailure) { return Result.fail(targetInvoiceSeriesCodeResult.error); + } const paymentTermIdResult = await this.resolveDuplicatePaymentTermId({ companyId: params.companyId, sourceCustomerId: params.source.customerId, sourcePaymentTermId: params.source.paymentTermId, transaction: params.transaction, }); - if (paymentTermIdResult.isFailure) return Result.fail(paymentTermIdResult.error); + if (paymentTermIdResult.isFailure) { + return Result.fail(paymentTermIdResult.error); + } const resolvedPaymentTerm = paymentTermIdResult.data; const customerResult = await this.deps.customerServices.getCustomerById( params.source.customerId, @@ -99,7 +106,9 @@ export class ProformaDuplicator implements IProformaDuplicator { transaction: params.transaction, } ); - if (customerResult.isFailure) return Result.fail(customerResult.error); + if (customerResult.isFailure) { + return Result.fail(customerResult.error); + } const taxRegimeCode = params.source.taxRegimeCode.isNone() ? customerResult.data.taxRegimeCode.isSome() ? customerResult.data.taxRegimeCode @@ -109,31 +118,40 @@ export class ProformaDuplicator implements IProformaDuplicator { companyId: params.companyId, code: taxRegimeCode, }); - if (taxRegimeResult.isFailure) return Result.fail(taxRegimeResult.error); + if (taxRegimeResult.isFailure) { + return Result.fail(taxRegimeResult.error); + } const paymentMethodResult = await this.deps.paymentResolver.ensurePaymentMethodById({ companyId: params.companyId, id: params.source.paymentMethodId, transaction: params.transaction, }); - if (paymentMethodResult.isFailure) return Result.fail(paymentMethodResult.error); + if (paymentMethodResult.isFailure) { + return Result.fail(paymentMethodResult.error); + } const itemsResult = await this.resolveItems({ companyId: params.companyId, atDate: dateResult.data, source: params.source, }); - if (itemsResult.isFailure) return Result.fail(itemsResult.error); + if (itemsResult.isFailure) { + return Result.fail(itemsResult.error); + } const numberResult = await this.getNextNumber({ companyId: params.companyId, proformaSeriesCode: proformaSeriesCodeResult.data, targetInvoiceSeriesCode: targetInvoiceSeriesCodeResult.data, transaction: params.transaction, }); - if (numberResult.isFailure) return Result.fail(numberResult.error); + if (numberResult.isFailure) { + return Result.fail(numberResult.error); + } const referenceResult = InvoiceNumber.create(numberResult.data.reference); const numberValueResult = InvoiceNumber.create(numberResult.data.number); const seriesResult = InvoiceSerie.create(numberResult.data.documentSeriesCode); - if (referenceResult.isFailure || numberValueResult.isFailure || seriesResult.isFailure) + if (referenceResult.isFailure || numberValueResult.isFailure || seriesResult.isFailure) { return Result.fail(new Error("Invalid duplicate numbering.")); + } const proformaResult = Proforma.create( { companyId: params.companyId, @@ -163,7 +181,9 @@ export class ProformaDuplicator implements IProformaDuplicator { }, UniqueID.generateNewID() ); - if (proformaResult.isFailure) return Result.fail(proformaResult.error); + if (proformaResult.isFailure) { + return Result.fail(proformaResult.error); + } return this.persistAndReload({ companyId: params.companyId, @@ -183,14 +203,18 @@ export class ProformaDuplicator implements IProformaDuplicator { params.proforma, params.transaction ); - if (persistenceResult.isFailure) return Result.fail(persistenceResult.error); + if (persistenceResult.isFailure) { + return Result.fail(persistenceResult.error); + } const reloadResult = await this.deps.repository.getByIdInCompany( params.companyId, params.proforma.id, params.transaction ); - if (reloadResult.isFailure) return Result.fail(reloadResult.error); + if (reloadResult.isFailure) { + return Result.fail(reloadResult.error); + } const auditResult = await this.deps.audit.recordMutation({ action: "proforma.duplicated", @@ -198,7 +222,9 @@ export class ProformaDuplicator implements IProformaDuplicator { before: {}, after: toProformaAuditState(reloadResult.data), }); - if (auditResult.isFailure) return Result.fail(auditResult.error); + if (auditResult.isFailure) { + return Result.fail(auditResult.error); + } return Result.ok({ proforma: reloadResult.data, @@ -218,13 +244,17 @@ export class ProformaDuplicator implements IProformaDuplicator { proformaSeriesCode: params.proformaSeriesCode, transaction: params.transaction, }); - if (seriesValidationResult.isFailure) return Result.fail(seriesValidationResult.error); + if (seriesValidationResult.isFailure) { + return Result.fail(seriesValidationResult.error); + } const numberResult = await this.deps.numberService.getNextForCompany( params.companyId, params.proformaSeriesCode, params.transaction ); - if (numberResult.isFailure) return Result.fail(numberResult.error); + if (numberResult.isFailure) { + return Result.fail(numberResult.error); + } const targetValidationResult = await this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ companyId: params.companyId, @@ -246,7 +276,9 @@ export class ProformaDuplicator implements IProformaDuplicator { documentType: "proforma", transaction: params.transaction, }); - if (listResult.isFailure) return Result.fail(listResult.error); + if (listResult.isFailure) { + return Result.fail(listResult.error); + } const sourceSeries = listResult.data.find( (series) => series.id === params.sourceDocumentSeriesId.getOrUndefined() ); @@ -269,13 +301,17 @@ export class ProformaDuplicator implements IProformaDuplicator { targetInvoiceSeriesCode: Maybe; transaction?: unknown; }): Promise, Error>> { - if (params.targetInvoiceSeriesCode.isNone()) return Result.ok(Maybe.none()); + if (params.targetInvoiceSeriesCode.isNone()) { + return Result.ok(Maybe.none()); + } const listResult = await this.deps.documentSeriesServices.listActiveSeries({ companyId: params.companyId.toString(), documentType: "issued_invoice", transaction: params.transaction, }); - if (listResult.isFailure) return Result.fail(listResult.error); + if (listResult.isFailure) { + return Result.fail(listResult.error); + } const targetCode = params.targetInvoiceSeriesCode.unwrap().toPrimitive(); return Result.ok( listResult.data.some((series) => series.code === targetCode) @@ -314,7 +350,9 @@ export class ProformaDuplicator implements IProformaDuplicator { transaction: params.transaction, } ); - if (customerResult.isFailure) return Result.fail(customerResult.error); + if (customerResult.isFailure) { + return Result.fail(customerResult.error); + } const customerResultPaymentTerm = await this.resolveActivePaymentTermId({ companyId: params.companyId, paymentTermId: customerResult.data.paymentTermId, @@ -335,7 +373,9 @@ export class ProformaDuplicator implements IProformaDuplicator { companyId: params.companyId, transaction: params.transaction, }); - if (companyResult.isFailure) return Result.fail(companyResult.error); + if (companyResult.isFailure) { + return Result.fail(companyResult.error); + } if (companyResult.data.isNone()) { return Result.fail(new Error("Company not found while duplicating proforma.")); } @@ -347,13 +387,17 @@ export class ProformaDuplicator implements IProformaDuplicator { }); } const idResult = UniqueID.create(defaultPaymentTermId); - if (idResult.isFailure) return Result.fail(idResult.error); + if (idResult.isFailure) { + return Result.fail(idResult.error); + } const companyPaymentTermResult = await this.resolveActivePaymentTermId({ companyId: params.companyId, paymentTermId: Maybe.some(idResult.data), transaction: params.transaction, }); - if (companyPaymentTermResult.isFailure) return Result.fail(companyPaymentTermResult.error); + if (companyPaymentTermResult.isFailure) { + return Result.fail(companyPaymentTermResult.error); + } return Result.ok({ paymentTermId: companyPaymentTermResult.data, paymentTermAdaptation: { @@ -368,13 +412,17 @@ export class ProformaDuplicator implements IProformaDuplicator { paymentTermId: Maybe; transaction?: unknown; }): Promise, Error>> { - if (params.paymentTermId.isNone()) return Result.ok(Maybe.none()); + if (params.paymentTermId.isNone()) { + return Result.ok(Maybe.none()); + } const result = await this.deps.paymentTermFinder.findByIdInCompany({ companyId: params.companyId, id: params.paymentTermId.unwrap(), transaction: params.transaction, }); - if (result.isFailure) return Result.fail(result.error); + if (result.isFailure) { + return Result.fail(result.error); + } return result.data.isSome() && result.data.unwrap().isActive ? Result.ok(params.paymentTermId) : Result.ok(Maybe.none()); @@ -396,7 +444,9 @@ export class ProformaDuplicator implements IProformaDuplicator { retentionCode: item.retentionCode(), }, }); - if (taxesResult.isFailure) return Result.fail(taxesResult.error); + if (taxesResult.isFailure) { + return Result.fail(taxesResult.error); + } items.push({ description: item.description, quantity: item.quantity, diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.test.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.test.ts index 6ccabecd..ff9ffea6 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.test.ts @@ -89,7 +89,9 @@ const buildApprovedProforma = (params?: { test("check devuelve canIssue true cuando la proforma y el payment method son válidos", async () => { const checker = new ProformaIssuanceReadinessChecker({ - taxAvailabilityChecker: { checkProformaTaxes: async () => Result.ok(new Collection([])) } as never, + taxAvailabilityChecker: { + checkProformaTaxes: async () => Result.ok(new Collection([])), + } as never, paymentMethodFinder: { findByIdInCompany: async () => Result.ok( @@ -118,7 +120,9 @@ test("check devuelve canIssue true cuando la proforma y el payment method son v test("check acumula blockers internos sin abortar en el primero", async () => { const checker = new ProformaIssuanceReadinessChecker({ - taxAvailabilityChecker: { checkProformaTaxes: async () => Result.ok(new Collection([])) } as never, + taxAvailabilityChecker: { + checkProformaTaxes: async () => Result.ok(new Collection([])), + } as never, paymentMethodFinder: { findByIdInCompany: async () => Result.ok(Maybe.none()), } as never, @@ -152,7 +156,9 @@ test("check acumula blockers internos sin abortar en el primero", async () => { test("check informa payment_method_unavailable cuando el finder no resuelve la referencia company-scoped", async () => { const checker = new ProformaIssuanceReadinessChecker({ - taxAvailabilityChecker: { checkProformaTaxes: async () => Result.ok(new Collection([])) } as never, + taxAvailabilityChecker: { + checkProformaTaxes: async () => Result.ok(new Collection([])), + } as never, paymentMethodFinder: { findByIdInCompany: async () => Result.ok(Maybe.none()), } as never, @@ -173,7 +179,9 @@ test("check informa payment_method_unavailable cuando el finder no resuelve la r test("check informa payment_method_inactive cuando la referencia resuelta está inactiva", async () => { const checker = new ProformaIssuanceReadinessChecker({ - taxAvailabilityChecker: { checkProformaTaxes: async () => Result.ok(new Collection([])) } as never, + taxAvailabilityChecker: { + checkProformaTaxes: async () => Result.ok(new Collection([])), + } as never, paymentMethodFinder: { findByIdInCompany: async () => Result.ok( diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.ts index 3d58a5d7..696eb108 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuance-readiness-checker.ts @@ -4,6 +4,7 @@ import { Result } from "@repo/rdx-utils"; import type { Proforma } from "../../../domain"; import type { ProformaIssuanceReadinessModel } from "../models"; + import type { ProformaTaxAvailabilityChecker } from "./proforma-tax-availability-checker"; export interface IProformaIssuanceReadinessChecker { diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuer.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuer.ts index 031d7a44..a40a2efe 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-issuer.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-issuer.ts @@ -3,8 +3,8 @@ import { Result } from "@repo/rdx-utils"; import type { IProformaToIssuedInvoiceConverter, - ProformaToIssuedInvoiceCreateProps, ProformaIssueReadModel, + ProformaToIssuedInvoiceCreateProps, } from "../../issued-invoices"; export interface IProformaIssuerParams { diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-status-changer.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-status-changer.ts index a5115e78..8f84e568 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-status-changer.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-status-changer.ts @@ -10,6 +10,7 @@ import { } from "../audit/proforma-audit"; import { ProformaTaxesUnavailableError } from "../errors"; import type { IProformaRepository } from "../repositories"; + import type { ProformaTaxAvailabilityChecker } from "./proforma-tax-availability-checker"; export interface IProformaStatusChanger { diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-tax-availability-checker.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-tax-availability-checker.ts index 6953e50b..57de9644 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-tax-availability-checker.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-tax-availability-checker.ts @@ -16,9 +16,7 @@ export interface ProformaTaxAvailabilityEntry { } export class ProformaTaxAvailabilityChecker { - public constructor( - private readonly taxDefinitionFinder: ITaxDefinitionPublicFinder - ) {} + public constructor(private readonly taxDefinitionFinder: ITaxDefinitionPublicFinder) {} public async checkProformaTaxes(params: { companyId: UniqueID; @@ -41,7 +39,9 @@ export class ProformaTaxAvailabilityChecker { } } } - const codes = new Collection(Array.from(new Set(references.map((reference) => reference.tax.code)))); + const codes = new Collection( + Array.from(new Set(references.map((reference) => reference.tax.code))) + ); if (codes.size() === 0) { return Result.ok(new Collection([])); diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts index e9442325..04f7e11b 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/full/proforma-items-full-snapshot-builder.ts @@ -1,10 +1,18 @@ import type { ProformaItemDetailDTO } from "@erp/customer-invoices/common"; import { maybeToNullable } from "@repo/rdx-ddd"; -import { ItemAmount, type ProformaCalculationContext, type ProformaItem, type ProformaItems } from "../../../../domain"; +import { + ItemAmount, + type ProformaCalculationContext, + type ProformaItem, + type ProformaItems, +} from "../../../../domain"; export interface IProformaItemsFullSnapshotBuilder { - toOutput(invoiceItems: ProformaItems, context: ProformaCalculationContext): ProformaItemDetailDTO[]; + toOutput( + invoiceItems: ProformaItems, + context: ProformaCalculationContext + ): ProformaItemDetailDTO[]; } export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnapshotBuilder { @@ -88,7 +96,10 @@ export class ProformaItemsFullSnapshotBuilder implements IProformaItemsFullSnaps }; } - toOutput(invoiceItems: ProformaItems, context: ProformaCalculationContext): ProformaItemDetailDTO[] { + toOutput( + invoiceItems: ProformaItems, + context: ProformaCalculationContext + ): ProformaItemDetailDTO[] { return invoiceItems.map((item, index) => this.mapItem(item, context, index)); } } diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issuance-readiness/proforma-issuance-readiness.snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issuance-readiness/proforma-issuance-readiness.snapshot-builder.ts index 66d275ae..ded74c47 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issuance-readiness/proforma-issuance-readiness.snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/issuance-readiness/proforma-issuance-readiness.snapshot-builder.ts @@ -4,14 +4,15 @@ import type { GetProformaIssuanceReadinessResponseDTO } from "../../../../../com import type { ProformaIssuanceReadinessModel } from "../../models"; export interface IProformaIssuanceReadinessSnapshotBuilder - extends ISnapshotBuilder {} + extends ISnapshotBuilder< + ProformaIssuanceReadinessModel, + GetProformaIssuanceReadinessResponseDTO + > {} export class ProformaIssuanceReadinessSnapshotBuilder implements IProformaIssuanceReadinessSnapshotBuilder { - public toOutput( - source: ProformaIssuanceReadinessModel - ): GetProformaIssuanceReadinessResponseDTO { + public toOutput(source: ProformaIssuanceReadinessModel): GetProformaIssuanceReadinessResponseDTO { return { can_issue: source.canIssue, blockers: source.blockers.map((blocker) => ({ diff --git a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/report/proforma-report-snapshot-builder.ts b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/report/proforma-report-snapshot-builder.ts index e13edf10..2e552b96 100644 --- a/modules/customer-invoices/src/api/application/proformas/snapshot-builders/report/proforma-report-snapshot-builder.ts +++ b/modules/customer-invoices/src/api/application/proformas/snapshot-builders/report/proforma-report-snapshot-builder.ts @@ -117,12 +117,18 @@ export class ProformaReportSnapshotBuilder implements IProformaReportSnapshotBui private formatAddress(recipient: ProformaFullSnapshot["recipient"]): string { const lines: string[] = []; - if (recipient.street) lines.push(recipient.street); - if (recipient.street2) lines.push(recipient.street2); + if (recipient.street) { + lines.push(recipient.street); + } + if (recipient.street2) { + lines.push(recipient.street2); + } const cityLine = [recipient.postal_code, recipient.city].filter(Boolean).join(" "); - if (cityLine) lines.push(cityLine); + if (cityLine) { + lines.push(cityLine); + } if (recipient.province && recipient.province !== recipient.city) { lines.push(recipient.province); } diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/archive-proforma-by-id.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/archive-proforma-by-id.use-case.ts index c2e432e8..ed3d762a 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/archive-proforma-by-id.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/archive-proforma-by-id.use-case.ts @@ -2,7 +2,11 @@ import type { ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import type { IProformaArchiver, IProformaFinder, IProformaFullReadModelAssembler } from "../services"; +import type { + IProformaArchiver, + IProformaFinder, + IProformaFullReadModelAssembler, +} from "../services"; import type { IProformaFullSnapshotBuilder } from "../snapshot-builders"; type ArchiveProformaByIdUseCaseInput = { diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/duplicate-proforma-by-id.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/duplicate-proforma-by-id.use-case.ts index 931bfa24..a1d63ffb 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/duplicate-proforma-by-id.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/duplicate-proforma-by-id.use-case.ts @@ -28,7 +28,9 @@ export class DuplicateProformaByIdUseCase { public async execute(params: DuplicateProformaByIdUseCaseInput) { const proformaIdResult = UniqueID.create(params.params.proforma_id); - if (proformaIdResult.isFailure) return Result.fail(proformaIdResult.error); + if (proformaIdResult.isFailure) { + return Result.fail(proformaIdResult.error); + } return this.deps.transactionManager.complete(async (transaction: unknown) => { try { @@ -37,21 +39,27 @@ export class DuplicateProformaByIdUseCase { proformaIdResult.data, transaction ); - if (sourceResult.isFailure) return Result.fail(sourceResult.error); + if (sourceResult.isFailure) { + return Result.fail(sourceResult.error); + } const duplicatedResult = await this.deps.duplicator.duplicate({ companyId: params.companyId, source: sourceResult.data, transaction, }); - if (duplicatedResult.isFailure) return Result.fail(duplicatedResult.error); + if (duplicatedResult.isFailure) { + return Result.fail(duplicatedResult.error); + } const readModelResult = await this.deps.fullReadModelAssembler.assemble({ companyId: params.companyId, proforma: duplicatedResult.data.proforma, transaction, }); - if (readModelResult.isFailure) return Result.fail(readModelResult.error); + if (readModelResult.isFailure) { + return Result.fail(readModelResult.error); + } return Result.ok({ ...this.deps.fullSnapshotBuilder.toOutput(readModelResult.data), diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/index.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/index.ts index d7444e47..6edd4274 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/index.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/index.ts @@ -1,8 +1,8 @@ export * from "./archive-proforma-by-id.use-case"; export * from "./change-status-proforma.use-case"; export * from "./create-proforma.use-case"; -export * from "./duplicate-proforma-by-id.use-case"; export * from "./delete-proforma-by-id.use-case"; +export * from "./duplicate-proforma-by-id.use-case"; export * from "./get-proforma-by-id.use-case"; export * from "./get-proforma-issuance-readiness.use-case"; export * from "./issue-proforma.use-case"; diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts index b9397721..d89a9266 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.test.ts @@ -55,7 +55,9 @@ class SuccessAudit implements IAudit { } const required = (result: Result): T => { - if (result.isFailure) throw result.error; + if (result.isFailure) { + throw result.error; + } return result.data; }; diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts index 9fec19b5..2d4b2dba 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/issue-proforma.use-case.ts @@ -3,9 +3,9 @@ import type { IAudit } from "@repo/rdx-audit"; import { UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; +import { VerifactuRecord, VerifactuRecordEstado } from "../../../domain"; import type { IIssuedInvoicePublicServices } from "../../issued-invoices"; import { toIssuedInvoiceAuditState } from "../../issued-invoices/audit/issued-invoice-audit"; -import { VerifactuRecord, VerifactuRecordEstado } from "../../../domain"; import type { IssueProformaCommand } from "../commands"; import { ProformaNotReadyForIssuanceError } from "../errors"; import type { IProformaRepository } from "../repositories"; diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/unarchive-proforma-by-id.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/unarchive-proforma-by-id.use-case.ts index 9258dde8..c7987607 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/unarchive-proforma-by-id.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/unarchive-proforma-by-id.use-case.ts @@ -2,7 +2,11 @@ import type { ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import type { IProformaArchiver, IProformaFinder, IProformaFullReadModelAssembler } from "../services"; +import type { + IProformaArchiver, + IProformaFinder, + IProformaFullReadModelAssembler, +} from "../services"; import type { IProformaFullSnapshotBuilder } from "../snapshot-builders"; type UnarchiveProformaByIdUseCaseInput = { diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/audit/recurring-proforma-audit.ts b/modules/customer-invoices/src/api/application/recurring-proformas/audit/recurring-proforma-audit.ts index 0e61a934..ac5533c2 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/audit/recurring-proforma-audit.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/audit/recurring-proforma-audit.ts @@ -1,7 +1,9 @@ import type { RecurringProforma } from "../../../domain"; -const nullable = (value: { isSome(): boolean; unwrap(): T }, map: (item: T) => U): U | null => - value.isSome() ? map(value.unwrap()) : null; +const nullable = ( + value: { isSome(): boolean; unwrap(): T }, + map: (item: T) => U +): U | null => (value.isSome() ? map(value.unwrap()) : null); export const RECURRING_PROFORMA_AUDIT_ACTIONS = { CREATED: "recurring_proforma.created", diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/index.ts b/modules/customer-invoices/src/api/application/recurring-proformas/index.ts index 6066b63f..083fddde 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/index.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/index.ts @@ -1,4 +1,4 @@ -export * from "./repositories"; export * from "./audit"; export * from "./models"; +export * from "./repositories"; export * from "./services"; diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/repositories/index.ts b/modules/customer-invoices/src/api/application/recurring-proformas/repositories/index.ts index 9f7a65a0..4db44ce9 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/repositories/index.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/repositories/index.ts @@ -1,2 +1,2 @@ -export * from "./recurring-proforma-repository.interface"; export * from "./recurring-proforma-occurrence-repository.interface"; +export * from "./recurring-proforma-repository.interface"; diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/repositories/recurring-proforma-repository.interface.ts b/modules/customer-invoices/src/api/application/recurring-proformas/repositories/recurring-proforma-repository.interface.ts index 266736b4..89f54c7d 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/repositories/recurring-proforma-repository.interface.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/repositories/recurring-proforma-repository.interface.ts @@ -36,10 +36,7 @@ export interface IRecurringProformaRepository { companyId: UniqueID, transaction?: unknown ): Promise>; - findDue( - dueThrough: Date, - limit: number - ): Promise>; + findDue(dueThrough: Date, limit: number): Promise>; listInCompany( query: ListRecurringProformasQuery, transaction?: unknown diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/create-recurring-proforma-occurrence-processor.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/create-recurring-proforma-occurrence-processor.ts index afc2ab93..f28843e5 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/create-recurring-proforma-occurrence-processor.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/create-recurring-proforma-occurrence-processor.ts @@ -5,6 +5,7 @@ import type { IRecurringProformaOccurrenceRepository, IRecurringProformaRepository, } from "../repositories"; + import type { RecurringProformaMaterializer } from "./recurring-proforma-materializer"; import { RecurringProformaOccurrenceProcessor } from "./recurring-proforma-occurrence-processor"; diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/index.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/index.ts index b7a5fd76..5639f378 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/index.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/index.ts @@ -1,8 +1,8 @@ -export * from "./recurring-proforma-services"; -export * from "./recurring-proforma-updater"; +export * from "./create-recurring-proforma-occurrence-processor"; export * from "./recurring-proforma-activation-readiness-checker"; +export * from "./recurring-proforma-capabilities"; export * from "./recurring-proforma-from-proforma-creator"; export * from "./recurring-proforma-materializer"; export * from "./recurring-proforma-occurrence-processor"; -export * from "./create-recurring-proforma-occurrence-processor"; -export * from "./recurring-proforma-capabilities"; +export * from "./recurring-proforma-services"; +export * from "./recurring-proforma-updater"; diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-activation-readiness-checker.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-activation-readiness-checker.ts index bf40c1ee..55e47036 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-activation-readiness-checker.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-activation-readiness-checker.ts @@ -46,10 +46,16 @@ export class RecurringProformaActivationReadinessChecker { recurringProforma.customerId.unwrap(), { companyId: params.companyId, transaction: params.transaction } ); - if (customer.isFailure) blockers.push({ code: "customer_unavailable" }); + if (customer.isFailure) { + blockers.push({ code: "customer_unavailable" }); + } + } + if (recurringProforma.items.size() === 0) { + blockers.push({ code: "missing_items" }); + } + if (recurringProforma.schedule.isNone()) { + blockers.push({ code: "missing_schedule" }); } - if (recurringProforma.items.size() === 0) blockers.push({ code: "missing_items" }); - if (recurringProforma.schedule.isNone()) blockers.push({ code: "missing_schedule" }); const paymentTerm = await this.deps.paymentResolver.ensurePaymentTermById({ companyId: params.companyId, id: recurringProforma.paymentTermId, @@ -72,15 +78,20 @@ export class RecurringProformaActivationReadinessChecker { proformaSeriesCode: recurringProforma.proformaSeries, transaction: params.transaction, }); - if (proformaSeries.isFailure) blockers.push({ code: "invalid_proforma_series" }); + if (proformaSeries.isFailure) { + blockers.push({ code: "invalid_proforma_series" }); + } } if (recurringProforma.issuedInvoiceSeries.isSome()) { - const issuedInvoiceSeries = await this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ - companyId: params.companyId, - targetInvoiceSeriesCode: recurringProforma.issuedInvoiceSeries, - transaction: params.transaction, - }); - if (issuedInvoiceSeries.isFailure) blockers.push({ code: "invalid_issued_invoice_series" }); + const issuedInvoiceSeries = + await this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ + companyId: params.companyId, + targetInvoiceSeriesCode: recurringProforma.issuedInvoiceSeries, + transaction: params.transaction, + }); + if (issuedInvoiceSeries.isFailure) { + blockers.push({ code: "invalid_issued_invoice_series" }); + } } return Result.ok({ canActivate: blockers.length === 0, blockers }); } diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.test.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.test.ts index b46b76aa..53aa36e0 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.test.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.test.ts @@ -38,7 +38,7 @@ const transactionManager = (): ITransactionManager => ({ const buildRecurring = () => { const currency = required(CurrencyCode.create("EUR")); - const language = required(LanguageCode.create("es")); + const _language = required(LanguageCode.create("es")); const discount = required(DiscountPercentage.create({ value: 500 })); const schedule = required( RecurrenceSchedule.create({ diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.ts index 7397fba2..da400be8 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-capabilities.ts @@ -42,7 +42,9 @@ export class RecurringProformaReader { public list(query: ListRecurringProformasQuery) { return this.transactionManager.complete(async (transaction) => { const page = await this.repository.listInCompany(query, transaction); - if (page.isFailure) return Result.fail(page.error); + if (page.isFailure) { + return Result.fail(page.error); + } const customerIds = [ ...new Map( @@ -51,13 +53,17 @@ export class RecurringProformaReader { .map((customerId) => [customerId.toPrimitive(), customerId]) ).values(), ]; - if (customerIds.length === 0) return page; + if (customerIds.length === 0) { + return page; + } const customers = await this.customerServices.findSummariesByIds(customerIds, { companyId: query.companyId, transaction, }); - if (customers.isFailure) return Result.fail(customers.error); + if (customers.isFailure) { + return Result.fail(customers.error); + } const customersById = new Map( customers.data.map((customer) => [customer.id.toPrimitive(), customer]) @@ -81,26 +87,34 @@ export class RecurringProformaReader { params.id, transaction ); - if (found.isFailure) return Result.fail(found.error); + if (found.isFailure) { + return Result.fail(found.error); + } const hasIssue = await this.occurrenceRepository.hasFailedOccurrences( params.id, params.companyId, transaction ); - if (hasIssue.isFailure) return Result.fail(hasIssue.error); + if (hasIssue.isFailure) { + return Result.fail(hasIssue.error); + } const customerId = found.data.customerId.match( (value) => value, () => null ); - if (!customerId) return Result.ok(this.toDetail(found.data, hasIssue.data, null)); + if (!customerId) { + return Result.ok(this.toDetail(found.data, hasIssue.data, null)); + } const customers = await this.customerServices.findSummariesByIds([customerId], { companyId: params.companyId, transaction, }); - if (customers.isFailure) return Result.fail(customers.error); + if (customers.isFailure) { + return Result.fail(customers.error); + } return Result.ok(this.toDetail(found.data, hasIssue.data, customers.data[0] ?? null)); }); @@ -305,7 +319,9 @@ export class RecurringProformaCommands { params.id, transaction ); - if (recurringProforma.isFailure) return Result.fail(recurringProforma.error); + if (recurringProforma.isFailure) { + return Result.fail(recurringProforma.error); + } return this.deps.readinessChecker.check({ companyId: params.companyId, recurringProforma: recurringProforma.data, diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.test.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.test.ts index 3ae1c154..98d29869 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.test.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.test.ts @@ -9,7 +9,7 @@ import { ItemAmount, ItemDescription, ItemQuantity, - RecurringProforma, + type RecurringProforma, type RecurringProformaItemCreateProps, } from "../../../domain"; @@ -17,7 +17,9 @@ import { RecurringProformaFromProformaCreator } from "./recurring-proforma-from- const required = (result: { data: T }): T => result.data; -const buildRecurringItem = (overrides?: Partial): RecurringProformaItemCreateProps => { +const buildRecurringItem = ( + overrides?: Partial +): RecurringProformaItemCreateProps => { const currency = required(CurrencyCode.create("EUR")); return { diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.ts index e030a83f..c036f341 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-from-proforma-creator.ts @@ -38,9 +38,13 @@ export class RecurringProformaFromProformaCreator { params.proformaId, params.transaction ); - if (source.isFailure) return Result.fail(source.error); + if (source.isFailure) { + return Result.fail(source.error); + } const schedule = RecurrenceSchedule.create(params.schedule); - if (schedule.isFailure) return Result.fail(schedule.error); + if (schedule.isFailure) { + return Result.fail(schedule.error); + } const items: RecurringProformaItemCreateProps[] = source.data.items.getAll().map((item) => ({ description: item.description, quantity: item.quantity, @@ -50,24 +54,34 @@ export class RecurringProformaFromProformaCreator { recCode: item.recCode(), retentionCode: item.retentionCode(), })); - const recurring = RecurringProforma.create({ - companyId: params.companyId, - name: params.name, - customerId: Maybe.some(source.data.customerId), - description: source.data.description, - schedule: Maybe.some(schedule.data), - globalDiscountPercentage: Maybe.some(source.data.globalDiscountPercentage), - paymentTermId: source.data.paymentTermId, - paymentMethodId: source.data.paymentMethodId, - language: Maybe.some(source.data.languageCode), - currency: Maybe.some(source.data.currencyCode), - proformaSeries: source.data.proformaSeriesCode, - issuedInvoiceSeries: source.data.targetInvoiceSeriesCode, - items, - }, params.id); - if (recurring.isFailure) return Result.fail(recurring.error); - const saved = await this.deps.recurringProformaRepository.save(recurring.data, params.transaction); - if (saved.isFailure) return Result.fail(saved.error); + const recurring = RecurringProforma.create( + { + companyId: params.companyId, + name: params.name, + customerId: Maybe.some(source.data.customerId), + description: source.data.description, + schedule: Maybe.some(schedule.data), + globalDiscountPercentage: Maybe.some(source.data.globalDiscountPercentage), + paymentTermId: source.data.paymentTermId, + paymentMethodId: source.data.paymentMethodId, + language: Maybe.some(source.data.languageCode), + currency: Maybe.some(source.data.currencyCode), + proformaSeries: source.data.proformaSeriesCode, + issuedInvoiceSeries: source.data.targetInvoiceSeriesCode, + items, + }, + params.id + ); + if (recurring.isFailure) { + return Result.fail(recurring.error); + } + const saved = await this.deps.recurringProformaRepository.save( + recurring.data, + params.transaction + ); + if (saved.isFailure) { + return Result.fail(saved.error); + } const audit = await this.deps.audit.recordMutation({ action: RECURRING_PROFORMA_AUDIT_ACTIONS.CREATED_FROM_PROFORMA, target: toRecurringProformaAuditTarget(recurring.data), diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.test.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.test.ts index 744de179..7d4afe5b 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.test.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.test.ts @@ -5,6 +5,7 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { RecurringProforma } from "../../../domain"; + import { RecurringProformaMaterializer } from "./recurring-proforma-materializer"; test("RecurringProformaMaterializer rechaza una plantilla sin customer antes de crear Proforma", async () => { @@ -19,9 +20,20 @@ test("RecurringProformaMaterializer rechaza una plantilla sin customer antes de taxResolver: {} as never, proformaSeriesValidator: {} as never, targetInvoiceSeriesValidator: {} as never, - proformaCreator: { create: async () => { creatorCalls += 1; return Result.fail(new Error("not used")); } } as never, + proformaCreator: { + create: async () => { + creatorCalls += 1; + return Result.fail(new Error("not used")); + }, + } as never, + }); + const result = await materializer.materialize({ + recurringProforma: recurring, + companyId, + scheduledDate: new Date("2026-09-01"), + generationDate: new Date("2026-09-15"), + transaction: {}, }); - const result = await materializer.materialize({ recurringProforma: recurring, companyId, scheduledDate: new Date("2026-09-01"), generationDate: new Date("2026-09-15"), transaction: {} }); assert.equal(result.isFailure, true); assert.equal(creatorCalls, 0); }); diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.ts index 81d95b45..38c25d5b 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-materializer.ts @@ -39,18 +39,25 @@ export class RecurringProformaMaterializer { transaction: unknown; }): Promise> { const customerId = params.recurringProforma.customerId; - if (customerId.isNone()) + if (customerId.isNone()) { return Result.fail(new Error("Recurring proforma customer is required.")); + } const customerResult = await this.deps.customerServices.getCustomerById(customerId.unwrap(), { companyId: params.companyId, transaction: params.transaction, }); - if (customerResult.isFailure) return Result.fail(customerResult.error); + if (customerResult.isFailure) { + return Result.fail(customerResult.error); + } const customer = customerResult.data; const date = UtcDate.createFromDate(params.generationDate); - if (date.isFailure) return Result.fail(date.error); + if (date.isFailure) { + return Result.fail(date.error); + } const paymentTerm = await this.resolvePaymentTerm(params.recurringProforma, customer, params); - if (paymentTerm.isFailure) return Result.fail(paymentTerm.error); + if (paymentTerm.isFailure) { + return Result.fail(paymentTerm.error); + } const paymentMethod = params.recurringProforma.paymentMethodId.isSome() ? params.recurringProforma.paymentMethodId : customer.paymentMethodId; @@ -59,7 +66,9 @@ export class RecurringProformaMaterializer { id: paymentMethod, transaction: params.transaction, }); - if (paymentMethodValidation.isFailure) return Result.fail(paymentMethodValidation.error); + if (paymentMethodValidation.isFailure) { + return Result.fail(paymentMethodValidation.error); + } const languageCode = params.recurringProforma.language.isSome() ? params.recurringProforma.language.unwrap() : customer.languageCode; @@ -67,14 +76,18 @@ export class RecurringProformaMaterializer { ? params.recurringProforma.currency.unwrap() : customer.currencyCode; const series = await this.validateSeries(params.recurringProforma, params); - if (series.isFailure) return Result.fail(series.error); + if (series.isFailure) { + return Result.fail(series.error); + } const taxConfig = await this.deps.taxConfigResolver.resolve({ companyId: params.companyId, atDate: date.data, customer, transaction: params.transaction, }); - if (taxConfig.isFailure) return Result.fail(taxConfig.error); + if (taxConfig.isFailure) { + return Result.fail(taxConfig.error); + } const items: ProformaCreateInputProps["items"] = []; @@ -89,7 +102,9 @@ export class RecurringProformaMaterializer { retentionCode: item.retentionCode, }, }); - if (taxes.isFailure) return Result.fail(taxes.error); + if (taxes.isFailure) { + return Result.fail(taxes.error); + } items.push({ description: item.description, quantity: item.quantity, @@ -113,9 +128,13 @@ export class RecurringProformaMaterializer { province: customer.address.province, country: customer.address.countryCode, }); - if (recipient.isFailure) return Result.fail(recipient.error); + if (recipient.isFailure) { + return Result.fail(recipient.error); + } const status = InvoiceStatus.create("draft"); - if (status.isFailure) return Result.fail(status.error); + if (status.isFailure) { + return Result.fail(status.error); + } const created = await this.deps.proformaCreator.create({ companyId: params.companyId, id: UniqueID.generateNewID(), @@ -158,7 +177,9 @@ export class RecurringProformaMaterializer { proformaSeriesCode: recurring.proformaSeries, transaction: params.transaction, }); - if (proforma.isFailure) return Result.fail(proforma.error); + if (proforma.isFailure) { + return Result.fail(proforma.error); + } return this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ companyId: params.companyId, targetInvoiceSeriesCode: recurring.issuedInvoiceSeries, diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.test.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.test.ts index 3d2d4617..514cc478 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.test.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.test.ts @@ -40,7 +40,9 @@ const createProcessorRecurring = (params: { { description: Maybe.some(required(ItemDescription.create("Servicio"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 100, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 100, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }, ], @@ -66,10 +68,17 @@ const createObservedProcessor = (params: { const transactions = params.transactions ?? [{ id: "main" }]; const locks: unknown[][] = []; const lookups: unknown[][] = []; - const materializerCalls: Array<{ scheduledDate: Date; generationDate: Date; transaction: unknown }> = []; + const materializerCalls: Array<{ + scheduledDate: Date; + generationDate: Date; + transaction: unknown; + }> = []; const audits: Array<{ action: string; transaction: unknown }> = []; const processor = new RecurringProformaOccurrenceProcessor({ - transactionManager: { complete: async (work: (transaction: unknown) => Promise) => work(transactions[transactionIndex++]) } as never, + transactionManager: { + complete: async (work: (transaction: unknown) => Promise) => + work(transactions[transactionIndex++]), + } as never, recurringProformaRepository: { findByIdInCompanyForUpdate: async (...args: unknown[]) => { locks.push(args); @@ -88,7 +97,12 @@ const createObservedProcessor = (params: { }, } as never, materializer: { - materialize: async (input: { recurringProforma: RecurringProforma; scheduledDate: Date; generationDate: Date; transaction: unknown }) => { + materialize: async (input: { + recurringProforma: RecurringProforma; + scheduledDate: Date; + generationDate: Date; + transaction: unknown; + }) => { materializerCalls.push(input); return params.materialize(input); }, @@ -100,7 +114,16 @@ const createObservedProcessor = (params: { }, } as never, }); - return { processor, locks, lookups, materializerCalls, audits, get occurrence() { return occurrence; } }; + return { + processor, + locks, + lookups, + materializerCalls, + audits, + get occurrence() { + return occurrence; + }, + }; }; test("first success materializes and persists one generated occurrence", async () => { @@ -132,7 +155,9 @@ test("first success materializes and persists one generated occurrence", async ( { description: Maybe.some(required(ItemDescription.create("Servicio"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 100, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 100, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }, ], @@ -146,7 +171,11 @@ test("first success materializes and persists one generated occurrence", async ( let recurringSaveTransaction: unknown; let lockCall: unknown[] | undefined; let lookupCall: unknown[] | undefined; - const materializerCalls: Array<{ scheduledDate: Date; generationDate: Date; transaction: unknown }> = []; + const materializerCalls: Array<{ + scheduledDate: Date; + generationDate: Date; + transaction: unknown; + }> = []; const audits: Array<{ action: string; transaction: unknown }> = []; const processor = new RecurringProformaOccurrenceProcessor({ @@ -175,7 +204,11 @@ test("first success materializes and persists one generated occurrence", async ( }, } as never, materializer: { - materialize: async (params: { scheduledDate: Date; generationDate: Date; transaction: unknown }) => { + materialize: async (params: { + scheduledDate: Date; + generationDate: Date; + transaction: unknown; + }) => { materializerCalls.push(params); return Result.ok({ proformaId: expectedProformaId }); }, @@ -200,9 +233,18 @@ test("first success materializes and persists one generated occurrence", async ( assert.equal(occurrence.proformaId.unwrap().equals(expectedProformaId), true); assert.equal(recurring.status, "active"); assert.equal(recurring.nextOccurrenceDate.unwrap().getTime(), slot2.getTime()); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, 0); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, 0); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, + 1 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, + 0 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, + 0 + ); assert.equal(materializerCalls[0]?.transaction, mainTransaction); assert.equal(occurrenceSaveTransaction, mainTransaction); assert.equal(recurringSaveTransaction, mainTransaction); @@ -238,7 +280,9 @@ test("first failure persists one failed occurrence in a new transaction", async { description: Maybe.some(required(ItemDescription.create("Servicio"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 100, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 100, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }, ], @@ -308,9 +352,18 @@ test("first failure persists one failed occurrence in a new transaction", async assert.equal(recurring.nextOccurrenceDate.unwrap().getTime(), slot1.getTime()); assert.equal(occurrenceSaveTransaction, failureTransaction); assert.deepEqual(lockCalls[1], [companyId, recurringProformaId, failureTransaction]); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, 0); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, 0); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, 1); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, + 0 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, + 0 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, + 1 + ); assert.equal(audits[0]?.transaction, failureTransaction); }); @@ -346,7 +399,9 @@ test("retry success reuses the failed occurrence and current recurring configura { description: Maybe.some(required(ItemDescription.create("Servicio B"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 100, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 100, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }, ], @@ -376,7 +431,8 @@ test("retry success reuses the failed occurrence and current recurring configura let transactionCalls = 0; let occurrenceSaveTransaction: unknown; let recurringSaveTransaction: unknown; - const materializerCalls: Array<{ recurringProforma: RecurringProforma; transaction: unknown }> = []; + const materializerCalls: Array<{ recurringProforma: RecurringProforma; transaction: unknown }> = + []; const audits: Array<{ action: string; transaction: unknown }> = []; const processor = new RecurringProformaOccurrenceProcessor({ @@ -402,7 +458,10 @@ test("retry success reuses the failed occurrence and current recurring configura }, } as never, materializer: { - materialize: async (params: { recurringProforma: RecurringProforma; transaction: unknown }) => { + materialize: async (params: { + recurringProforma: RecurringProforma; + transaction: unknown; + }) => { materializerCalls.push(params); return Result.ok({ proformaId: expectedProformaId }); }, @@ -429,14 +488,26 @@ test("retry success reuses the failed occurrence and current recurring configura assert.equal(occurrence.processingStartedAt.isNone(), true); assert.equal(recurring.status, "active"); assert.equal(recurring.nextOccurrenceDate.unwrap().getTime(), slot2.getTime()); - assert.equal(materializerCalls[0]?.recurringProforma.paymentMethodId.unwrap().equals(currentPaymentMethodId), true); + assert.equal( + materializerCalls[0]?.recurringProforma.paymentMethodId.unwrap().equals(currentPaymentMethodId), + true + ); assert.equal(materializerCalls[0]?.transaction, mainTransaction); assert.equal(occurrenceSaveTransaction, mainTransaction); assert.equal(recurringSaveTransaction, mainTransaction); assert.equal(transactionCalls, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, 0); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, + 1 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, + 1 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, + 0 + ); assert.equal(audits[0]?.transaction, mainTransaction); assert.equal(audits[1]?.transaction, mainTransaction); }); @@ -449,7 +520,14 @@ test("retry failure reuses the failed occurrence in the failure transaction", as const currentPaymentMethodId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-31T00:00:00.000Z"); const currency = required(CurrencyCode.create("EUR")); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); const recurring = required( RecurringProforma.create( { @@ -464,7 +542,9 @@ test("retry failure reuses the failed occurrence in the failure transaction", as { description: Maybe.some(required(ItemDescription.create("Servicio B"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 100, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 100, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }, ], @@ -496,7 +576,8 @@ test("retry failure reuses the failed occurrence in the failure transaction", as let transactionIndex = 0; let occurrenceSaveTransaction: unknown; const lockCalls: unknown[][] = []; - const materializerCalls: Array<{ recurringProforma: RecurringProforma; transaction: unknown }> = []; + const materializerCalls: Array<{ recurringProforma: RecurringProforma; transaction: unknown }> = + []; const audits: Array<{ action: string; transaction: unknown }> = []; const processor = new RecurringProformaOccurrenceProcessor({ @@ -521,7 +602,10 @@ test("retry failure reuses the failed occurrence in the failure transaction", as }, } as never, materializer: { - materialize: async (params: { recurringProforma: RecurringProforma; transaction: unknown }) => { + materialize: async (params: { + recurringProforma: RecurringProforma; + transaction: unknown; + }) => { materializerCalls.push(params); return Result.fail(new Error("new_error")); }, @@ -549,13 +633,25 @@ test("retry failure reuses the failed occurrence in the failure transaction", as assert.equal(recurring.nextOccurrenceDate.unwrap().getTime(), slot1.getTime()); assert.notEqual(mainTransaction, failureTransaction); assert.equal(materializerCalls[0]?.transaction, mainTransaction); - assert.equal(materializerCalls[0]?.recurringProforma.paymentMethodId.unwrap().equals(currentPaymentMethodId), true); + assert.equal( + materializerCalls[0]?.recurringProforma.paymentMethodId.unwrap().equals(currentPaymentMethodId), + true + ); assert.equal(occurrenceSaveTransaction, failureTransaction); assert.deepEqual(lockCalls[0], [companyId, recurringProformaId, mainTransaction]); assert.deepEqual(lockCalls[1], [companyId, recurringProformaId, failureTransaction]); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, 1); - assert.equal(audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, 0); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.retried").length, + 1 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.failed").length, + 1 + ); + assert.equal( + audits.filter((audit) => audit.action === "recurring_proforma_occurrence.generated").length, + 0 + ); assert.equal(audits[0]?.transaction, failureTransaction); assert.equal(audits[1]?.transaction, failureTransaction); }); @@ -564,10 +660,32 @@ test("processing occurrence does not materialize or mutate the cursor", async () const companyId = UniqueID.generateNewID(); const recurringId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: slot1 }); - const occurrence = required(RecurringProformaOccurrence.create({ companyId, recurringProformaId: recurringId, scheduledDate: slot1, status: "processing", attemptCount: 3, processingStartedAt: Maybe.some(slot1), createdAt: slot1, updatedAt: slot1 })); - const fixture = createObservedProcessor({ recurring, occurrence, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const occurrence = required( + RecurringProformaOccurrence.create({ + companyId, + recurringProformaId: recurringId, + scheduledDate: slot1, + status: "processing", + attemptCount: 3, + processingStartedAt: Maybe.some(slot1), + createdAt: slot1, + updatedAt: slot1, + }) + ); + const fixture = createObservedProcessor({ + recurring, + occurrence, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId }); @@ -583,10 +701,33 @@ test("generated occurrence is a safe inconsistency and does not materialize twic const companyId = UniqueID.generateNewID(); const recurringId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: slot1 }); - const occurrence = required(RecurringProformaOccurrence.create({ companyId, recurringProformaId: recurringId, scheduledDate: slot1, status: "generated", attemptCount: 1, proformaId: Maybe.some(UniqueID.generateNewID()), generatedAt: Maybe.some(slot1), createdAt: slot1, updatedAt: slot1 })); - const fixture = createObservedProcessor({ recurring, occurrence, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const occurrence = required( + RecurringProformaOccurrence.create({ + companyId, + recurringProformaId: recurringId, + scheduledDate: slot1, + status: "generated", + attemptCount: 1, + proformaId: Maybe.some(UniqueID.generateNewID()), + generatedAt: Maybe.some(slot1), + createdAt: slot1, + updatedAt: slot1, + }) + ); + const fixture = createObservedProcessor({ + recurring, + occurrence, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId }); @@ -602,9 +743,26 @@ test("non-processable parent states do not create occurrences", async () => { const companyId = UniqueID.generateNewID(); const recurringId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); - const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: slot1, status }); - const fixture = createObservedProcessor({ recurring, occurrence: null, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); + const recurring = createProcessorRecurring({ + companyId, + id: recurringId, + schedule, + slot: slot1, + status, + }); + const fixture = createObservedProcessor({ + recurring, + occurrence: null, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId }); @@ -613,7 +771,10 @@ test("non-processable parent states do not create occurrences", async () => { assert.equal(fixture.materializerCalls.length, 0); assert.equal(fixture.occurrence, null); assert.equal(recurring.status, status); - assert.equal(recurring.nextOccurrenceDate.isNone(), status === "draft" || status === "completed" || status === "archived"); + assert.equal( + recurring.nextOccurrenceDate.isNone(), + status === "draft" || status === "completed" || status === "archived" + ); } }); @@ -622,9 +783,20 @@ test("a success processes only the cursor slot and advances exactly one slot", a const recurringId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-01T00:00:00.000Z"); const slot2 = new Date("2026-02-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: slot1 }); - const fixture = createObservedProcessor({ recurring, occurrence: null, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const fixture = createObservedProcessor({ + recurring, + occurrence: null, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId }); @@ -639,7 +811,14 @@ test("a failure at the cursor blocks every later schedule slot", async () => { const companyId = UniqueID.generateNewID(); const recurringId = UniqueID.generateNewID(); const slot1 = new Date("2026-01-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: slot1, unit: "month", interval: 1, monthlyRule: "same_day" })); + const schedule = required( + RecurrenceSchedule.create({ + startDate: slot1, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: slot1 }); const mainTransaction = { id: "main" }; const failureTransaction = { id: "failure" }; @@ -665,9 +844,26 @@ test("the inclusive end date is materialized before completing the recurring pro const recurringId = UniqueID.generateNewID(); const startDate = new Date("2026-01-01T00:00:00.000Z"); const endDate = new Date("2026-03-01T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate, endDate, unit: "month", interval: 1, monthlyRule: "same_day" })); - const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: endDate }); - const fixture = createObservedProcessor({ recurring, occurrence: null, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const schedule = required( + RecurrenceSchedule.create({ + startDate, + endDate, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); + const recurring = createProcessorRecurring({ + companyId, + id: recurringId, + schedule, + slot: endDate, + }); + const fixture = createObservedProcessor({ + recurring, + occurrence: null, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId }); @@ -684,11 +880,31 @@ test("generation date is passed independently from the schedule cursor", async ( const recurringId = UniqueID.generateNewID(); const scheduledDate = new Date("2026-10-01T00:00:00.000Z"); const generationDate = new Date("2026-09-15T00:00:00.000Z"); - const schedule = required(RecurrenceSchedule.create({ startDate: scheduledDate, unit: "month", interval: 1, monthlyRule: "same_day" })); - const recurring = createProcessorRecurring({ companyId, id: recurringId, schedule, slot: scheduledDate }); - const fixture = createObservedProcessor({ recurring, occurrence: null, materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }) }); + const schedule = required( + RecurrenceSchedule.create({ + startDate: scheduledDate, + unit: "month", + interval: 1, + monthlyRule: "same_day", + }) + ); + const recurring = createProcessorRecurring({ + companyId, + id: recurringId, + schedule, + slot: scheduledDate, + }); + const fixture = createObservedProcessor({ + recurring, + occurrence: null, + materialize: async () => Result.ok({ proformaId: UniqueID.generateNewID() }), + }); - const result = await fixture.processor.process({ companyId, recurringProformaId: recurringId, generationDate }); + const result = await fixture.processor.process({ + companyId, + recurringProformaId: recurringId, + generationDate, + }); assert.equal(result.isSuccess, true); assert.equal(fixture.materializerCalls[0]?.scheduledDate.getTime(), scheduledDate.getTime()); diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.ts index 391de39f..4e5c8785 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-occurrence-processor.ts @@ -1,10 +1,14 @@ import type { ITransactionManager } from "@erp/core/api"; import type { IAudit } from "@repo/rdx-audit"; import type { UniqueID } from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; +import { Result } from "@repo/rdx-utils"; + +import { type RecurringProforma, RecurringProformaOccurrence } from "../../../domain"; +import type { + IRecurringProformaOccurrenceRepository, + IRecurringProformaRepository, +} from "../repositories"; -import { RecurringProformaOccurrence, type RecurringProforma } from "../../../domain"; -import type { IRecurringProformaOccurrenceRepository, IRecurringProformaRepository } from "../repositories"; import type { RecurringProformaMaterializer } from "./recurring-proforma-materializer"; const OCCURRENCE_AUDIT = { @@ -14,60 +18,35 @@ const OCCURRENCE_AUDIT = { } as const; export class RecurringProformaOccurrenceProcessor { - public constructor(private readonly deps: { - transactionManager: ITransactionManager; - recurringProformaRepository: IRecurringProformaRepository; - occurrenceRepository: IRecurringProformaOccurrenceRepository; - materializer: RecurringProformaMaterializer; - audit: IAudit; - }) {} + public constructor( + private readonly deps: { + transactionManager: ITransactionManager; + recurringProformaRepository: IRecurringProformaRepository; + occurrenceRepository: IRecurringProformaOccurrenceRepository; + materializer: RecurringProformaMaterializer; + audit: IAudit; + } + ) {} - public async process(params: { recurringProformaId: UniqueID; companyId: UniqueID; generationDate?: Date }): Promise> { + public async process(params: { + recurringProformaId: UniqueID; + companyId: UniqueID; + generationDate?: Date; + }): Promise< + Result< + { + occurrenceId: UniqueID; + status: string; + proformaId?: UniqueID; + recurringProformaStatus?: string; + nextOccurrenceDate?: Date | null; + }, + Error + > + > { let scheduledDate: Date | null = null; let wasRetry = false; let materializationAttempted = false; - try { - return await this.deps.transactionManager.complete(async (transaction) => { - const recurring = await this.deps.recurringProformaRepository.findByIdInCompanyForUpdate(params.companyId, params.recurringProformaId, transaction); - if (recurring.isFailure) return Result.fail(recurring.error); - if (recurring.data.status !== "active") return Result.fail(new Error("invalid_recurring_proforma_status")); - if (recurring.data.nextOccurrenceDate.isNone()) return Result.fail(new Error("missing_next_occurrence_date")); - scheduledDate = recurring.data.nextOccurrenceDate.unwrap(); - const found = await this.deps.occurrenceRepository.findByRecurringProformaAndScheduledDate(params.recurringProformaId, scheduledDate, params.companyId, transaction); - if (found.isFailure) return Result.fail(found.error); - const now = new Date(); - const occurrence = found.data.isSome() ? found.data.unwrap() : this.createOccurrence(params.companyId, params.recurringProformaId, scheduledDate, now); - if (occurrence.status === "processing") return Result.fail(new Error("already_processing")); - if (occurrence.status === "generated") return Result.fail(new Error("generated_occurrence_cursor_inconsistency")); - wasRetry = occurrence.status === "failed"; - const processing = occurrence.markProcessing(now); - if (processing.isFailure) return Result.fail(processing.error); - materializationAttempted = true; - const materialized = await this.deps.materializer.materialize({ recurringProforma: recurring.data, companyId: params.companyId, scheduledDate, generationDate: params.generationDate ?? scheduledDate, transaction }); - if (materialized.isFailure) throw materialized.error; - const generated = occurrence.markGenerated({ now, generatedAt: now, proformaId: materialized.data.proformaId }); - if (generated.isFailure) return Result.fail(generated.error); - const savedOccurrence = await this.deps.occurrenceRepository.save(occurrence, transaction); - if (savedOccurrence.isFailure) return Result.fail(savedOccurrence.error); - const advanced = this.advanceCursor(recurring.data, scheduledDate); - if (advanced.isFailure) return Result.fail(advanced.error); - const savedRecurring = await this.deps.recurringProformaRepository.save(recurring.data, transaction); - if (savedRecurring.isFailure) return Result.fail(savedRecurring.error); - const audit = await this.deps.audit.recordMutation({ action: OCCURRENCE_AUDIT.GENERATED, target: { type: "recurring_proforma_occurrence", id: occurrence.id.toPrimitive(), companyId: params.companyId.toPrimitive() }, before: {}, after: { recurringProformaId: params.recurringProformaId.toPrimitive(), scheduledDate: scheduledDate.toISOString(), proformaId: materialized.data.proformaId.toPrimitive(), attemptCount: occurrence.attemptCount } }, { transaction }); - if (audit.isFailure) return Result.fail(audit.error); - if (wasRetry) { - const retried = await this.deps.audit.recordMutation({ action: OCCURRENCE_AUDIT.RETRIED, target: { type: "recurring_proforma_occurrence", id: occurrence.id.toPrimitive(), companyId: params.companyId.toPrimitive() }, before: {}, after: { recurringProformaId: params.recurringProformaId.toPrimitive(), scheduledDate: scheduledDate.toISOString(), attemptCount: occurrence.attemptCount } }, { transaction }); - if (retried.isFailure) return Result.fail(retried.error); - } - return Result.ok({ occurrenceId: occurrence.id, status: occurrence.status, proformaId: materialized.data.proformaId, recurringProformaStatus: recurring.data.status, nextOccurrenceDate: recurring.data.nextOccurrenceDate.getOrUndefined() ?? null }); - }); - } catch (error: unknown) { - if (scheduledDate === null || !materializationAttempted) return Result.fail(error instanceof Error ? error : new Error("proforma_materialization_failed")); - return this.persistFailure({ ...params, scheduledDate, wasRetry, error: error instanceof Error ? error : new Error("proforma_materialization_failed") }); - } - } - - private async persistFailure(params: { recurringProformaId: UniqueID; companyId: UniqueID; scheduledDate: Date; generationDate?: Date; wasRetry: boolean; error: Error }): Promise> { try { return await this.deps.transactionManager.complete(async (transaction) => { const recurring = await this.deps.recurringProformaRepository.findByIdInCompanyForUpdate( @@ -75,35 +54,274 @@ export class RecurringProformaOccurrenceProcessor { params.recurringProformaId, transaction ); - if (recurring.isFailure) return Result.fail(recurring.error); - const found = await this.deps.occurrenceRepository.findByRecurringProformaAndScheduledDate(params.recurringProformaId, params.scheduledDate, params.companyId, transaction); - if (found.isFailure) return Result.fail(found.error); + if (recurring.isFailure) { + return Result.fail(recurring.error); + } + if (recurring.data.status !== "active") { + return Result.fail(new Error("invalid_recurring_proforma_status")); + } + if (recurring.data.nextOccurrenceDate.isNone()) { + return Result.fail(new Error("missing_next_occurrence_date")); + } + scheduledDate = recurring.data.nextOccurrenceDate.unwrap(); + const found = await this.deps.occurrenceRepository.findByRecurringProformaAndScheduledDate( + params.recurringProformaId, + scheduledDate, + params.companyId, + transaction + ); + if (found.isFailure) { + return Result.fail(found.error); + } const now = new Date(); - const occurrence = found.data.isSome() ? found.data.unwrap() : this.createOccurrence(params.companyId, params.recurringProformaId, params.scheduledDate, now); + const occurrence = found.data.isSome() + ? found.data.unwrap() + : this.createOccurrence(params.companyId, params.recurringProformaId, scheduledDate, now); + if (occurrence.status === "processing") { + return Result.fail(new Error("already_processing")); + } + if (occurrence.status === "generated") { + return Result.fail(new Error("generated_occurrence_cursor_inconsistency")); + } + wasRetry = occurrence.status === "failed"; const processing = occurrence.markProcessing(now); - if (processing.isFailure) return Result.fail(processing.error); - const failed = occurrence.markFailed({ now, errorCode: params.error.message, errorMessage: params.error.message }); - if (failed.isFailure) return Result.fail(failed.error); + if (processing.isFailure) { + return Result.fail(processing.error); + } + materializationAttempted = true; + const materialized = await this.deps.materializer.materialize({ + recurringProforma: recurring.data, + companyId: params.companyId, + scheduledDate, + generationDate: params.generationDate ?? scheduledDate, + transaction, + }); + if (materialized.isFailure) { + throw materialized.error; + } + const generated = occurrence.markGenerated({ + now, + generatedAt: now, + proformaId: materialized.data.proformaId, + }); + if (generated.isFailure) { + return Result.fail(generated.error); + } + const savedOccurrence = await this.deps.occurrenceRepository.save(occurrence, transaction); + if (savedOccurrence.isFailure) { + return Result.fail(savedOccurrence.error); + } + const advanced = this.advanceCursor(recurring.data, scheduledDate); + if (advanced.isFailure) { + return Result.fail(advanced.error); + } + const savedRecurring = await this.deps.recurringProformaRepository.save( + recurring.data, + transaction + ); + if (savedRecurring.isFailure) { + return Result.fail(savedRecurring.error); + } + const audit = await this.deps.audit.recordMutation( + { + action: OCCURRENCE_AUDIT.GENERATED, + target: { + type: "recurring_proforma_occurrence", + id: occurrence.id.toPrimitive(), + companyId: params.companyId.toPrimitive(), + }, + before: {}, + after: { + recurringProformaId: params.recurringProformaId.toPrimitive(), + scheduledDate: scheduledDate.toISOString(), + proformaId: materialized.data.proformaId.toPrimitive(), + attemptCount: occurrence.attemptCount, + }, + }, + { transaction } + ); + if (audit.isFailure) { + return Result.fail(audit.error); + } + if (wasRetry) { + const retried = await this.deps.audit.recordMutation( + { + action: OCCURRENCE_AUDIT.RETRIED, + target: { + type: "recurring_proforma_occurrence", + id: occurrence.id.toPrimitive(), + companyId: params.companyId.toPrimitive(), + }, + before: {}, + after: { + recurringProformaId: params.recurringProformaId.toPrimitive(), + scheduledDate: scheduledDate.toISOString(), + attemptCount: occurrence.attemptCount, + }, + }, + { transaction } + ); + if (retried.isFailure) { + return Result.fail(retried.error); + } + } + return Result.ok({ + occurrenceId: occurrence.id, + status: occurrence.status, + proformaId: materialized.data.proformaId, + recurringProformaStatus: recurring.data.status, + nextOccurrenceDate: recurring.data.nextOccurrenceDate.getOrUndefined() ?? null, + }); + }); + } catch (error: unknown) { + if (scheduledDate === null || !materializationAttempted) { + return Result.fail( + error instanceof Error ? error : new Error("proforma_materialization_failed") + ); + } + return this.persistFailure({ + ...params, + scheduledDate, + wasRetry, + error: error instanceof Error ? error : new Error("proforma_materialization_failed"), + }); + } + } + + private async persistFailure(params: { + recurringProformaId: UniqueID; + companyId: UniqueID; + scheduledDate: Date; + generationDate?: Date; + wasRetry: boolean; + error: Error; + }): Promise> { + try { + return await this.deps.transactionManager.complete(async (transaction) => { + const recurring = await this.deps.recurringProformaRepository.findByIdInCompanyForUpdate( + params.companyId, + params.recurringProformaId, + transaction + ); + if (recurring.isFailure) { + return Result.fail(recurring.error); + } + const found = await this.deps.occurrenceRepository.findByRecurringProformaAndScheduledDate( + params.recurringProformaId, + params.scheduledDate, + params.companyId, + transaction + ); + if (found.isFailure) { + return Result.fail(found.error); + } + const now = new Date(); + const occurrence = found.data.isSome() + ? found.data.unwrap() + : this.createOccurrence( + params.companyId, + params.recurringProformaId, + params.scheduledDate, + now + ); + const processing = occurrence.markProcessing(now); + if (processing.isFailure) { + return Result.fail(processing.error); + } + const failed = occurrence.markFailed({ + now, + errorCode: params.error.message, + errorMessage: params.error.message, + }); + if (failed.isFailure) { + return Result.fail(failed.error); + } const saved = await this.deps.occurrenceRepository.save(occurrence, transaction); - if (saved.isFailure) return Result.fail(saved.error); - const audit = await this.deps.audit.recordMutation({ action: OCCURRENCE_AUDIT.FAILED, target: { type: "recurring_proforma_occurrence", id: occurrence.id.toPrimitive(), companyId: params.companyId.toPrimitive() }, before: {}, after: { recurringProformaId: params.recurringProformaId.toPrimitive(), scheduledDate: params.scheduledDate.toISOString(), attemptCount: occurrence.attemptCount, lastErrorCode: occurrence.lastErrorCode.unwrap() } }, { transaction }); - if (audit.isFailure) return Result.fail(audit.error); + if (saved.isFailure) { + return Result.fail(saved.error); + } + const audit = await this.deps.audit.recordMutation( + { + action: OCCURRENCE_AUDIT.FAILED, + target: { + type: "recurring_proforma_occurrence", + id: occurrence.id.toPrimitive(), + companyId: params.companyId.toPrimitive(), + }, + before: {}, + after: { + recurringProformaId: params.recurringProformaId.toPrimitive(), + scheduledDate: params.scheduledDate.toISOString(), + attemptCount: occurrence.attemptCount, + lastErrorCode: occurrence.lastErrorCode.unwrap(), + }, + }, + { transaction } + ); + if (audit.isFailure) { + return Result.fail(audit.error); + } if (params.wasRetry) { - const retried = await this.deps.audit.recordMutation({ action: OCCURRENCE_AUDIT.RETRIED, target: { type: "recurring_proforma_occurrence", id: occurrence.id.toPrimitive(), companyId: params.companyId.toPrimitive() }, before: {}, after: { recurringProformaId: params.recurringProformaId.toPrimitive(), scheduledDate: params.scheduledDate.toISOString(), attemptCount: occurrence.attemptCount } }, { transaction }); - if (retried.isFailure) return Result.fail(retried.error); + const retried = await this.deps.audit.recordMutation( + { + action: OCCURRENCE_AUDIT.RETRIED, + target: { + type: "recurring_proforma_occurrence", + id: occurrence.id.toPrimitive(), + companyId: params.companyId.toPrimitive(), + }, + before: {}, + after: { + recurringProformaId: params.recurringProformaId.toPrimitive(), + scheduledDate: params.scheduledDate.toISOString(), + attemptCount: occurrence.attemptCount, + }, + }, + { transaction } + ); + if (retried.isFailure) { + return Result.fail(retried.error); + } } return Result.ok({ occurrenceId: occurrence.id, status: occurrence.status }); }); - } catch (error: unknown) { return Result.fail(error instanceof Error ? error : new Error("failure_persistence_failed")); } + } catch (error: unknown) { + return Result.fail(error instanceof Error ? error : new Error("failure_persistence_failed")); + } } - private createOccurrence(companyId: UniqueID, recurringProformaId: UniqueID, scheduledDate: Date, now: Date): RecurringProformaOccurrence { - return RecurringProformaOccurrence.create({ companyId, recurringProformaId, scheduledDate, createdAt: now, updatedAt: now }).data; + private createOccurrence( + companyId: UniqueID, + recurringProformaId: UniqueID, + scheduledDate: Date, + now: Date + ): RecurringProformaOccurrence { + return RecurringProformaOccurrence.create({ + companyId, + recurringProformaId, + scheduledDate, + createdAt: now, + updatedAt: now, + }).data; } private advanceCursor(recurring: RecurringProforma, current: Date): Result { - if (recurring.schedule.isNone()) return Result.fail(new Error("missing_next_occurrence_date")); + if (recurring.schedule.isNone()) { + return Result.fail(new Error("missing_next_occurrence_date")); + } let index = 0; - while (true) { const candidate = recurring.schedule.unwrap().occurrenceAt(index); if (candidate.isFailure) return Result.fail(candidate.error); if (candidate.data === null || candidate.data.getTime() > current.getTime()) { if (candidate.data === null) return recurring.complete().map(() => undefined); return recurring.advanceNextOccurrenceDate(candidate.data).map(() => undefined); } index += 1; } + while (true) { + const candidate = recurring.schedule.unwrap().occurrenceAt(index); + if (candidate.isFailure) { + return Result.fail(candidate.error); + } + if (candidate.data === null || candidate.data.getTime() > current.getTime()) { + if (candidate.data === null) { + return recurring.complete().map(() => undefined); + } + return recurring.advanceNextOccurrenceDate(candidate.data).map(() => undefined); + } + index += 1; + } } } diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.test.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.test.ts index fa215585..03a07a7b 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.test.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.test.ts @@ -5,8 +5,14 @@ import { DiscountPercentage } from "@erp/core/api"; import { CurrencyCode, LanguageCode, UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; -import { ItemAmount, ItemDescription, ItemQuantity, RecurrenceSchedule, RecurringProforma } from "../../../domain"; import type { RecurringProformaItemCreateProps } from "../../../domain"; +import { + ItemAmount, + ItemDescription, + ItemQuantity, + RecurrenceSchedule, + RecurringProforma, +} from "../../../domain"; import { RecurringProformaActivationReadinessChecker } from "./recurring-proforma-activation-readiness-checker"; import { RecurringProformaUpdater } from "./recurring-proforma-updater"; @@ -19,7 +25,9 @@ const item = (): RecurringProformaItemCreateProps => { return { description: Maybe.some(required(ItemDescription.create("Servicio"))), quantity: Maybe.some(required(ItemQuantity.create({ value: 1 }))), - unitAmount: Maybe.some(required(ItemAmount.create({ value: 10, currency_code: currency.code }))), + unitAmount: Maybe.some( + required(ItemAmount.create({ value: 10, currency_code: currency.code })) + ), itemDiscountPercentage: Maybe.none(), }; }; @@ -59,8 +67,12 @@ test("RecurringProformaUpdater distingue omitido, null y valor", async () => { ensurePaymentTermById: async () => Result.ok(undefined), ensurePaymentMethodById: async () => Result.ok(undefined), } as never, - proformaSeriesValidator: { ensureValidProformaSeries: async () => Result.ok(undefined) } as never, - targetInvoiceSeriesValidator: { ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined) } as never, + proformaSeriesValidator: { + ensureValidProformaSeries: async () => Result.ok(undefined), + } as never, + targetInvoiceSeriesValidator: { + ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined), + } as never, audit: { recordMutation: async () => Result.ok(undefined) } as never, }); @@ -85,7 +97,9 @@ test("RecurringProformaUpdater distingue omitido, null y valor", async () => { test("RecurringProformaUpdater bloquea endDate anterior al historial", async () => { const companyId = id(); - const recurring = required(RecurringProforma.create({ companyId, name: "Inicial", schedule: Maybe.some(schedule()) })); + const recurring = required( + RecurringProforma.create({ companyId, name: "Inicial", schedule: Maybe.some(schedule()) }) + ); const updater = new RecurringProformaUpdater({ repository: { findByIdInCompany: async () => Result.ok(recurring), @@ -100,15 +114,24 @@ test("RecurringProformaUpdater bloquea endDate anterior al historial", async () hasFailedOccurrences: async () => Result.ok(false), }, customerServices: { getCustomerById: async () => Result.ok({}) } as never, - paymentResolver: { ensurePaymentTermById: async () => Result.ok(undefined), ensurePaymentMethodById: async () => Result.ok(undefined) } as never, - proformaSeriesValidator: { ensureValidProformaSeries: async () => Result.ok(undefined) } as never, - targetInvoiceSeriesValidator: { ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined) } as never, + paymentResolver: { + ensurePaymentTermById: async () => Result.ok(undefined), + ensurePaymentMethodById: async () => Result.ok(undefined), + } as never, + proformaSeriesValidator: { + ensureValidProformaSeries: async () => Result.ok(undefined), + } as never, + targetInvoiceSeriesValidator: { + ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined), + } as never, audit: { recordMutation: async () => Result.ok(undefined) } as never, }); const result = await updater.update({ companyId, id: recurring.id, - patch: { schedule: { ...schedule().getProps(), endDate: new Date("2026-01-31T00:00:00.000Z") } }, + patch: { + schedule: { ...schedule().getProps(), endDate: new Date("2026-01-31T00:00:00.000Z") }, + }, }); assert.equal(result.isFailure, true); }); @@ -118,18 +141,28 @@ test("RecurringProformaActivationReadinessChecker acumula bloqueos sin persistir const recurring = required(RecurringProforma.create({ companyId, name: "Borrador" })); const checker = new RecurringProformaActivationReadinessChecker({ customerServices: { getCustomerById: async () => Result.fail(new Error("not used")) } as never, - paymentResolver: { ensurePaymentTermById: async () => Result.ok(undefined), ensurePaymentMethodById: async () => Result.ok(undefined) } as never, - proformaSeriesValidator: { ensureValidProformaSeries: async () => Result.ok(undefined) } as never, - targetInvoiceSeriesValidator: { ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined) } as never, + paymentResolver: { + ensurePaymentTermById: async () => Result.ok(undefined), + ensurePaymentMethodById: async () => Result.ok(undefined), + } as never, + proformaSeriesValidator: { + ensureValidProformaSeries: async () => Result.ok(undefined), + } as never, + targetInvoiceSeriesValidator: { + ensureValidIssuedInvoiceSeries: async () => Result.ok(undefined), + } as never, }); const readiness = await checker.check({ recurringProforma: recurring, companyId }); assert.equal(readiness.isSuccess, true); - assert.deepEqual(readiness.data.blockers.map((blocker) => blocker.code), ["missing_customer", "missing_items", "missing_schedule"]); + assert.deepEqual( + readiness.data.blockers.map((blocker) => blocker.code), + ["missing_customer", "missing_items", "missing_schedule"] + ); }); test("RecurringProformaUpdater reemplaza items y conserva el calendario sin historial", async () => { const companyId = id(); - const recurring = required(RecurringProforma.create({ companyId, name: "Inicial" })); + const _recurring = required(RecurringProforma.create({ companyId, name: "Inicial" })); const discount = required(DiscountPercentage.create({ value: 500 })); const language = required(LanguageCode.create("es")); const currency = required(CurrencyCode.create("EUR")); diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.ts index f8875081..81a925da 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-services.ts @@ -1,6 +1,6 @@ import type { IAudit } from "@repo/rdx-audit"; import type { UniqueID } from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; +import { type Maybe, Result } from "@repo/rdx-utils"; import type { RecurringProformaItemCreateProps } from "../../../domain"; import { RecurringProforma } from "../../../domain"; @@ -13,18 +13,37 @@ import type { IRecurringProformaOccurrenceRepository, IRecurringProformaRepository, } from "../repositories"; + import type { RecurringProformaActivationReadinessChecker } from "./recurring-proforma-activation-readiness-checker"; type MutationDeps = { repository: IRecurringProformaRepository; audit: IAudit }; export class RecurringProformaCreator { public constructor(private readonly deps: MutationDeps) {} - public async create(params: { companyId: UniqueID; id: UniqueID; name: string; description?: Maybe; transaction?: unknown }): Promise> { - const created = RecurringProforma.create({ companyId: params.companyId, name: params.name, description: params.description }, params.id); - if (created.isFailure) return Result.fail(created.error); + public async create(params: { + companyId: UniqueID; + id: UniqueID; + name: string; + description?: Maybe; + transaction?: unknown; + }): Promise> { + const created = RecurringProforma.create( + { companyId: params.companyId, name: params.name, description: params.description }, + params.id + ); + if (created.isFailure) { + return Result.fail(created.error); + } const saved = await this.deps.repository.save(created.data, params.transaction); - if (saved.isFailure) return Result.fail(saved.error); - const audit = await this.deps.audit.recordMutation({ action: RECURRING_PROFORMA_AUDIT_ACTIONS.CREATED, target: toRecurringProformaAuditTarget(created.data), before: {}, after: toRecurringProformaAuditState(created.data) }); + if (saved.isFailure) { + return Result.fail(saved.error); + } + const audit = await this.deps.audit.recordMutation({ + action: RECURRING_PROFORMA_AUDIT_ACTIONS.CREATED, + target: toRecurringProformaAuditTarget(created.data), + before: {}, + after: toRecurringProformaAuditState(created.data), + }); return audit.isFailure ? Result.fail(audit.error) : Result.ok(created.data); } } @@ -36,16 +55,29 @@ export class RecurringProformaStatusChanger { readinessChecker: RecurringProformaActivationReadinessChecker; } ) {} - public async change(params: { companyId: UniqueID; id: UniqueID; action: "activate" | "pause" | "resume" | "complete" | "archive"; transaction?: unknown }): Promise> { - const found = await this.deps.repository.findByIdInCompany(params.companyId, params.id, params.transaction); - if (found.isFailure) return Result.fail(found.error); + public async change(params: { + companyId: UniqueID; + id: UniqueID; + action: "activate" | "pause" | "resume" | "complete" | "archive"; + transaction?: unknown; + }): Promise> { + const found = await this.deps.repository.findByIdInCompany( + params.companyId, + params.id, + params.transaction + ); + if (found.isFailure) { + return Result.fail(found.error); + } if (params.action === "activate") { const readiness = await this.deps.readinessChecker.check({ recurringProforma: found.data, companyId: params.companyId, transaction: params.transaction, }); - if (readiness.isFailure) return Result.fail(readiness.error); + if (readiness.isFailure) { + return Result.fail(readiness.error); + } if (!readiness.data.canActivate) { return Result.fail( new Error( @@ -62,46 +94,146 @@ export class RecurringProformaStatusChanger { params.companyId, params.transaction ); - if (failed.isFailure) return Result.fail(failed.error); - if (failed.data) return Result.fail(new Error("Recurring proforma has unresolved failed occurrences")); + if (failed.isFailure) { + return Result.fail(failed.error); + } + if (failed.data) { + return Result.fail(new Error("Recurring proforma has unresolved failed occurrences")); + } } const before = toRecurringProformaAuditState(found.data); - const result = params.action === "activate" || params.action === "resume" ? found.data.activate() : params.action === "pause" ? found.data.pause() : params.action === "complete" ? found.data.complete() : found.data.archive(); - if (result.isFailure || !result.data) return result.isFailure ? Result.fail(result.error) : Result.fail(new Error("Recurring proforma transition is a no-op")); + const result = + params.action === "activate" || params.action === "resume" + ? found.data.activate() + : params.action === "pause" + ? found.data.pause() + : params.action === "complete" + ? found.data.complete() + : found.data.archive(); + if (result.isFailure || !result.data) { + return result.isFailure + ? Result.fail(result.error) + : Result.fail(new Error("Recurring proforma transition is a no-op")); + } const saved = await this.deps.repository.save(found.data, params.transaction); - if (saved.isFailure) return Result.fail(saved.error); - const action = params.action === "activate" ? RECURRING_PROFORMA_AUDIT_ACTIONS.ACTIVATED : params.action === "pause" ? RECURRING_PROFORMA_AUDIT_ACTIONS.PAUSED : params.action === "resume" ? RECURRING_PROFORMA_AUDIT_ACTIONS.RESUMED : params.action === "complete" ? RECURRING_PROFORMA_AUDIT_ACTIONS.COMPLETED : RECURRING_PROFORMA_AUDIT_ACTIONS.ARCHIVED; - const audit = await this.deps.audit.recordMutation({ action, target: toRecurringProformaAuditTarget(found.data), before, after: toRecurringProformaAuditState(found.data) }); + if (saved.isFailure) { + return Result.fail(saved.error); + } + const action = + params.action === "activate" + ? RECURRING_PROFORMA_AUDIT_ACTIONS.ACTIVATED + : params.action === "pause" + ? RECURRING_PROFORMA_AUDIT_ACTIONS.PAUSED + : params.action === "resume" + ? RECURRING_PROFORMA_AUDIT_ACTIONS.RESUMED + : params.action === "complete" + ? RECURRING_PROFORMA_AUDIT_ACTIONS.COMPLETED + : RECURRING_PROFORMA_AUDIT_ACTIONS.ARCHIVED; + const audit = await this.deps.audit.recordMutation({ + action, + target: toRecurringProformaAuditTarget(found.data), + before, + after: toRecurringProformaAuditState(found.data), + }); return audit.isFailure ? Result.fail(audit.error) : Result.ok(found.data); } } export class RecurringProformaDeleter { public constructor(private readonly deps: MutationDeps) {} - public async delete(params: { companyId: UniqueID; id: UniqueID; transaction?: unknown }): Promise> { - const found = await this.deps.repository.findByIdInCompany(params.companyId, params.id, params.transaction); - if (found.isFailure) return Result.fail(found.error); - if (found.data.status !== "draft") return Result.fail(new Error("Only draft recurring proformas can be deleted")); - const history = await this.deps.repository.hasOccurrences(params.id, params.companyId, params.transaction); - if (history.isFailure || history.data) return history.isFailure ? Result.fail(history.error) : Result.fail(new Error("Recurring proformas with occurrences cannot be deleted")); - const deleted = await this.deps.repository.deleteByIdInCompany(params.companyId, params.id, params.transaction); - if (deleted.isFailure) return Result.fail(deleted.error); - const audit = await this.deps.audit.recordMutation({ action: RECURRING_PROFORMA_AUDIT_ACTIONS.DELETED, target: toRecurringProformaAuditTarget(found.data), before: toRecurringProformaAuditState(found.data), after: {} }); + public async delete(params: { + companyId: UniqueID; + id: UniqueID; + transaction?: unknown; + }): Promise> { + const found = await this.deps.repository.findByIdInCompany( + params.companyId, + params.id, + params.transaction + ); + if (found.isFailure) { + return Result.fail(found.error); + } + if (found.data.status !== "draft") { + return Result.fail(new Error("Only draft recurring proformas can be deleted")); + } + const history = await this.deps.repository.hasOccurrences( + params.id, + params.companyId, + params.transaction + ); + if (history.isFailure || history.data) { + return history.isFailure + ? Result.fail(history.error) + : Result.fail(new Error("Recurring proformas with occurrences cannot be deleted")); + } + const deleted = await this.deps.repository.deleteByIdInCompany( + params.companyId, + params.id, + params.transaction + ); + if (deleted.isFailure) { + return Result.fail(deleted.error); + } + const audit = await this.deps.audit.recordMutation({ + action: RECURRING_PROFORMA_AUDIT_ACTIONS.DELETED, + target: toRecurringProformaAuditTarget(found.data), + before: toRecurringProformaAuditState(found.data), + after: {}, + }); return audit.isFailure ? Result.fail(audit.error) : deleted; } } export class RecurringProformaDuplicator { public constructor(private readonly deps: MutationDeps) {} - public async duplicate(params: { companyId: UniqueID; sourceId: UniqueID; id: UniqueID; transaction?: unknown }): Promise> { - const source = await this.deps.repository.findByIdInCompany(params.companyId, params.sourceId, params.transaction); - if (source.isFailure) return Result.fail(source.error); - const items: RecurringProformaItemCreateProps[] = source.data.items.getAll().map((item) => item.getProps()); - const copy = RecurringProforma.create({ companyId: params.companyId, name: source.data.name, description: source.data.description, customerId: source.data.customerId, schedule: source.data.schedule, paymentTermId: source.data.paymentTermId, paymentMethodId: source.data.paymentMethodId, language: source.data.language, currency: source.data.currency, proformaSeries: source.data.proformaSeries, issuedInvoiceSeries: source.data.issuedInvoiceSeries, items }, params.id); - if (copy.isFailure) return Result.fail(copy.error); + public async duplicate(params: { + companyId: UniqueID; + sourceId: UniqueID; + id: UniqueID; + transaction?: unknown; + }): Promise> { + const source = await this.deps.repository.findByIdInCompany( + params.companyId, + params.sourceId, + params.transaction + ); + if (source.isFailure) { + return Result.fail(source.error); + } + const items: RecurringProformaItemCreateProps[] = source.data.items + .getAll() + .map((item) => item.getProps()); + const copy = RecurringProforma.create( + { + companyId: params.companyId, + name: source.data.name, + description: source.data.description, + customerId: source.data.customerId, + schedule: source.data.schedule, + paymentTermId: source.data.paymentTermId, + paymentMethodId: source.data.paymentMethodId, + language: source.data.language, + currency: source.data.currency, + proformaSeries: source.data.proformaSeries, + issuedInvoiceSeries: source.data.issuedInvoiceSeries, + items, + }, + params.id + ); + if (copy.isFailure) { + return Result.fail(copy.error); + } const saved = await this.deps.repository.save(copy.data, params.transaction); - if (saved.isFailure) return Result.fail(saved.error); - const audit = await this.deps.audit.recordMutation({ action: RECURRING_PROFORMA_AUDIT_ACTIONS.DUPLICATED, target: toRecurringProformaAuditTarget(copy.data), before: {}, after: toRecurringProformaAuditState(copy.data) }); + if (saved.isFailure) { + return Result.fail(saved.error); + } + const audit = await this.deps.audit.recordMutation({ + action: RECURRING_PROFORMA_AUDIT_ACTIONS.DUPLICATED, + target: toRecurringProformaAuditTarget(copy.data), + before: {}, + after: toRecurringProformaAuditState(copy.data), + }); return audit.isFailure ? Result.fail(audit.error) : Result.ok(copy.data); } } diff --git a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-updater.ts b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-updater.ts index 1e05eba4..50fe0021 100644 --- a/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-updater.ts +++ b/modules/customer-invoices/src/api/application/recurring-proformas/services/recurring-proforma-updater.ts @@ -1,16 +1,19 @@ +import type { DiscountPercentage } from "@erp/core/api"; import type { ICustomerPublicServices } from "@erp/customers/api"; import type { IAudit } from "@repo/rdx-audit"; import type { CurrencyCode, LanguageCode, UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; -import type { DiscountPercentage } from "@erp/core/api"; import { - type RecurrenceScheduleProps, + type InvoiceSerie, RecurrenceSchedule, + type RecurrenceScheduleProps, type RecurringProforma, type RecurringProformaItemCreateProps, - type InvoiceSerie, } from "../../../domain"; +import type { ProformaPaymentResolver } from "../../proformas/services/catalog-resolver/proforma-payment-resolver"; +import type { ProformaSeriesValidator } from "../../proformas/services/proforma-series-validator.interface"; +import type { ProformaTargetInvoiceSeriesValidator } from "../../proformas/services/proforma-target-invoice-series-validator.interface"; import { RECURRING_PROFORMA_AUDIT_ACTIONS, toRecurringProformaAuditState, @@ -20,9 +23,6 @@ import type { IRecurringProformaOccurrenceRepository, IRecurringProformaRepository, } from "../repositories"; -import type { ProformaPaymentResolver } from "../../proformas/services/catalog-resolver/proforma-payment-resolver"; -import type { ProformaSeriesValidator } from "../../proformas/services/proforma-series-validator.interface"; -import type { ProformaTargetInvoiceSeriesValidator } from "../../proformas/services/proforma-target-invoice-series-validator.interface"; export interface RecurringProformaPatchProps { name?: string; @@ -63,25 +63,33 @@ export class RecurringProformaUpdater { params.id, params.transaction ); - if (found.isFailure) return Result.fail(found.error); + if (found.isFailure) { + return Result.fail(found.error); + } const recurringProforma = found.data; const before = toRecurringProformaAuditState(recurringProforma); const validation = await this.validateReferences(params); - if (validation.isFailure) return Result.fail(validation.error); + if (validation.isFailure) { + return Result.fail(validation.error); + } let schedule: RecurrenceSchedule | undefined; let hasOccurrences = false; if (params.patch.schedule !== undefined) { const scheduleResult = RecurrenceSchedule.create(params.patch.schedule); - if (scheduleResult.isFailure) return Result.fail(scheduleResult.error); + if (scheduleResult.isFailure) { + return Result.fail(scheduleResult.error); + } schedule = scheduleResult.data; const history = await this.deps.repository.hasOccurrences( params.id, params.companyId, params.transaction ); - if (history.isFailure) return Result.fail(history.error); + if (history.isFailure) { + return Result.fail(history.error); + } hasOccurrences = history.data; if (hasOccurrences) { const endDateRule = await this.ensureEndDateDoesNotPrecedeHistory({ @@ -90,16 +98,24 @@ export class RecurringProformaUpdater { endDate: schedule.endDate, transaction: params.transaction, }); - if (endDateRule.isFailure) return Result.fail(endDateRule.error); + if (endDateRule.isFailure) { + return Result.fail(endDateRule.error); + } } } const mutation = this.applyPatch(recurringProforma, params.patch, schedule, hasOccurrences); - if (mutation.isFailure) return Result.fail(mutation.error); - if (!mutation.data) return Result.ok(recurringProforma); + if (mutation.isFailure) { + return Result.fail(mutation.error); + } + if (!mutation.data) { + return Result.ok(recurringProforma); + } const saved = await this.deps.repository.save(recurringProforma, params.transaction); - if (saved.isFailure) return Result.fail(saved.error); + if (saved.isFailure) { + return Result.fail(saved.error); + } const audited = await this.deps.audit.recordMutation({ action: RECURRING_PROFORMA_AUDIT_ACTIONS.UPDATED, target: toRecurringProformaAuditTarget(recurringProforma), @@ -119,7 +135,9 @@ export class RecurringProformaUpdater { companyId: params.companyId, transaction: params.transaction, }); - if (customer.isFailure) return Result.fail(customer.error); + if (customer.isFailure) { + return Result.fail(customer.error); + } } if (params.patch.paymentTermId !== undefined && params.patch.paymentTermId !== null) { const paymentTerm = await this.deps.paymentResolver.ensurePaymentTermById({ @@ -127,7 +145,9 @@ export class RecurringProformaUpdater { id: Maybe.some(params.patch.paymentTermId), transaction: params.transaction, }); - if (paymentTerm.isFailure) return Result.fail(paymentTerm.error); + if (paymentTerm.isFailure) { + return Result.fail(paymentTerm.error); + } } if (params.patch.paymentMethodId !== undefined && params.patch.paymentMethodId !== null) { const paymentMethod = await this.deps.paymentResolver.ensurePaymentMethodById({ @@ -135,7 +155,9 @@ export class RecurringProformaUpdater { id: Maybe.some(params.patch.paymentMethodId), transaction: params.transaction, }); - if (paymentMethod.isFailure) return Result.fail(paymentMethod.error); + if (paymentMethod.isFailure) { + return Result.fail(paymentMethod.error); + } } if (params.patch.proformaSeries !== undefined && params.patch.proformaSeries !== null) { const proformaSeries = await this.deps.proformaSeriesValidator.ensureValidProformaSeries({ @@ -143,15 +165,23 @@ export class RecurringProformaUpdater { proformaSeriesCode: Maybe.some(params.patch.proformaSeries), transaction: params.transaction, }); - if (proformaSeries.isFailure) return Result.fail(proformaSeries.error); + if (proformaSeries.isFailure) { + return Result.fail(proformaSeries.error); + } } - if (params.patch.issuedInvoiceSeries !== undefined && params.patch.issuedInvoiceSeries !== null) { - const issuedInvoiceSeries = await this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ - companyId: params.companyId, - targetInvoiceSeriesCode: Maybe.some(params.patch.issuedInvoiceSeries), - transaction: params.transaction, - }); - if (issuedInvoiceSeries.isFailure) return Result.fail(issuedInvoiceSeries.error); + if ( + params.patch.issuedInvoiceSeries !== undefined && + params.patch.issuedInvoiceSeries !== null + ) { + const issuedInvoiceSeries = + await this.deps.targetInvoiceSeriesValidator.ensureValidIssuedInvoiceSeries({ + companyId: params.companyId, + targetInvoiceSeriesCode: Maybe.some(params.patch.issuedInvoiceSeries), + transaction: params.transaction, + }); + if (issuedInvoiceSeries.isFailure) { + return Result.fail(issuedInvoiceSeries.error); + } } return Result.ok(undefined); } @@ -162,13 +192,17 @@ export class RecurringProformaUpdater { endDate: Date | null; transaction?: unknown; }): Promise> { - if (params.endDate === null) return Result.ok(undefined); + if (params.endDate === null) { + return Result.ok(undefined); + } const latest = await this.deps.occurrenceRepository.latestScheduledDate( params.id, params.companyId, params.transaction ); - if (latest.isFailure) return Result.fail(latest.error); + if (latest.isFailure) { + return Result.fail(latest.error); + } if (latest.data.isNone() || params.endDate.getTime() >= latest.data.unwrap().getTime()) { return Result.ok(undefined); } @@ -182,21 +216,85 @@ export class RecurringProformaUpdater { hasOccurrences: boolean ): Result { const operations: Array> = []; - if (patch.name !== undefined) operations.push(recurringProforma.rename(patch.name)); - if (patch.description !== undefined) operations.push(recurringProforma.setDescription(patch.description === null ? Maybe.none() : Maybe.some(patch.description))); - if (patch.globalDiscountPercentage !== undefined) operations.push(recurringProforma.setGlobalDiscountPercentage(patch.globalDiscountPercentage === null ? Maybe.none() : Maybe.some(patch.globalDiscountPercentage))); - if (patch.customerId !== undefined) operations.push(recurringProforma.setCustomerId(patch.customerId === null ? Maybe.none() : Maybe.some(patch.customerId))); - if (patch.paymentTermId !== undefined) operations.push(recurringProforma.setPaymentTermId(patch.paymentTermId === null ? Maybe.none() : Maybe.some(patch.paymentTermId))); - if (patch.paymentMethodId !== undefined) operations.push(recurringProforma.setPaymentMethodId(patch.paymentMethodId === null ? Maybe.none() : Maybe.some(patch.paymentMethodId))); - if (patch.language !== undefined) operations.push(recurringProforma.setLanguage(patch.language === null ? Maybe.none() : Maybe.some(patch.language))); - if (patch.currency !== undefined) operations.push(recurringProforma.setCurrency(patch.currency === null ? Maybe.none() : Maybe.some(patch.currency))); - if (patch.proformaSeries !== undefined) operations.push(recurringProforma.setProformaSeries(patch.proformaSeries === null ? Maybe.none() : Maybe.some(patch.proformaSeries))); - if (patch.issuedInvoiceSeries !== undefined) operations.push(recurringProforma.setIssuedInvoiceSeries(patch.issuedInvoiceSeries === null ? Maybe.none() : Maybe.some(patch.issuedInvoiceSeries))); - if (schedule !== undefined) operations.push(recurringProforma.setSchedule(schedule, { hasOccurrences })); - if (patch.items !== undefined) operations.push(recurringProforma.replaceItems(patch.items)); + if (patch.name !== undefined) { + operations.push(recurringProforma.rename(patch.name)); + } + if (patch.description !== undefined) { + operations.push( + recurringProforma.setDescription( + patch.description === null ? Maybe.none() : Maybe.some(patch.description) + ) + ); + } + if (patch.globalDiscountPercentage !== undefined) { + operations.push( + recurringProforma.setGlobalDiscountPercentage( + patch.globalDiscountPercentage === null + ? Maybe.none() + : Maybe.some(patch.globalDiscountPercentage) + ) + ); + } + if (patch.customerId !== undefined) { + operations.push( + recurringProforma.setCustomerId( + patch.customerId === null ? Maybe.none() : Maybe.some(patch.customerId) + ) + ); + } + if (patch.paymentTermId !== undefined) { + operations.push( + recurringProforma.setPaymentTermId( + patch.paymentTermId === null ? Maybe.none() : Maybe.some(patch.paymentTermId) + ) + ); + } + if (patch.paymentMethodId !== undefined) { + operations.push( + recurringProforma.setPaymentMethodId( + patch.paymentMethodId === null ? Maybe.none() : Maybe.some(patch.paymentMethodId) + ) + ); + } + if (patch.language !== undefined) { + operations.push( + recurringProforma.setLanguage( + patch.language === null ? Maybe.none() : Maybe.some(patch.language) + ) + ); + } + if (patch.currency !== undefined) { + operations.push( + recurringProforma.setCurrency( + patch.currency === null ? Maybe.none() : Maybe.some(patch.currency) + ) + ); + } + if (patch.proformaSeries !== undefined) { + operations.push( + recurringProforma.setProformaSeries( + patch.proformaSeries === null ? Maybe.none() : Maybe.some(patch.proformaSeries) + ) + ); + } + if (patch.issuedInvoiceSeries !== undefined) { + operations.push( + recurringProforma.setIssuedInvoiceSeries( + patch.issuedInvoiceSeries === null ? Maybe.none() : Maybe.some(patch.issuedInvoiceSeries) + ) + ); + } + if (schedule !== undefined) { + operations.push(recurringProforma.setSchedule(schedule, { hasOccurrences })); + } + if (patch.items !== undefined) { + operations.push(recurringProforma.replaceItems(patch.items)); + } let changed = false; for (const operation of operations) { - if (operation.isFailure) return Result.fail(operation.error); + if (operation.isFailure) { + return Result.fail(operation.error); + } changed ||= operation.data; } return Result.ok(changed); diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts b/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts index 0b0a323e..315dd104 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/aggregates/issued-invoice.aggregate.ts @@ -28,10 +28,7 @@ import { type VerifactuRecord, } from "../entities"; import { IssuedInvoiceItemMismatch } from "../errors"; -import type { - IssuedInvoiceFiscalClassification, - IssuedInvoiceRecipient, -} from "../value-objects"; +import type { IssuedInvoiceFiscalClassification, IssuedInvoiceRecipient } from "../value-objects"; export interface IIssuedInvoiceCreateProps { companyId: UniqueID; diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-items/issued-invoice-item.entity.ts b/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-items/issued-invoice-item.entity.ts index 76d78702..e66b394e 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-items/issued-invoice-item.entity.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-items/issued-invoice-item.entity.ts @@ -107,7 +107,9 @@ export class IssuedInvoiceItem id?: UniqueID ): Result { const taxNamesResult = validateTaxNames(props); - if (taxNamesResult.isFailure) return Result.fail(taxNamesResult.error); + if (taxNamesResult.isFailure) { + return Result.fail(taxNamesResult.error); + } const item = new IssuedInvoiceItem(props, id); @@ -242,7 +244,9 @@ function validateTaxNames(props: IIssuedInvoiceItemCreateProps): Result { this.currencyCode.equals(item.currencyCode) && this.globalDiscountPercentage.equals(item.globalDiscountPercentage) ) - ) + ) { return false; + } return super.add(item); } diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-taxes/issued-invoice-tax.entity.ts b/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-taxes/issued-invoice-tax.entity.ts index 825027d9..c56ae205 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-taxes/issued-invoice-tax.entity.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/entities/issued-invoice-taxes/issued-invoice-tax.entity.ts @@ -30,12 +30,18 @@ export class IssuedInvoiceTax extends DomainEntity { props: IssuedInvoiceTaxProps, id?: UniqueID ): Result { - if (props.ivaName.length === 0) return Result.fail(new Error("Issued invoice tax IVA name is required")); + if (props.ivaName.length === 0) { + return Result.fail(new Error("Issued invoice tax IVA name is required")); + } if (props.recCode.isSome() !== props.recName.isSome()) { - return Result.fail(new Error("Issued invoice tax REC code and name must be present together")); + return Result.fail( + new Error("Issued invoice tax REC code and name must be present together") + ); } if (props.retentionCode.isSome() !== props.retentionName.isSome()) { - return Result.fail(new Error("Issued invoice tax retention code and name must be present together")); + return Result.fail( + new Error("Issued invoice tax retention code and name must be present together") + ); } return Result.ok(new IssuedInvoiceTax(props, id)); } diff --git a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-fiscal-classification.vo.ts b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-fiscal-classification.vo.ts index f43a764a..80fa79b9 100644 --- a/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-fiscal-classification.vo.ts +++ b/modules/customer-invoices/src/api/domain/issued-invoices/value-objects/issued-invoice-fiscal-classification.vo.ts @@ -11,7 +11,9 @@ export type IssuedInvoiceFiscalClassificationProps = { export class IssuedInvoiceFiscalClassification extends ValueObject { protected static validate(values: IssuedInvoiceFiscalClassificationProps) { if (!values.taxRegimeCode.trim()) { - return Result.fail(new Error("Issued invoice fiscal classification requires tax regime code")); + return Result.fail( + new Error("Issued invoice fiscal classification requires tax regime code") + ); } return Result.ok(values); @@ -64,7 +66,10 @@ export class IssuedInvoiceFiscalClassification extends ValueObject value), - operation_qualification_code: maybeToNullable(this.operationQualificationCode, (value) => value), + operation_qualification_code: maybeToNullable( + this.operationQualificationCode, + (value) => value + ), operation_qualification_label: null, exemption_reason_code: maybeToNullable(this.exemptionReasonCode, (value) => value), exemption_reason_label: null, diff --git a/modules/customer-invoices/src/api/domain/proformas/aggregates/proforma.aggregate.ts b/modules/customer-invoices/src/api/domain/proformas/aggregates/proforma.aggregate.ts index 1ee45c9f..b2222c59 100644 --- a/modules/customer-invoices/src/api/domain/proformas/aggregates/proforma.aggregate.ts +++ b/modules/customer-invoices/src/api/domain/proformas/aggregates/proforma.aggregate.ts @@ -40,8 +40,8 @@ import { import type { IProformaTaxTotals, ProformaCalculationContext } from "../services"; import { canManuallyTransitionProformaStatus } from "../services"; import { - ProformaItemTaxes, type ProformaItemTax, + ProformaItemTaxes, type ProformaItemTaxesProps, type ProformaRecipient, type ProformaTaxConfig, @@ -205,7 +205,7 @@ export class Proforma extends AggregateRoot implements IP } private static validateCreateProps( - props: IProformaCreateProps | ProformaInternalProps + _props: IProformaCreateProps | ProformaInternalProps ): Result { return Result.ok(); } @@ -855,14 +855,18 @@ export class Proforma extends AggregateRoot implements IP public addItem(props: IProformaItemCreateProps): Result { const taxesResult = ProformaItemTaxes.create(props.taxes); - if (taxesResult.isFailure) return Result.fail(taxesResult.error); + if (taxesResult.isFailure) { + return Result.fail(taxesResult.error); + } const itemResult = ProformaItem.create({ ...props, taxes: taxesResult.data, }); - if (itemResult.isFailure) return Result.fail(itemResult.error); + if (itemResult.isFailure) { + return Result.fail(itemResult.error); + } const added = this._items.add(itemResult.data); diff --git a/modules/customer-invoices/src/api/domain/proformas/errors/index.ts b/modules/customer-invoices/src/api/domain/proformas/errors/index.ts index 2bae4230..128e3c94 100644 --- a/modules/customer-invoices/src/api/domain/proformas/errors/index.ts +++ b/modules/customer-invoices/src/api/domain/proformas/errors/index.ts @@ -1,8 +1,8 @@ -export * from "./proforma-cannot-be-archived-error"; export * from "./customer-invoice-id-already-exits-error"; export * from "./entity-is-not-proforma-error"; export * from "./invalid-proforma-transition-error"; -export * from "./proforma-cannot-be-unarchived-error"; +export * from "./proforma-cannot-be-archived-error"; export * from "./proforma-cannot-be-converted-to-invoice-error"; export * from "./proforma-cannot-be-deleted-error"; +export * from "./proforma-cannot-be-unarchived-error"; export * from "./proforma-item-not-valid-error"; diff --git a/modules/customer-invoices/src/api/domain/proformas/errors/proforma-cannot-be-archived-error.ts b/modules/customer-invoices/src/api/domain/proformas/errors/proforma-cannot-be-archived-error.ts index 82894cca..5dc1c1ef 100644 --- a/modules/customer-invoices/src/api/domain/proformas/errors/proforma-cannot-be-archived-error.ts +++ b/modules/customer-invoices/src/api/domain/proformas/errors/proforma-cannot-be-archived-error.ts @@ -10,6 +10,5 @@ export class ProformaCannotBeArchivedError extends DomainError { } } -export const isProformaCannotBeArchivedError = ( - e: unknown -): e is ProformaCannotBeArchivedError => e instanceof ProformaCannotBeArchivedError; +export const isProformaCannotBeArchivedError = (e: unknown): e is ProformaCannotBeArchivedError => + e instanceof ProformaCannotBeArchivedError; diff --git a/modules/customer-invoices/src/api/domain/proformas/policies/proforma-archive-policy.ts b/modules/customer-invoices/src/api/domain/proformas/policies/proforma-archive-policy.ts index fbc706ed..75b2930d 100644 --- a/modules/customer-invoices/src/api/domain/proformas/policies/proforma-archive-policy.ts +++ b/modules/customer-invoices/src/api/domain/proformas/policies/proforma-archive-policy.ts @@ -1,4 +1,4 @@ -import { type InvoiceStatus, INVOICE_STATUS } from "../../common/value-objects"; +import { INVOICE_STATUS, type InvoiceStatus } from "../../common/value-objects"; export const ARCHIVABLE_PROFORMA_STATUSES = [ INVOICE_STATUS.DRAFT, diff --git a/modules/customer-invoices/src/api/domain/proformas/services/proforma-compare-tax-totals.ts b/modules/customer-invoices/src/api/domain/proformas/services/proforma-compare-tax-totals.ts index 03c21ea4..cc474732 100644 --- a/modules/customer-invoices/src/api/domain/proformas/services/proforma-compare-tax-totals.ts +++ b/modules/customer-invoices/src/api/domain/proformas/services/proforma-compare-tax-totals.ts @@ -11,19 +11,29 @@ import type { IProformaTaxTotals } from "./proforma-taxes-calculator"; */ export function proformaCompareTaxTotals(a: IProformaTaxTotals, b: IProformaTaxTotals): number { const byIvaCode = compareMaybeCodeNoneFirst(a.ivaCode, b.ivaCode); - if (byIvaCode !== 0) return byIvaCode; + if (byIvaCode !== 0) { + return byIvaCode; + } const byIvaPct = compareMaybePctNoneFirst(a.ivaPercentage, b.ivaPercentage); - if (byIvaPct !== 0) return byIvaPct; + if (byIvaPct !== 0) { + return byIvaPct; + } const byRecCode = compareMaybeCodeNoneFirst(a.recCode, b.recCode); - if (byRecCode !== 0) return byRecCode; + if (byRecCode !== 0) { + return byRecCode; + } const byRecPct = compareMaybePctNoneFirst(a.recPercentage, b.recPercentage); - if (byRecPct !== 0) return byRecPct; + if (byRecPct !== 0) { + return byRecPct; + } const byRetCode = compareMaybeCodeNoneFirst(a.retentionCode, b.retentionCode); - if (byRetCode !== 0) return byRetCode; + if (byRetCode !== 0) { + return byRetCode; + } return compareMaybePctNoneFirst(a.retentionPercentage, b.retentionPercentage); } @@ -39,17 +49,29 @@ function comparePct(a: TaxPercentage, b: TaxPercentage): number { } function compareMaybeCodeNoneFirst(a: Maybe, b: Maybe): number { - if (a.isNone() && b.isNone()) return 0; - if (a.isNone() && b.isSome()) return -1; // None primero - if (a.isSome() && b.isNone()) return 1; + if (a.isNone() && b.isNone()) { + return 0; + } + if (a.isNone() && b.isSome()) { + return -1; // None primero + } + if (a.isSome() && b.isNone()) { + return 1; + } return compareCode(a.unwrap(), b.unwrap()); } function compareMaybePctNoneFirst(a: Maybe, b: Maybe): number { - if (a.isNone() && b.isNone()) return 0; - if (a.isNone() && b.isSome()) return -1; // None primero - if (a.isSome() && b.isNone()) return 1; + if (a.isNone() && b.isNone()) { + return 0; + } + if (a.isNone() && b.isSome()) { + return -1; // None primero + } + if (a.isSome() && b.isNone()) { + return 1; + } return comparePct(a.unwrap(), b.unwrap()); } diff --git a/modules/customer-invoices/src/api/domain/proformas/services/proforma-compute-tax-groups.ts b/modules/customer-invoices/src/api/domain/proformas/services/proforma-compute-tax-groups.ts index 442d50c0..dda26f88 100644 --- a/modules/customer-invoices/src/api/domain/proformas/services/proforma-compute-tax-groups.ts +++ b/modules/customer-invoices/src/api/domain/proformas/services/proforma-compute-tax-groups.ts @@ -101,7 +101,9 @@ function ensureTaxNameMatches( tax: Maybe, kind: string ): void { - if (code.isNone() || tax.isNone()) return; + if (code.isNone() || tax.isNone()) { + return; + } if (code.unwrap() !== tax.unwrap().code || name.isNone() || name.unwrap() !== tax.unwrap().name) { throw new Error(`Inconsistent ${kind} tax metadata in grouped proforma taxes`); } @@ -113,7 +115,9 @@ function ensureTaxPercentageMatches( tax: Maybe, kind: string ): void { - if (code.isNone() || tax.isNone()) return; + if (code.isNone() || tax.isNone()) { + return; + } if ( code.unwrap() !== tax.unwrap().code || percentage.isNone() || diff --git a/modules/customer-invoices/src/api/domain/proformas/services/proforma-taxes-calculator.test.ts b/modules/customer-invoices/src/api/domain/proformas/services/proforma-taxes-calculator.test.ts index 74cd8ea2..f9df215d 100644 --- a/modules/customer-invoices/src/api/domain/proformas/services/proforma-taxes-calculator.test.ts +++ b/modules/customer-invoices/src/api/domain/proformas/services/proforma-taxes-calculator.test.ts @@ -6,8 +6,8 @@ import { Maybe } from "@repo/rdx-utils"; import { ItemAmount, ItemQuantity } from "../../common"; import type { IProformaItemCreateProps } from "../entities"; -import { ProformaItemTax } from "../value-objects"; import { createProformaTestFixture } from "../testing/proforma-test-fixture"; +import { ProformaItemTax } from "../value-objects"; const required = (result: { data: T }): T => result.data; diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/index.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/index.ts index c4d5f747..c7da2be1 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/index.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/index.ts @@ -1,5 +1,5 @@ export * from "./recurrence-schedule"; +export * from "./recurring-proforma"; export * from "./recurring-proforma-item"; export * from "./recurring-proforma-items"; -export * from "./recurring-proforma"; export * from "./recurring-proforma-occurrence"; diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-item.test.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-item.test.ts index c01e80f8..96251a2c 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-item.test.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-item.test.ts @@ -36,13 +36,25 @@ test("RecurringProformaItem reutiliza los VOs reales de proforma", () => { }); test("RecurringProformaItem conserva códigos fiscales configurables sin resolver impuestos", () => { - const empty = required(RecurringProformaItem.create({ - description: Maybe.none(), quantity: Maybe.none(), unitAmount: Maybe.none(), itemDiscountPercentage: Maybe.none(), - })); - const configured = required(RecurringProformaItem.create({ - description: Maybe.none(), quantity: Maybe.none(), unitAmount: Maybe.none(), itemDiscountPercentage: Maybe.none(), - ivaCode: Maybe.some("iva_21"), recCode: Maybe.some("rec_5_2"), retentionCode: Maybe.some("retention_15"), - })); + const empty = required( + RecurringProformaItem.create({ + description: Maybe.none(), + quantity: Maybe.none(), + unitAmount: Maybe.none(), + itemDiscountPercentage: Maybe.none(), + }) + ); + const configured = required( + RecurringProformaItem.create({ + description: Maybe.none(), + quantity: Maybe.none(), + unitAmount: Maybe.none(), + itemDiscountPercentage: Maybe.none(), + ivaCode: Maybe.some("iva_21"), + recCode: Maybe.some("rec_5_2"), + retentionCode: Maybe.some("retention_15"), + }) + ); assert.equal(empty.ivaCode.isNone(), true); assert.equal(empty.recCode.isNone(), true); diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.test.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.test.ts index b4d13ac5..82e232a6 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.test.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.test.ts @@ -13,9 +13,7 @@ const createOccurrence = () => RecurringProformaOccurrence.create( { companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000201")), - recurringProformaId: required( - UniqueID.create("01900000-0000-7000-8000-000000000202") - ), + recurringProformaId: required(UniqueID.create("01900000-0000-7000-8000-000000000202")), scheduledDate: new Date("2026-09-01T00:00:00.000Z"), createdAt: new Date("2026-08-30T09:00:00.000Z"), updatedAt: new Date("2026-08-30T09:00:00.000Z"), diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.ts index 5fd72382..c4c97198 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma-occurrence.ts @@ -1,11 +1,7 @@ import { DomainEntity, DomainValidationError, type UniqueID } from "@repo/rdx-ddd"; import { type Maybe, Result } from "@repo/rdx-utils"; -export type RecurringProformaOccurrenceStatus = - | "pending" - | "processing" - | "failed" - | "generated"; +export type RecurringProformaOccurrenceStatus = "pending" | "processing" | "failed" | "generated"; export interface RecurringProformaOccurrenceCreateProps { companyId: UniqueID; @@ -57,9 +53,8 @@ export class RecurringProformaOccurrence extends DomainEntity RecurringProformaOccurrence.cloneDate(value)) ?? Maybe.none(), processingStartedAt: - props.processingStartedAt?.map((value) => - RecurringProformaOccurrence.cloneDate(value) - ) ?? Maybe.none(), + props.processingStartedAt?.map((value) => RecurringProformaOccurrence.cloneDate(value)) ?? + Maybe.none(), lastErrorCode: props.lastErrorCode ?? Maybe.none(), lastErrorMessage: props.lastErrorMessage ?? Maybe.none(), proformaId: props.proformaId ?? Maybe.none(), @@ -172,11 +167,7 @@ export class RecurringProformaOccurrence extends DomainEntity { - return this.props.lastAttemptAt.map((value) => - RecurringProformaOccurrence.cloneDate(value) - ); + return this.props.lastAttemptAt.map((value) => RecurringProformaOccurrence.cloneDate(value)); } public get processingStartedAt(): Maybe { @@ -244,9 +231,7 @@ export class RecurringProformaOccurrence extends DomainEntity { - return this.props.generatedAt.map((value) => - RecurringProformaOccurrence.cloneDate(value) - ); + return this.props.generatedAt.map((value) => RecurringProformaOccurrence.cloneDate(value)); } public get createdAt(): Date { diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.test.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.test.ts index 3956013b..3e2a4a61 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.test.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.test.ts @@ -5,15 +5,11 @@ import { DiscountPercentage } from "@erp/core/api"; import { CurrencyCode, LanguageCode, UniqueID } from "@repo/rdx-ddd"; import { Maybe } from "@repo/rdx-utils"; -import { ItemAmount, ItemDescription, ItemQuantity } from "../common"; -import { InvoiceSerie } from "../common"; +import { InvoiceSerie, ItemAmount, ItemDescription, ItemQuantity } from "../common"; import { RecurrenceSchedule } from "./recurrence-schedule"; +import { RecurringProforma, type RecurringProformaInternalProps } from "./recurring-proforma"; import type { RecurringProformaItemCreateProps } from "./recurring-proforma-item"; -import { - RecurringProforma, - type RecurringProformaInternalProps, -} from "./recurring-proforma"; import { RecurringProformaItems } from "./recurring-proforma-items"; const required = (result: { data: T }): T => result.data; @@ -60,7 +56,10 @@ test("RecurringProforma conserva y limpia preferencias de series funcionales", ( const issuedInvoiceSeries = required(InvoiceSerie.create("INV")); assert.equal(required(recurringProforma.setProformaSeries(Maybe.some(proformaSeries))), true); - assert.equal(required(recurringProforma.setIssuedInvoiceSeries(Maybe.some(issuedInvoiceSeries))), true); + assert.equal( + required(recurringProforma.setIssuedInvoiceSeries(Maybe.some(issuedInvoiceSeries))), + true + ); assert.equal(recurringProforma.proformaSeries.unwrap().toPrimitive(), "PRO"); assert.equal(recurringProforma.issuedInvoiceSeries.unwrap().toPrimitive(), "INV"); assert.equal(required(recurringProforma.setProformaSeries(Maybe.none())), true); @@ -96,7 +95,10 @@ test("RecurringProforma crea, actualiza y rehidrata la descripción comercial", ); assert.equal(recurringProforma.description.unwrap(), "Servicio recurrente"); - assert.equal(required(recurringProforma.setDescription(Maybe.some("Servicio actualizado"))), true); + assert.equal( + required(recurringProforma.setDescription(Maybe.some("Servicio actualizado"))), + true + ); assert.equal(recurringProforma.description.unwrap(), "Servicio actualizado"); const rehydrated = required( @@ -341,8 +343,14 @@ test("RecurringProforma bloquea cambios de calendario cuando Application informa ); required(recurringProforma.setSchedule(originalSchedule)); - assert.equal(required(recurringProforma.setSchedule(extendedSchedule, { hasOccurrences: true })), true); - assert.equal(recurringProforma.setSchedule(changedCalendar, { hasOccurrences: true }).isFailure, true); + assert.equal( + required(recurringProforma.setSchedule(extendedSchedule, { hasOccurrences: true })), + true + ); + assert.equal( + recurringProforma.setSchedule(changedCalendar, { hasOccurrences: true }).isFailure, + true + ); }); test("RecurringProforma limpia nextOccurrenceDate al completar y bloquea mutaciones terminales", () => { @@ -364,7 +372,12 @@ test("RecurringProforma limpia nextOccurrenceDate al completar y bloquea mutacio assert.equal(recurringProforma.status, "archived"); assert.equal(recurringProforma.replaceItems([createItem("No permitido")]).isFailure, true); assert.equal(recurringProforma.setDescription(Maybe.some("Tampoco permitido")).isFailure, true); - assert.equal(recurringProforma.setPaymentTermId(Maybe.some(required(UniqueID.create("01900000-0000-7000-8000-000000000120")))).isFailure, true); + assert.equal( + recurringProforma.setPaymentTermId( + Maybe.some(required(UniqueID.create("01900000-0000-7000-8000-000000000120"))) + ).isFailure, + true + ); }); test("RecurringProforma rechaza rehidratación de estados estructuralmente imposibles", () => { diff --git a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.ts b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.ts index fef6ffa0..7f7d02b1 100644 --- a/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.ts +++ b/modules/customer-invoices/src/api/domain/recurring-proformas/recurring-proforma.ts @@ -677,8 +677,12 @@ export class RecurringProforma extends AggregateRoot boolean ): Result { const mutable = this.ensureMutable(); - if (mutable.isFailure) return Result.fail(mutable.error); - if (this.sameMaybe(current, value, equals)) return Result.ok(false); + if (mutable.isFailure) { + return Result.fail(mutable.error); + } + if (this.sameMaybe(current, value, equals)) { + return Result.ok(false); + } assign(value); return Result.ok(true); } diff --git a/modules/customer-invoices/src/api/index.ts b/modules/customer-invoices/src/api/index.ts index fa84ed8f..5432c7e7 100644 --- a/modules/customer-invoices/src/api/index.ts +++ b/modules/customer-invoices/src/api/index.ts @@ -2,11 +2,11 @@ import type { IModuleServer } from "@erp/core/api"; import { buildCompanyVerifactuProfileDependencies, - companyVerifactuProfileRouter, buildIssuedInvoicePublicServices, buildIssuedInvoicesDependencies, buildProformaPublicServices, buildProformasDependencies, + companyVerifactuProfileRouter, issuedInvoicesRouter, models, proformasRouter, diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/index.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/index.ts index 8fed183d..a42d39f4 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/index.ts @@ -1,4 +1,7 @@ import companyVerifactuProfileModelInit from "./models/company-verifactu-profile.model"; +import recurringProformaModelInit from "./models/recurring-proforma.model"; +import recurringProformaItemModelInit from "./models/recurring-proforma-item.model"; +import recurringProformaOccurrenceModelInit from "./models/recurring-proforma-occurrence.model"; import issuedInvoiceModelInit from "./models/sequelize-issued-invoice.model"; import issuedInvoiceItemModelInit from "./models/sequelize-issued-invoice-item.model"; import issuedInvoiceTaxModelInit from "./models/sequelize-issued-invoice-tax.model"; @@ -6,9 +9,6 @@ import proformaModelInit from "./models/sequelize-proforma.model"; import proformaItemModelInit from "./models/sequelize-proforma-item.model"; import proformaTaxModelInit from "./models/sequelize-proforma-tax.model"; import verifactuRecordModelInit from "./models/verifactu-record.model"; -import recurringProformaModelInit from "./models/recurring-proforma.model"; -import recurringProformaItemModelInit from "./models/recurring-proforma-item.model"; -import recurringProformaOccurrenceModelInit from "./models/recurring-proforma-occurrence.model"; export * from "./models"; diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/company-verifactu-profile.model.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/company-verifactu-profile.model.ts index 5c8fd809..b9c5732e 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/company-verifactu-profile.model.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/company-verifactu-profile.model.ts @@ -6,9 +6,8 @@ import { type Sequelize, } from "sequelize"; -export type CompanyVerifactuProfileCreationAttributes = InferCreationAttributes< - CompanyVerifactuProfileModel ->; +export type CompanyVerifactuProfileCreationAttributes = + InferCreationAttributes; export class CompanyVerifactuProfileModel extends Model< InferAttributes, diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/index.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/index.ts index 22f3570f..e5b71da7 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/index.ts @@ -1,11 +1,11 @@ export * from "./company-verifactu-profile.model"; -export * from "./sequelize-proforma.model"; -export * from "./sequelize-proforma-item.model"; -export * from "./sequelize-proforma-tax.model"; -export * from "./sequelize-issued-invoice.model"; -export * from "./sequelize-issued-invoice-item.model"; -export * from "./sequelize-issued-invoice-tax.model"; -export * from "./verifactu-record.model"; export * from "./recurring-proforma.model"; export * from "./recurring-proforma-item.model"; export * from "./recurring-proforma-occurrence.model"; +export * from "./sequelize-issued-invoice.model"; +export * from "./sequelize-issued-invoice-item.model"; +export * from "./sequelize-issued-invoice-tax.model"; +export * from "./sequelize-proforma.model"; +export * from "./sequelize-proforma-item.model"; +export * from "./sequelize-proforma-tax.model"; +export * from "./verifactu-record.model"; diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-item.model.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-item.model.ts index 0c04896a..51cd0f18 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-item.model.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-item.model.ts @@ -31,7 +31,7 @@ export class RecurringProformaItemModel extends Model< public static associate(database: Sequelize): void { const { RecurringProformaModel, RecurringProformaItemModel } = database.models; - if (!RecurringProformaModel || !RecurringProformaItemModel) { + if (!(RecurringProformaModel && RecurringProformaItemModel)) { throw new Error("[RecurringProformaItemModel.associate] Missing recurring proforma models"); } RecurringProformaItemModel.belongsTo(RecurringProformaModel, { @@ -54,14 +54,28 @@ export default (database: Sequelize) => { unit_amount_value: { type: DataTypes.BIGINT, allowNull: true, defaultValue: null }, unit_amount_scale: { type: DataTypes.SMALLINT, allowNull: false, defaultValue: 4 }, unit_amount_currency: { type: DataTypes.STRING(3), allowNull: true, defaultValue: null }, - item_discount_percentage_value: { type: DataTypes.SMALLINT, allowNull: true, defaultValue: null }, - item_discount_percentage_scale: { type: DataTypes.SMALLINT, allowNull: false, defaultValue: 2 }, + item_discount_percentage_value: { + type: DataTypes.SMALLINT, + allowNull: true, + defaultValue: null, + }, + item_discount_percentage_scale: { + type: DataTypes.SMALLINT, + allowNull: false, + defaultValue: 2, + }, iva_code: { type: DataTypes.STRING(32), allowNull: true, defaultValue: null }, rec_code: { type: DataTypes.STRING(32), allowNull: true, defaultValue: null }, retention_code: { type: DataTypes.STRING(32), allowNull: true, defaultValue: null }, position: { type: DataTypes.MEDIUMINT.UNSIGNED, allowNull: false }, }, - { sequelize: database, modelName: "RecurringProformaItemModel", tableName: "recurring_proforma_items", timestamps: false, underscored: true } + { + sequelize: database, + modelName: "RecurringProformaItemModel", + tableName: "recurring_proforma_items", + timestamps: false, + underscored: true, + } ); return RecurringProformaItemModel; }; diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-models.test.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-models.test.ts index 98ec2762..c34542ad 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-models.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-models.test.ts @@ -3,13 +3,13 @@ import test from "node:test"; import { Sequelize } from "sequelize"; +import recurringProformaModelInit, { RecurringProformaModel } from "./recurring-proforma.model"; import recurringProformaItemModelInit, { RecurringProformaItemModel, } from "./recurring-proforma-item.model"; import recurringProformaOccurrenceModelInit, { RecurringProformaOccurrenceModel, } from "./recurring-proforma-occurrence.model"; -import recurringProformaModelInit, { RecurringProformaModel } from "./recurring-proforma.model"; test("modelos de recurring proformas declaran tablas, nulabilidad e índices contractuales", () => { const database = new Sequelize("mysql://root:pass@localhost:3306/erp_test", { logging: false }); @@ -24,7 +24,8 @@ test("modelos de recurring proformas declaran tablas, nulabilidad e índices con assert.equal(RecurringProformaOccurrenceModel.getTableName(), "recurring_proforma_occurrences"); assert.equal( RecurringProformaOccurrenceModel.options.indexes?.some( - (index) => index.name === "uq_recurring_proforma_occurrences_schedule" && index.unique === true + (index) => + index.name === "uq_recurring_proforma_occurrences_schedule" && index.unique === true ), true ); diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-occurrence.model.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-occurrence.model.ts index 3c6d5a6b..1fb1fd94 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-occurrence.model.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma-occurrence.model.ts @@ -8,12 +8,15 @@ import { type Sequelize, } from "sequelize"; -import type { ProformaModel } from "./sequelize-proforma.model"; import type { RecurringProformaModel } from "./recurring-proforma.model"; +import type { ProformaModel } from "./sequelize-proforma.model"; export class RecurringProformaOccurrenceModel extends Model< InferAttributes, - InferCreationAttributes + InferCreationAttributes< + RecurringProformaOccurrenceModel, + { omit: "recurring_proforma" | "proforma" } + > > { declare id: string; declare company_id: string; @@ -33,12 +36,23 @@ export class RecurringProformaOccurrenceModel extends Model< declare proforma: NonAttribute; public static associate(database: Sequelize): void { - const { RecurringProformaModel, RecurringProformaOccurrenceModel, ProformaModel } = database.models; - if (!RecurringProformaModel || !RecurringProformaOccurrenceModel || !ProformaModel) { + const { RecurringProformaModel, RecurringProformaOccurrenceModel, ProformaModel } = + database.models; + if (!(RecurringProformaModel && RecurringProformaOccurrenceModel && ProformaModel)) { throw new Error("[RecurringProformaOccurrenceModel.associate] Missing required models"); } - RecurringProformaOccurrenceModel.belongsTo(RecurringProformaModel, { as: "recurring_proforma", foreignKey: "recurring_proforma_id", onDelete: "RESTRICT", onUpdate: "CASCADE" }); - RecurringProformaOccurrenceModel.belongsTo(ProformaModel, { as: "proforma", foreignKey: "proforma_id", onDelete: "RESTRICT", onUpdate: "CASCADE" }); + RecurringProformaOccurrenceModel.belongsTo(RecurringProformaModel, { + as: "recurring_proforma", + foreignKey: "recurring_proforma_id", + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }); + RecurringProformaOccurrenceModel.belongsTo(ProformaModel, { + as: "proforma", + foreignKey: "proforma_id", + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }); } } @@ -67,10 +81,24 @@ export default (database: Sequelize) => { createdAt: "created_at", updatedAt: "updated_at", indexes: [ - { name: "uq_recurring_proforma_occurrences_schedule", fields: ["recurring_proforma_id", "scheduled_date"], unique: true }, - { name: "uq_recurring_proforma_occurrences_proforma", fields: ["proforma_id"], unique: true }, - { name: "idx_recurring_proforma_occurrences_company_status", fields: ["company_id", "status"] }, - { name: "idx_recurring_proforma_occurrences_company_status_processing", fields: ["company_id", "status", "processing_started_at"] }, + { + name: "uq_recurring_proforma_occurrences_schedule", + fields: ["recurring_proforma_id", "scheduled_date"], + unique: true, + }, + { + name: "uq_recurring_proforma_occurrences_proforma", + fields: ["proforma_id"], + unique: true, + }, + { + name: "idx_recurring_proforma_occurrences_company_status", + fields: ["company_id", "status"], + }, + { + name: "idx_recurring_proforma_occurrences_company_status_processing", + fields: ["company_id", "status", "processing_started_at"], + }, ], } ); diff --git a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma.model.ts b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma.model.ts index bdfe50ce..f67c4228 100644 --- a/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma.model.ts +++ b/modules/customer-invoices/src/api/infrastructure/common/persistence/sequelize/models/recurring-proforma.model.ts @@ -44,7 +44,9 @@ export class RecurringProformaModel extends Model< const { RecurringProformaModel, RecurringProformaItemModel, RecurringProformaOccurrenceModel } = database.models; - if (!RecurringProformaModel || !RecurringProformaItemModel || !RecurringProformaOccurrenceModel) { + if ( + !(RecurringProformaModel && RecurringProformaItemModel && RecurringProformaOccurrenceModel) + ) { throw new Error("[RecurringProformaModel.associate] Missing recurring proforma models"); } @@ -70,8 +72,16 @@ export default (database: Sequelize) => { company_id: { type: DataTypes.UUID, allowNull: false } as any, name: { type: DataTypes.STRING(), allowNull: false }, description: { type: DataTypes.TEXT(), allowNull: true, defaultValue: null }, - global_discount_percentage_value: { type: DataTypes.SMALLINT, allowNull: true, defaultValue: null }, - global_discount_percentage_scale: { type: DataTypes.SMALLINT, allowNull: true, defaultValue: null }, + global_discount_percentage_value: { + type: DataTypes.SMALLINT, + allowNull: true, + defaultValue: null, + }, + global_discount_percentage_scale: { + type: DataTypes.SMALLINT, + allowNull: true, + defaultValue: null, + }, status: { type: DataTypes.STRING(16), allowNull: false }, customer_id: { type: DataTypes.UUID, allowNull: true, defaultValue: null } as any, payment_term_id: { type: DataTypes.UUID, allowNull: true, defaultValue: null } as any, @@ -79,7 +89,11 @@ export default (database: Sequelize) => { language: { type: DataTypes.STRING(2), allowNull: true, defaultValue: null }, currency: { type: DataTypes.STRING(3), allowNull: true, defaultValue: null }, proforma_series_code: { type: DataTypes.STRING(32), allowNull: true, defaultValue: null }, - issued_invoice_series_code: { type: DataTypes.STRING(32), allowNull: true, defaultValue: null }, + issued_invoice_series_code: { + type: DataTypes.STRING(32), + allowNull: true, + defaultValue: null, + }, schedule_start_date: { type: DataTypes.DATEONLY, allowNull: true, defaultValue: null }, schedule_end_date: { type: DataTypes.DATEONLY, allowNull: true, defaultValue: null }, schedule_unit: { type: DataTypes.STRING(8), allowNull: true, defaultValue: null }, @@ -96,7 +110,10 @@ export default (database: Sequelize) => { createdAt: "created_at", updatedAt: "updated_at", indexes: [ - { name: "idx_recurring_proformas_company_status_next", fields: ["company_id", "status", "next_occurrence_date"] }, + { + name: "idx_recurring_proformas_company_status_next", + fields: ["company_id", "status", "next_occurrence_date"], + }, { name: "idx_recurring_proformas_company_customer", fields: ["company_id", "customer_id"] }, ], } diff --git a/modules/customer-invoices/src/api/infrastructure/company-verifactu-profile/express/controllers/company-verifactu-profile.controllers.test.ts b/modules/customer-invoices/src/api/infrastructure/company-verifactu-profile/express/controllers/company-verifactu-profile.controllers.test.ts index 9fa6e83b..3e904b77 100644 --- a/modules/customer-invoices/src/api/infrastructure/company-verifactu-profile/express/controllers/company-verifactu-profile.controllers.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/company-verifactu-profile/express/controllers/company-verifactu-profile.controllers.test.ts @@ -65,7 +65,11 @@ test("GET controller devuelve 401 sin autenticación", async () => { } as never); const res = createResponseDouble(); - await controller.execute(createRequestDouble(), res as never as Response, (() => undefined) as NextFunction); + await controller.execute( + createRequestDouble(), + res as never as Response, + (() => undefined) as NextFunction + ); assert.equal(res.statusCode, 401); }); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/documents/pre-processors/issued-invoice-signed-document-cache-pre-processor.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/documents/pre-processors/issued-invoice-signed-document-cache-pre-processor.ts index 633d71df..7ff8d09b 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/documents/pre-processors/issued-invoice-signed-document-cache-pre-processor.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/documents/pre-processors/issued-invoice-signed-document-cache-pre-processor.ts @@ -56,7 +56,9 @@ export class IssuedInvoiceSignedDocumentCachePreProcessor implements IDocumentPr * No valida firma criptográfica. */ private isValid(document: IDocument | null): boolean { - if (!document) return false; + if (!document) { + return false; + } if (!document.payload || document.payload.length === 0) { return false; diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/issued-invoices-api-error-mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/issued-invoices-api-error-mapper.ts index 0901c64d..d7da1c63 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/issued-invoices-api-error-mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/issued-invoices-api-error-mapper.ts @@ -15,7 +15,7 @@ import { InvoiceSeriesLockRequiredError, InvoiceSeriesNotFoundError, InvoiceSeriesTransactionRequiredError, - IssuedInvoiceNumberAlreadyExistsError, + type IssuedInvoiceNumberAlreadyExistsError, isIssuedInvoiceNumberAlreadyExistsError, } from "../../../application"; import { diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts index 191d39cd..c9034dd0 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-domain.mapper.ts @@ -137,7 +137,9 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< "payment_method_name", errors ); - if (paymentVO) paymentMethod = paymentVO; + if (paymentVO) { + paymentMethod = paymentVO; + } } const taxRegime = extractOrPushError( @@ -304,10 +306,10 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< currencyCode: attributes.currencyCode!, }); const fiscalClassificationResult = IssuedInvoiceFiscalClassification.create({ - taxRegimeCode: attributes.taxRegime!.code, + taxRegimeCode: attributes.taxRegime?.code, taxRegimeLabel: - attributes.taxRegime!.description.trim().length > 0 - ? Maybe.some(attributes.taxRegime!.description) + attributes.taxRegime?.description.trim().length > 0 + ? Maybe.some(attributes.taxRegime?.description) : Maybe.none(), operationQualificationCode: raw.operation_qualification_code == null @@ -377,14 +379,18 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< ...params, }); - if (itemsResult.isFailure) errors.push({ path: "items", message: itemsResult.error.message }); + if (itemsResult.isFailure) { + errors.push({ path: "items", message: itemsResult.error.message }); + } const taxesResult = this._taxesMapper.mapToPersistenceArray(source.taxes, { errors, parent: source, ...params, }); - if (taxesResult.isFailure) errors.push({ path: "taxes", message: taxesResult.error.message }); + if (taxesResult.isFailure) { + errors.push({ path: "taxes", message: taxesResult.error.message }); + } const recipient = this._recipientMapper.mapToPersistence(source.recipient, { errors, parent: source, @@ -554,11 +560,11 @@ export class SequelizeIssuedInvoiceV2DomainMapper extends SequelizeDomainMapper< private parsePaymentTermSnapshotJson(source: string): Result< { version: 1; - dueRules: Array<{ - dueDays: string; - percentage: { - value: number; - scale: number; + dueRules: Array<{ + dueDays: string; + percentage: { + value: number; + scale: number; }; }>; }, diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-item-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-item-domain.mapper.ts index 369062c5..b0930738 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-item-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-item-domain.mapper.ts @@ -36,8 +36,6 @@ export class SequelizeIssuedInvoiceV2ItemDomainMapper extends SequelizeDomainMap IssuedInvoiceItemCreationAttributes, IssuedInvoiceItem > { - private readonly taxCatalog!: JsonTaxCatalogProvider; - constructor(params: MapperParamsType) { super(); const { taxCatalog } = params as { taxCatalog: JsonTaxCatalogProvider }; diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-recipient-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-recipient-domain.mapper.ts index 3c15fe2e..181b0d94 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-recipient-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-recipient-domain.mapper.ts @@ -91,7 +91,7 @@ export class SequelizeIssuedInvoiceV2RecipientDomainMapper { return Result.ok(createResult.data); } - mapToPersistence(source: IssuedInvoiceRecipient, params?: MapperParamsType) { + mapToPersistence(source: IssuedInvoiceRecipient, _params?: MapperParamsType) { return { customer_tin: source.tin.toString(), customer_name: source.name.toPrimitive(), diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-taxes-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-taxes-domain.mapper.ts index d4443d89..84bb20cf 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-taxes-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-taxes-domain.mapper.ts @@ -35,8 +35,6 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa IssuedInvoiceTaxCreationAttributes, IssuedInvoiceTax > { - private taxCatalog!: JsonTaxCatalogProvider; - constructor(params: MapperParamsType) { super(); const { taxCatalog } = params as { taxCatalog: JsonTaxCatalogProvider }; @@ -46,7 +44,10 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa this.taxCatalog = taxCatalog; } - public mapToDomain(raw: IssuedInvoiceTaxModel, params?: MapperParamsType): Result { + public mapToDomain( + raw: IssuedInvoiceTaxModel, + params?: MapperParamsType + ): Result { const { errors, index, attributes } = params as { index: number; errors: ValidationErrorDetail[]; @@ -54,14 +55,25 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa }; const taxableAmount = extractOrPushError( - InvoiceAmount.create({ value: raw.taxable_amount_value, currency_code: attributes.currencyCode?.code }), + InvoiceAmount.create({ + value: raw.taxable_amount_value, + currency_code: attributes.currencyCode?.code, + }), `taxes[${index}].taxable_amount_value`, errors ); const ivaCode = raw.iva_code; const ivaName = raw.iva_name; - if (typeof ivaCode !== "string" || ivaCode.length === 0 || typeof ivaName !== "string" || ivaName.length === 0) { - errors.push({ path: `taxes[${index}].iva`, message: "tax code and name must be present together" }); + if ( + typeof ivaCode !== "string" || + ivaCode.length === 0 || + typeof ivaName !== "string" || + ivaName.length === 0 + ) { + errors.push({ + path: `taxes[${index}].iva`, + message: "tax code and name must be present together", + }); } const ivaPercentage = extractOrPushError( TaxPercentage.create({ value: Number(raw.iva_percentage_value) }), @@ -69,7 +81,10 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa errors ); const ivaAmount = extractOrPushError( - InvoiceAmount.create({ value: raw.iva_amount_value, currency_code: attributes.currencyCode?.code }), + InvoiceAmount.create({ + value: raw.iva_amount_value, + currency_code: attributes.currencyCode?.code, + }), `taxes[${index}].iva_amount_value`, errors ); @@ -82,7 +97,10 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa errors ); const recAmount = extractOrPushError( - InvoiceAmount.create({ value: raw.rec_amount_value, currency_code: attributes.currencyCode?.code }), + InvoiceAmount.create({ + value: raw.rec_amount_value, + currency_code: attributes.currencyCode?.code, + }), `taxes[${index}].rec_amount_value`, errors ); @@ -90,7 +108,9 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa const retentionName = maybeFromNullableOrEmptyString(raw.retention_name); ensureCodeAndName(retentionCode, retentionName, `taxes[${index}].retention`, errors); const retentionPercentage = extractOrPushError( - maybeFromNullableResult(raw.retention_percentage_value, (value) => TaxPercentage.create({ value })), + maybeFromNullableResult(raw.retention_percentage_value, (value) => + TaxPercentage.create({ value }) + ), `taxes[${index}].retention_percentage_value`, errors ); @@ -103,7 +123,10 @@ export class SequelizeIssuedInvoiceV2TaxesDomainMapper extends SequelizeDomainMa errors ); const taxesAmount = extractOrPushError( - InvoiceAmount.create({ value: raw.taxes_amount_value, currency_code: attributes.currencyCode?.code }), + InvoiceAmount.create({ + value: raw.taxes_amount_value, + currency_code: attributes.currencyCode?.code, + }), `taxes[${index}].taxes_amount_value`, errors ); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts index d224d9b2..ac001fcf 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.test.ts @@ -8,17 +8,20 @@ import { SequelizeIssuedInvoiceV2VerifactuDomainMapper } from "./sequelize-issue test("rehidrata un VerifactuRecord histórico cuando existe en persistencia", () => { const mapper = new SequelizeIssuedInvoiceV2VerifactuDomainMapper(); - const result = mapper.mapToDomain({ - id: "11111111-1111-4111-8111-111111111111", - estado: "Correcto", - qr: "qr-value", - url: "https://example.com/verifactu/1", - uuid: "vf-uuid", - operacion: "alta", - } as never, { - errors: [], - attributes: {}, - }); + const result = mapper.mapToDomain( + { + id: "11111111-1111-4111-8111-111111111111", + estado: "Correcto", + qr: "qr-value", + url: "https://example.com/verifactu/1", + uuid: "vf-uuid", + operacion: "alta", + } as never, + { + errors: [], + attributes: {}, + } + ); assert.equal(result.isSuccess, true); assert.equal(result.data.isSome(), true); diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts index 077b53ab..01d382ab 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/domain/sequelize-issued-invoice-v2-verifactu-domain.mapper.ts @@ -17,7 +17,10 @@ import { VerifactuRecord, VerifactuRecordEstado, } from "../../../../../../domain"; -import type { VerifactuRecordCreationAttributes, VerifactuRecordModel } from "../../../../../common"; +import type { + VerifactuRecordCreationAttributes, + VerifactuRecordModel, +} from "../../../../../common"; export class SequelizeIssuedInvoiceV2VerifactuDomainMapper extends SequelizeDomainMapper< VerifactuRecordModel, @@ -38,7 +41,11 @@ export class SequelizeIssuedInvoiceV2VerifactuDomainMapper extends SequelizeDoma } const recordId = extractOrPushError(UniqueID.create(source.id), "id", errors); - const estado = extractOrPushError(VerifactuRecordEstado.create(source.estado), "estado", errors); + const estado = extractOrPushError( + VerifactuRecordEstado.create(source.estado), + "estado", + errors + ); const qr = extractOrPushError( maybeFromNullableResult(source.qr, (value) => Result.ok(String(value))), "qr", diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-issued-invoice-v2-recipient-summary.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-issued-invoice-v2-recipient-summary.mapper.ts index e5e9b92b..7d36b36d 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-issued-invoice-v2-recipient-summary.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-issued-invoice-v2-recipient-summary.mapper.ts @@ -31,15 +31,31 @@ export class SequelizeIssuedInvoiceV2RecipientSummaryMapper extends SequelizeQue attributes: Partial; }; - const customerName = extractOrPushError(Name.create(raw.customer_name!), "customer_name", errors); - const customerTin = extractOrPushError(TINNumber.create(raw.customer_tin!), "customer_tin", errors); - const customerStreet = extractOrPushError(Street.create(raw.customer_street!), "customer_street", errors); + const customerName = extractOrPushError( + Name.create(raw.customer_name!), + "customer_name", + errors + ); + const customerTin = extractOrPushError( + TINNumber.create(raw.customer_tin!), + "customer_tin", + errors + ); + const customerStreet = extractOrPushError( + Street.create(raw.customer_street!), + "customer_street", + errors + ); const customerStreet2 = extractOrPushError( maybeFromNullableResult(raw.customer_street2, (value) => Street.create(value)), "customer_street2", errors ); - const customerCity = extractOrPushError(City.create(raw.customer_city!), "customer_city", errors); + const customerCity = extractOrPushError( + City.create(raw.customer_city!), + "customer_city", + errors + ); const customerProvince = extractOrPushError( maybeFromNullableResult(raw.customer_province, (value) => Province.create(value)), "customer_province", diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-verifactu-record-summary.mapper.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-verifactu-record-summary.mapper.ts index aa9f7bdc..9dfd5f24 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-verifactu-record-summary.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/mappers/summary/sequelize-verifactu-record-summary.mapper.ts @@ -18,7 +18,7 @@ export class SequelizeVerifactuRecordSummaryMapper extends SequelizeQueryMapper< > { public mapToReadModel( raw: VerifactuRecordModel, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const errors: ValidationErrorDetail[] = []; diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/repositories/issued-invoice-v2.repository.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/repositories/issued-invoice-v2.repository.ts index e1c6dc67..7b5ba6b8 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/repositories/issued-invoice-v2.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/persistence/sequelize/repositories/issued-invoice-v2.repository.ts @@ -22,8 +22,8 @@ import { } from "sequelize"; import { - IssuedInvoiceNumberAlreadyExistsError, type IIssuedInvoiceRepository, + IssuedInvoiceNumberAlreadyExistsError, type IssuedInvoiceSummary, } from "../../../../../application"; import type { IssuedInvoice } from "../../../../../domain"; @@ -55,7 +55,9 @@ export class SequelizeIssuedInvoiceRepositoryV2 async create(invoice: IssuedInvoice, transaction?: Transaction): Promise> { try { const dtoResult = this.domainMapper.mapToPersistence(invoice); - if (dtoResult.isFailure) return Result.fail(dtoResult.error); + if (dtoResult.isFailure) { + return Result.fail(dtoResult.error); + } const dto = dtoResult.data; const { id, items, taxes, verifactu, ...createPayload } = dto; await IssuedInvoiceModel.create({ ...createPayload, id }, { transaction }); @@ -107,7 +109,11 @@ export class SequelizeIssuedInvoiceRepositoryV2 options: FindOptions> = {} ): Promise> { try { - const normalizedOrder = Array.isArray(options.order) ? options.order : options.order ? [options.order] : []; + const normalizedOrder = Array.isArray(options.order) + ? options.order + : options.order + ? [options.order] + : []; const normalizedInclude = Array.isArray(options.include) ? options.include : options.include @@ -127,7 +133,12 @@ export class SequelizeIssuedInvoiceRepositoryV2 ], include: [ ...normalizedInclude, - { model: VerifactuRecordModel, as: "verifactu", required: false, attributes: ["id", "estado", "url", "uuid", "qr"] }, + { + model: VerifactuRecordModel, + as: "verifactu", + required: false, + attributes: ["id", "estado", "url", "uuid", "qr"], + }, { model: IssuedInvoiceItemModel, as: "items", required: false }, { model: IssuedInvoiceTaxModel, as: "taxes", required: false }, ], @@ -227,7 +238,10 @@ export class SequelizeIssuedInvoiceRepositoryV2 }, ]; - const order: OrderItem[] = [...normalizeOrder(criteriaQuery.order), ...normalizeOrder(options.order)]; + const order: OrderItem[] = [ + ...normalizeOrder(criteriaQuery.order), + ...normalizeOrder(options.order), + ]; const findQuery: FindOptions> = { ...options, ...criteriaQuery, diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/di/index.ts b/modules/customer-invoices/src/api/infrastructure/proformas/di/index.ts index ddec7d63..d9b73762 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/di/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/di/index.ts @@ -1,6 +1,6 @@ -export * from "./proforma-public-services"; +export * from "./proforma-number-generator-v2.di"; export * from "./proforma-persistence.di"; export * from "./proforma-persistence-mappers-v2.di"; -export * from "./proforma-number-generator-v2.di"; +export * from "./proforma-public-services"; export * from "./proforma-repositories-v2.di"; export * from "./proformas.di"; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/di/proforma-persistence-mappers-v2.di.ts b/modules/customer-invoices/src/api/infrastructure/proformas/di/proforma-persistence-mappers-v2.di.ts index 1da241a8..a8d11839 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/di/proforma-persistence-mappers-v2.di.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/di/proforma-persistence-mappers-v2.di.ts @@ -1,7 +1,4 @@ -import { - SequelizeProformaV2DomainMapper, - SequelizeProformaV2SummaryMapper, -} from "../persistence"; +import { SequelizeProformaV2DomainMapper, SequelizeProformaV2SummaryMapper } from "../persistence"; export interface IProformaPersistenceMappersV2 { domainMapper: SequelizeProformaV2DomainMapper; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/di/proformas.di.test.ts b/modules/customer-invoices/src/api/infrastructure/proformas/di/proformas.di.test.ts index b2105ddb..113e8d33 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/di/proformas.di.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/di/proformas.di.test.ts @@ -12,21 +12,37 @@ test("buildProformasDependencies expone capabilities recurrentes neutrales y con transactionContext: {}, audit: { recordMutation: async () => ({ isFailure: false, isSuccess: true, data: undefined }) }, config: { - paths: { fastReportBin: "/tmp/fastreport", templates: "/tmp/templates", signedDocuments: "/tmp/documents" }, + paths: { + fastReportBin: "/tmp/fastreport", + templates: "/tmp/templates", + signedDocuments: "/tmp/documents", + }, signingService: { url: "http://localhost", method: "POST", timeoutMs: { maxRetries: 0 } }, }, getService: (name: string) => { - if (name.startsWith("catalogs:")) return { finder }; + if (name.startsWith("catalogs:")) { + return { finder }; + } return {}; }, } as never); const recurring = dependencies.recurringProformas; for (const capability of [ - recurring.list, recurring.detail, recurring.occurrences, - recurring.create, recurring.update, recurring.activationReadiness, - recurring.activate, recurring.pause, recurring.resume, recurring.complete, - recurring.archive, recurring.duplicate, recurring.delete, recurring.fromProforma, + recurring.list, + recurring.detail, + recurring.occurrences, + recurring.create, + recurring.update, + recurring.activationReadiness, + recurring.activate, + recurring.pause, + recurring.resume, + recurring.complete, + recurring.archive, + recurring.duplicate, + recurring.delete, + recurring.fromProforma, recurring.occurrenceProcessor.process, ]) { assert.equal(typeof capability, "function"); diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas-api-error-mapper.ts b/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas-api-error-mapper.ts index 2ec335fd..346910e8 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas-api-error-mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/express/proformas-api-error-mapper.ts @@ -7,19 +7,19 @@ import { } from "@erp/core/api"; import { - CompanyReportProfileNotFoundError, + type CompanyReportProfileNotFoundError, InvoiceSeriesConcurrencyError, InvoiceSeriesInactiveError, InvoiceSeriesLockRequiredError, InvoiceSeriesNotFoundError, InvoiceSeriesTransactionRequiredError, - IssuedInvoiceNumberAlreadyExistsError, - ProformaTaxesUnavailableError, - ProformaNotReadyForIssuanceError, + type IssuedInvoiceNumberAlreadyExistsError, + type ProformaNotReadyForIssuanceError, + type ProformaTaxesUnavailableError, isCompanyReportProfileNotFoundError, isIssuedInvoiceNumberAlreadyExistsError, - isProformaTaxesUnavailableError, isProformaNotReadyForIssuanceError, + isProformaTaxesUnavailableError, } from "../../../application"; import { type CustomerInvoiceIdAlreadyExistsError, @@ -37,9 +37,9 @@ import { isInvalidInvoiceSeriesPaddingLengthError, isInvalidProformaTransitionError, isProformaCannotBeArchivedError, - isProformaCannotBeUnarchivedError, isProformaCannotBeConvertedToInvoiceError, isProformaCannotBeDeletedError, + isProformaCannotBeUnarchivedError, isProformaItemMismatch, } from "../../../domain"; @@ -111,10 +111,7 @@ const proformaConversionRule: ErrorToApiRule = { const proformaCannotBeDeletedRule: ErrorToApiRule = { priority: 120, matches: (e) => isProformaCannotBeDeletedError(e), - build: (e) => - new ConflictApiError( - (e as Error).message || "Proforma cannot be deleted." - ), + build: (e) => new ConflictApiError((e as Error).message || "Proforma cannot be deleted."), }; const proformaCannotBeArchivedRule: ErrorToApiRule = { @@ -153,8 +150,7 @@ const invalidInvoiceSeriesNextNumberRule: ErrorToApiRule = { matches: (e) => isInvalidInvoiceSeriesNextNumberError(e), build: (e) => new ValidationApiError( - (e as InvalidInvoiceSeriesNextNumberError).message || - "Invoice series next number is invalid." + (e as InvalidInvoiceSeriesNextNumberError).message || "Invoice series next number is invalid." ), }; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-domain.mapper.ts index c4940091..786a2378 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-domain.mapper.ts @@ -37,7 +37,7 @@ export class SequelizeProformaV2DomainMapper extends SequelizeDomainMapper< private _recipientMapper: SequelizeProformaV2RecipientDomainMapper; private _taxesMapper: SequelizeProformaV2TaxesDomainMapper; - constructor(params?: MapperParamsType) { + constructor(_params?: MapperParamsType) { super(); this._itemsMapper = new SequelizeProformaV2ItemDomainMapper(); this._recipientMapper = new SequelizeProformaV2RecipientDomainMapper(); @@ -281,7 +281,7 @@ export class SequelizeProformaV2DomainMapper extends SequelizeDomainMapper< errors.push({ path: "taxes", message: taxesResult.error.message }); } - const recipient = this._recipientMapper.mapToPersistence(source.recipient); + const _recipient = this._recipientMapper.mapToPersistence(source.recipient); if (errors.length > 0) { return Result.fail( diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-recipient-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-recipient-domain.mapper.ts index 493f4b2f..66302a55 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-recipient-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-recipient-domain.mapper.ts @@ -95,7 +95,7 @@ export class SequelizeProformaV2RecipientDomainMapper { return Result.ok(Maybe.some(createResult.data)); } - mapToPersistence(source: Maybe) { + mapToPersistence(_source: Maybe) { return Result.ok(); /*if (source.isNone()) { return { diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-taxes-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-taxes-domain.mapper.ts index 454b3a39..3c2e4d73 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-taxes-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/domain/sequelize-proforma-v2-taxes-domain.mapper.ts @@ -10,7 +10,10 @@ export class SequelizeProformaV2TaxesDomainMapper extends SequelizeDomainMapper< ProformaTaxCreationAttributes, IProformaTaxTotals > { - public mapToDomain(_source: ProformaTaxModel, _params?: MapperParamsType): Result { + public mapToDomain( + _source: ProformaTaxModel, + _params?: MapperParamsType + ): Result { throw new Error("Proforma tax groups are derived from detail lines"); } diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/index.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/index.ts index 967aab81..8967392f 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/index.ts @@ -1,2 +1,2 @@ -export * from "./sequelize-proforma-v2-summary.mapper"; export * from "./sequelize-proforma-v2-recipient-summary.mapper"; +export * from "./sequelize-proforma-v2-summary.mapper"; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/sequelize-proforma-v2-recipient-summary.mapper.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/sequelize-proforma-v2-recipient-summary.mapper.ts index e27ed389..8ec83420 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/sequelize-proforma-v2-recipient-summary.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/mappers/summary/sequelize-proforma-v2-recipient-summary.mapper.ts @@ -35,14 +35,14 @@ export class SequelizeProformaV2RecipientSummaryMapper extends SequelizeQueryMap * En proforma -> datos de "current_customer" */ - const _name = raw.current_customer!.name; - const _tin = raw.current_customer!.tin; - const _street = raw.current_customer!.street; - const _street2 = raw.current_customer!.street2; - const _city = raw.current_customer!.city; - const _postal_code = raw.current_customer!.postal_code; - const _province = raw.current_customer!.province; - const _country = raw.current_customer!.country; + const _name = raw.current_customer?.name; + const _tin = raw.current_customer?.tin; + const _street = raw.current_customer?.street; + const _street2 = raw.current_customer?.street2; + const _city = raw.current_customer?.city; + const _postal_code = raw.current_customer?.postal_code; + const _province = raw.current_customer?.province; + const _country = raw.current_customer?.country; const customerName = extractOrPushError(Name.create(_name), "customer_name", errors); const customerTin = extractOrPushError( diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.test.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.test.ts index fbe0ceb3..10655d71 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.test.ts @@ -1,5 +1,5 @@ -import test from "node:test"; import assert from "node:assert/strict"; +import test from "node:test"; import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; import { Op, Sequelize, type WhereOptions } from "sequelize"; @@ -35,7 +35,10 @@ function buildWhereSql(quickSearch: string): string { test("genera fallback LIKE para referencia documental con guion", () => { const sql = buildWhereSql("PF-0066"); - assert.match(sql, /MATCH\(`ProformaModel`\.`proforma_reference`, `ProformaModel`\.`reference`, `ProformaModel`\.`description`\)/); + assert.match( + sql, + /MATCH\(`ProformaModel`\.`proforma_reference`, `ProformaModel`\.`reference`, `ProformaModel`\.`description`\)/ + ); assert.match(sql, /\+PF\* \+0066\*/); assert.match(sql, /`proforma_reference` LIKE '%PF-0066%'/); assert.match(sql, /`proforma_number` LIKE '%PF-0066%'/); diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.ts index 3458c743..73c3e6cb 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-quick-search.ts @@ -1,11 +1,6 @@ -import type { ProformaModel } from "../../../../common"; +import { type InferAttributes, Op, Sequelize, type WhereOptions } from "sequelize"; -import { - Op, - Sequelize, - type InferAttributes, - type WhereOptions, -} from "sequelize"; +import type { ProformaModel } from "../../../../common"; type BuildProformaQuickSearchWhereParams = { quickSearch?: string; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-v2.repository.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-v2.repository.ts index 3de40640..d9de605b 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-v2.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/repositories/proforma-v2.repository.ts @@ -64,7 +64,9 @@ export class SequelizeProformaRepositoryV2 async create(proforma: Proforma, transaction?: Transaction): Promise> { try { const dtoResult = this.domainMapper.mapToPersistence(proforma); - if (dtoResult.isFailure) return Result.fail(dtoResult.error); + if (dtoResult.isFailure) { + return Result.fail(dtoResult.error); + } const dto = dtoResult.data; const { id, items, taxes, ...createPayload } = dto; await ProformaModel.create({ ...createPayload, id }, { transaction }); @@ -85,7 +87,9 @@ export class SequelizeProformaRepositoryV2 async update(proforma: Proforma, transaction: Transaction): Promise> { try { const dtoResult = this.domainMapper.mapToPersistence(proforma); - if (dtoResult.isFailure) return Result.fail(dtoResult.error); + if (dtoResult.isFailure) { + return Result.fail(dtoResult.error); + } const dto = dtoResult.data; const { id, items, taxes, ...updatePayload } = dto; await ProformaModel.update(updatePayload, { diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/services/sequelize-proforma-v2-number-generator.service.ts b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/services/sequelize-proforma-v2-number-generator.service.ts index e6429b1e..71336431 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/services/sequelize-proforma-v2-number-generator.service.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/persistence/sequelize/services/sequelize-proforma-v2-number-generator.service.ts @@ -10,31 +10,31 @@ import type { InvoiceSerie } from "../../../../../domain"; * Adapter V2 para delegar la numeracion de proformas en document-series. */ export class SequelizeProformaV2NumberGenerator implements IProformaNumberGenerator { - public constructor( - private readonly documentSeriesServices: DocumentSeriesPublicServicesType - ) {} + public constructor(private readonly documentSeriesServices: DocumentSeriesPublicServicesType) {} public async getNextForCompany( companyId: UniqueID, proformaSeriesCode: Maybe, transaction: Transaction ) { - return this.documentSeriesServices.assignNextNumber({ - companyId: companyId.toString(), - documentType: "proforma", - seriesCode: proformaSeriesCode.getOrUndefined()?.toPrimitive() ?? null, - transaction, - }).then((assignmentResult) => { - if (assignmentResult.isFailure) { - return Result.fail(assignmentResult.error); - } + return this.documentSeriesServices + .assignNextNumber({ + companyId: companyId.toString(), + documentType: "proforma", + seriesCode: proformaSeriesCode.getOrUndefined()?.toPrimitive() ?? null, + transaction, + }) + .then((assignmentResult) => { + if (assignmentResult.isFailure) { + return Result.fail(assignmentResult.error); + } - return Result.ok({ - documentSeriesId: assignmentResult.data.series_id, - documentSeriesCode: assignmentResult.data.series_code, - number: assignmentResult.data.number, - reference: assignmentResult.data.reference, + return Result.ok({ + documentSeriesId: assignmentResult.data.series_id, + documentSeriesCode: assignmentResult.data.series_code, + number: assignmentResult.data.number, + reference: assignmentResult.data.reference, + }); }); - }); } } diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.test.ts index 8619192e..20cb9a4b 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.test.ts @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; @@ -50,12 +51,12 @@ test("from-proforma delegates the minimal input and returns the created recurren const recurringProformaId = UniqueID.generateNewID(); const fromProformaCalls: Array> = []; const httpResponse = response(); - const controller = new CreateRecurringProformaFromProformaController( - (async (input: Record) => { - fromProformaCalls.push(input); - return Result.ok({ id: recurringProformaId, status: "draft" }); - }) as never - ); + const controller = new CreateRecurringProformaFromProformaController((async ( + input: Record + ) => { + fromProformaCalls.push(input); + return Result.ok({ id: recurringProformaId, status: "draft" }); + }) as never); await controller.execute( request(companyId, proformaId, { @@ -96,12 +97,12 @@ test("from-proforma preserves a null end date", async () => { const proformaId = UniqueID.generateNewID(); const fromProformaCalls: Array> = []; const httpResponse = response(); - const controller = new CreateRecurringProformaFromProformaController( - (async (input: Record) => { - fromProformaCalls.push(input); - return Result.ok({ id: UniqueID.generateNewID(), status: "draft" }); - }) as never - ); + const controller = new CreateRecurringProformaFromProformaController((async ( + input: Record + ) => { + fromProformaCalls.push(input); + return Result.ok({ id: UniqueID.generateNewID(), status: "draft" }); + }) as never); await controller.execute( request(companyId, proformaId, { @@ -113,10 +114,7 @@ test("from-proforma preserves a null end date", async () => { ); assert.equal(fromProformaCalls.length, 1); - assert.equal( - (fromProformaCalls[0]?.schedule as { endDate: Date | null }).endDate, - null - ); + assert.equal((fromProformaCalls[0]?.schedule as { endDate: Date | null }).endDate, null); }); test("from-proforma rejects an invalid date before invoking Application", async () => { @@ -124,12 +122,12 @@ test("from-proforma rejects an invalid date before invoking Application", async const proformaId = UniqueID.generateNewID(); const fromProformaCalls: Array> = []; const httpResponse = response(); - const controller = new CreateRecurringProformaFromProformaController( - (async (input: Record) => { - fromProformaCalls.push(input); - return Result.ok({ id: UniqueID.generateNewID(), status: "draft" }); - }) as never - ); + const controller = new CreateRecurringProformaFromProformaController((async ( + input: Record + ) => { + fromProformaCalls.push(input); + return Result.ok({ id: UniqueID.generateNewID(), status: "draft" }); + }) as never); await controller.execute( request(companyId, proformaId, { @@ -149,12 +147,12 @@ test("from-proforma propagates an Application failure", async () => { const proformaId = UniqueID.generateNewID(); const fromProformaCalls: Array> = []; const httpResponse = response(); - const controller = new CreateRecurringProformaFromProformaController( - (async (input: Record) => { - fromProformaCalls.push(input); - return Result.fail(new Error("Proforma not found")); - }) as never - ); + const controller = new CreateRecurringProformaFromProformaController((async ( + input: Record + ) => { + fromProformaCalls.push(input); + return Result.fail(new Error("Proforma not found")); + }) as never); await controller.execute( request(companyId, proformaId, { diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.ts index bfeb69ef..f002d729 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma-from-proforma.controller.ts @@ -16,12 +16,18 @@ export class CreateRecurringProformaFromProformaController extends ExpressContro const companyId = this.getTenantId(); const proformaId = UniqueID.create(this.req.params.proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (proformaId.isFailure) return this.invalidInputError(proformaId.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (proformaId.isFailure) { + return this.invalidInputError(proformaId.error.message); + } const body = this.req.body as CreateRecurringProformaFromProformaRequestDTO; const startDate = parseDateOnly(body.schedule.startDate); - if (startDate.isFailure) return this.invalidInputError(startDate.error.message); + if (startDate.isFailure) { + return this.invalidInputError(startDate.error.message); + } const endDate = body.schedule.endDate; const parsedEndDate = typeof endDate === "string" ? parseDateOnly(endDate) : undefined; diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma.controller.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma.controller.test.ts index c363eb88..97e3d3d8 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma.controller.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/create-recurring-proforma.controller.test.ts @@ -1,18 +1,26 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { CreateRecurringProformaRequestSchema } from "../../../../../common"; import { RecurringProforma } from "../../../../../domain"; + import { CreateRecurringProformaController } from "./recurring-proformas-read.controllers"; test("CreateRecurringProformaRequestSchema acepta un draft con solo name", () => { - assert.deepEqual(CreateRecurringProformaRequestSchema.parse({ name: "Mantenimiento mensual" }), { name: "Mantenimiento mensual" }); + assert.deepEqual(CreateRecurringProformaRequestSchema.parse({ name: "Mantenimiento mensual" }), { + name: "Mantenimiento mensual", + }); }); test("CreateRecurringProformaRequestSchema conserva null explícito en description", () => { - assert.equal(CreateRecurringProformaRequestSchema.parse({ name: "Mantenimiento mensual", description: null }).description, null); + assert.equal( + CreateRecurringProformaRequestSchema.parse({ name: "Mantenimiento mensual", description: null }) + .description, + null + ); }); test("CreateRecurringProformaController responde con el detail read model tras crear", async () => { diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/index.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/index.ts index d14c508a..f8f26464 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/index.ts @@ -1,2 +1,2 @@ -export * from "./recurring-proformas-read.controllers"; export * from "./create-recurring-proforma-from-proforma.controller"; +export * from "./recurring-proformas-read.controllers"; diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proforma-transitions.controller.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proforma-transitions.controller.test.ts index 72a8b3d4..15266694 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proforma-transitions.controller.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proforma-transitions.controller.test.ts @@ -1,14 +1,18 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { ArchiveRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { DuplicateRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { DeleteRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { ActivateRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { PauseRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { ResumeRecurringProformaController } from "./recurring-proformas-read.controllers"; -import { CompleteRecurringProformaController } from "./recurring-proformas-read.controllers"; + +import { + ActivateRecurringProformaController, + ArchiveRecurringProformaController, + CompleteRecurringProformaController, + DeleteRecurringProformaController, + DuplicateRecurringProformaController, + PauseRecurringProformaController, + ResumeRecurringProformaController, +} from "./recurring-proformas-read.controllers"; test("archive success delegates once and returns HTTP success", async () => { const companyId = UniqueID.generateNewID(); @@ -16,12 +20,34 @@ test("archive success delegates once and returns HTTP success", async () => { const archiveCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new ArchiveRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - archiveCalls.push(input); - return Result.ok({ id: recurringProformaId.toPrimitive(), status: "archived" }); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/archive", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new ArchiveRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + archiveCalls.push(input); + return Result.ok({ id: recurringProformaId.toPrimitive(), status: "archived" }); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/archive", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(archiveCalls.length, 1); assert.equal(archiveCalls[0]?.companyId.equals(companyId), true); @@ -36,12 +62,34 @@ test("archive failure delegates once and returns the mapped functional error", a const archiveCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new ArchiveRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - archiveCalls.push(input); - return Result.fail(new Error("Invalid recurring proforma transition")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/archive", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new ArchiveRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + archiveCalls.push(input); + return Result.fail(new Error("Invalid recurring proforma transition")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/archive", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(archiveCalls.length, 1); assert.equal(archiveCalls[0]?.companyId.equals(companyId), true); @@ -57,12 +105,34 @@ test("duplicate success delegates only the command identifiers and returns HTTP const duplicateCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new DuplicateRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - duplicateCalls.push(input); - return Result.ok({ id: duplicatedId.toPrimitive(), status: "draft" }); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/duplicate", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new DuplicateRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + duplicateCalls.push(input); + return Result.ok({ id: duplicatedId.toPrimitive(), status: "draft" }); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/duplicate", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(duplicateCalls.length, 1); assert.equal(duplicateCalls[0]?.companyId.equals(companyId), true); @@ -78,12 +148,34 @@ test("duplicate failure delegates once and returns the mapped functional error", const duplicateCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new DuplicateRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - duplicateCalls.push(input); - return Result.fail(new Error("Recurring proforma not found")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/duplicate", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new DuplicateRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + duplicateCalls.push(input); + return Result.fail(new Error("Recurring proforma not found")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/duplicate", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(duplicateCalls.length, 1); assert.equal(duplicateCalls[0]?.companyId.equals(companyId), true); @@ -98,12 +190,34 @@ test("delete success delegates once and returns HTTP success", async () => { const deleteCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new DeleteRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - deleteCalls.push(input); - return Result.ok(true); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x", method: "DELETE", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new DeleteRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + deleteCalls.push(input); + return Result.ok(true); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x", + method: "DELETE", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(deleteCalls.length, 1); assert.equal(deleteCalls[0]?.companyId.equals(companyId), true); @@ -118,12 +232,34 @@ test("delete failure delegates once and returns the mapped functional error", as const deleteCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new DeleteRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - deleteCalls.push(input); - return Result.fail(new Error("Only draft recurring proformas can be deleted")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x", method: "DELETE", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new DeleteRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + deleteCalls.push(input); + return Result.fail(new Error("Only draft recurring proformas can be deleted")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x", + method: "DELETE", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(deleteCalls.length, 1); assert.equal(deleteCalls[0]?.companyId.equals(companyId), true); @@ -138,12 +274,34 @@ test("activate failure delegates once and returns the mapped functional error", const activateCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new ActivateRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - activateCalls.push(input); - return Result.fail(new Error("Recurring proforma cannot be activated: missing_customer")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/activate", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new ActivateRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + activateCalls.push(input); + return Result.fail(new Error("Recurring proforma cannot be activated: missing_customer")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/activate", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(activateCalls.length, 1); assert.equal(activateCalls[0]?.companyId.equals(companyId), true); @@ -158,12 +316,34 @@ test("pause failure delegates once and returns the mapped functional error", asy const pauseCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new PauseRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - pauseCalls.push(input); - return Result.fail(new Error("Invalid recurring proforma transition")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/pause", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new PauseRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + pauseCalls.push(input); + return Result.fail(new Error("Invalid recurring proforma transition")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/pause", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(pauseCalls.length, 1); assert.equal(pauseCalls[0]?.companyId.equals(companyId), true); @@ -178,12 +358,34 @@ test("resume failure delegates once and returns the mapped functional error", as const resumeCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new ResumeRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - resumeCalls.push(input); - return Result.fail(new Error("Invalid recurring proforma transition")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/resume", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new ResumeRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + resumeCalls.push(input); + return Result.fail(new Error("Invalid recurring proforma transition")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/resume", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(resumeCalls.length, 1); assert.equal(resumeCalls[0]?.companyId.equals(companyId), true); @@ -198,12 +400,34 @@ test("complete failure for failed occurrences delegates once and returns the map const completeCalls: Array<{ companyId: typeof companyId; id: typeof recurringProformaId }> = []; let statusCode: number | undefined; let body: unknown; - const controller = new CompleteRecurringProformaController(async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { - completeCalls.push(input); - return Result.fail(new Error("Recurring proforma has unresolved failed occurrences")); - }); - const req = { user: { companyId }, params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, query: {}, originalUrl: "/recurring-proformas/x/complete", method: "POST", protocol: "http", get: () => undefined }; - const res = { status: (value: number) => { statusCode = value; return res; }, json: (value: unknown) => { body = value; return res; }, sendStatus: () => res, type: () => res, setHeader: () => undefined }; + const controller = new CompleteRecurringProformaController( + async (input: { companyId: typeof companyId; id: typeof recurringProformaId }) => { + completeCalls.push(input); + return Result.fail(new Error("Recurring proforma has unresolved failed occurrences")); + } + ); + const req = { + user: { companyId }, + params: { recurring_proforma_id: recurringProformaId.toPrimitive() }, + query: {}, + originalUrl: "/recurring-proformas/x/complete", + method: "POST", + protocol: "http", + get: () => undefined, + }; + const res = { + status: (value: number) => { + statusCode = value; + return res; + }, + json: (value: unknown) => { + body = value; + return res; + }, + sendStatus: () => res, + type: () => res, + setHeader: () => undefined, + }; await controller.execute(req as never, res as never, () => undefined); assert.equal(completeCalls.length, 1); assert.equal(completeCalls[0]?.companyId.equals(companyId), true); diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proformas-read.controllers.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proformas-read.controllers.ts index 25b49665..0f309b38 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proformas-read.controllers.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/recurring-proformas-read.controllers.ts @@ -35,7 +35,7 @@ const schedule = ( monthly_rule: value.monthlyRule, }; -const listPresenter = (page: any) => ({ +const _listPresenter = (page: any) => ({ page: page.page, per_page: page.perPage, total_items: page.totalItems, @@ -76,7 +76,9 @@ export class ListRecurringProformasController extends RecurringProformaControlle } protected async executeImpl() { const companyId = this.company(); - if (!companyId) return this.forbiddenError("Tenant ID not found"); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const result = await this.list({ companyId, ...this.req.query, scope: this.scope }); return result.match( (data: any) => this.ok(this.presenter.present(data)), @@ -94,8 +96,12 @@ export class GetRecurringProformaController extends RecurringProformaController protected async executeImpl() { const companyId = this.company(); const parsed = UniqueID.create(this.req.params.recurring_proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (parsed.isFailure) return this.invalidInputError(parsed.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (parsed.isFailure) { + return this.invalidInputError(parsed.error.message); + } const result = await this.detail({ companyId, id: parsed.data }); return result.match( (data: any) => this.ok(this.presenter.present(data)), @@ -113,8 +119,12 @@ export class ListRecurringProformaOccurrencesController extends RecurringProform protected async executeImpl() { const companyId = this.company(); const parsed = UniqueID.create(this.req.params.recurring_proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (parsed.isFailure) return this.invalidInputError(parsed.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (parsed.isFailure) { + return this.invalidInputError(parsed.error.message); + } const result = await this.occurrences({ companyId, recurringProformaId: parsed.data, @@ -136,7 +146,9 @@ export class CreateRecurringProformaController extends RecurringProformaControll } protected async executeImpl() { const companyId = this.company(); - if (!companyId) return this.forbiddenError("Tenant ID not found"); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const created = await this.create({ companyId, ...this.req.body }); return created.match( async (data: any) => { @@ -161,8 +173,12 @@ export class UpdateRecurringProformaController extends RecurringProformaControll protected async executeImpl() { const companyId = this.company(); const parsed = UniqueID.create(this.req.params.recurring_proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (parsed.isFailure) return this.invalidInputError(parsed.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (parsed.isFailure) { + return this.invalidInputError(parsed.error.message); + } const body = this.req.body; const paymentTermId = body.payment_term_id === undefined @@ -200,8 +216,12 @@ export class GetRecurringProformaActivationReadinessController extends Recurring protected async executeImpl() { const companyId = this.company(); const parsed = UniqueID.create(this.req.params.recurring_proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (parsed.isFailure) return this.invalidInputError(parsed.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (parsed.isFailure) { + return this.invalidInputError(parsed.error.message); + } const result = await this.readiness({ companyId, id: parsed.data }); return result.match( (data: any) => this.ok(this.presenter.present(data)), @@ -216,8 +236,12 @@ class Transition extends RecurringProformaController { protected async executeImpl() { const companyId = this.company(); const parsed = UniqueID.create(this.req.params.recurring_proforma_id); - if (!companyId) return this.forbiddenError("Tenant ID not found"); - if (parsed.isFailure) return this.invalidInputError(parsed.error.message); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + if (parsed.isFailure) { + return this.invalidInputError(parsed.error.message); + } const result = await this.command({ companyId, id: parsed.data }); return result.match( (data: any) => this.ok(data), diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/update-recurring-proforma.controller.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/update-recurring-proforma.controller.test.ts index ffcff189..abd34ab9 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/update-recurring-proforma.controller.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/express/controllers/update-recurring-proforma.controller.test.ts @@ -1,14 +1,28 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; + import { UpdateRecurringProformaRequestSchema } from "../../../../../common"; + import { UpdateRecurringProformaController } from "./recurring-proformas-read.controllers"; test("PATCH conserva omitted, null y value para payment_term_id", () => { - assert.equal(UpdateRecurringProformaRequestSchema.parse({ description: "Nueva" }).payment_term_id, undefined); - assert.equal(UpdateRecurringProformaRequestSchema.parse({ payment_term_id: null }).payment_term_id, null); - assert.equal(UpdateRecurringProformaRequestSchema.parse({ payment_term_id: "01900000-0000-7000-8000-000000000901" }).payment_term_id, "01900000-0000-7000-8000-000000000901"); + assert.equal( + UpdateRecurringProformaRequestSchema.parse({ description: "Nueva" }).payment_term_id, + undefined + ); + assert.equal( + UpdateRecurringProformaRequestSchema.parse({ payment_term_id: null }).payment_term_id, + null + ); + assert.equal( + UpdateRecurringProformaRequestSchema.parse({ + payment_term_id: "01900000-0000-7000-8000-000000000901", + }).payment_term_id, + "01900000-0000-7000-8000-000000000901" + ); }); test("PATCH conserva omitted, null y value para language", () => { @@ -20,7 +34,16 @@ test("PATCH conserva omitted, null y value para language", () => { test("UpdateRecurringProformaController relee detail tras actualizar antes de responder", async () => { const companyId = UniqueID.generateNewID(); const recurringProformaId = UniqueID.generateNewID(); - const updateCalls: Array<{ companyId: UniqueID; id: UniqueID; patch: { description?: string | null; paymentTermId?: UniqueID | null; language?: string | null; proformaSeries?: string | null } }> = []; + const updateCalls: Array<{ + companyId: UniqueID; + id: UniqueID; + patch: { + description?: string | null; + paymentTermId?: UniqueID | null; + language?: string | null; + proformaSeries?: string | null; + }; + }> = []; const detailCalls: Array<{ companyId: UniqueID; id: UniqueID }> = []; let statusCode: number | undefined; let body: any; diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/index.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/index.ts index a8ef57d4..ee8971b6 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/index.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/index.ts @@ -1,2 +1,2 @@ -export * from "./persistence"; export * from "./express/recurring-proformas.routes"; +export * from "./persistence"; diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.test.ts index d7da5665..60f31e5b 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.test.ts @@ -18,7 +18,9 @@ test("SequelizeRecurringProformaDomainMapper persiste draft mínimo con schedule name: "Cuota", }) ); - const persistence = required(new SequelizeRecurringProformaDomainMapper().mapToPersistence(source)); + const persistence = required( + new SequelizeRecurringProformaDomainMapper().mapToPersistence(source) + ); assert.equal(persistence.schedule_start_date, null); assert.equal(persistence.next_occurrence_date, null); @@ -29,13 +31,18 @@ test("SequelizeRecurringProformaDomainMapper persiste draft mínimo con schedule test("SequelizeRecurringProformaDomainMapper preserva description entre dominio y persistencia", () => { const source = required( - RecurringProforma.create({ - companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000301")), - name: "Cuota", - description: Maybe.some("Servicio comercial"), - }, required(UniqueID.create("01900000-0000-7000-8000-000000000302"))) + RecurringProforma.create( + { + companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000301")), + name: "Cuota", + description: Maybe.some("Servicio comercial"), + }, + required(UniqueID.create("01900000-0000-7000-8000-000000000302")) + ) + ); + const persistence = required( + new SequelizeRecurringProformaDomainMapper().mapToPersistence(source) ); - const persistence = required(new SequelizeRecurringProformaDomainMapper().mapToPersistence(source)); const rehydrated = required( new SequelizeRecurringProformaDomainMapper().mapToDomain({ ...persistence, @@ -108,25 +115,37 @@ test("SequelizeRecurringProformaDomainMapper rechaza descuento global parcialmen const mapper = new SequelizeRecurringProformaDomainMapper(); assert.equal( - mapper.mapToDomain({ ...base, global_discount_percentage_value: 15, global_discount_percentage_scale: null } as unknown as RecurringProformaModel).isFailure, + mapper.mapToDomain({ + ...base, + global_discount_percentage_value: 15, + global_discount_percentage_scale: null, + } as unknown as RecurringProformaModel).isFailure, true ); assert.equal( - mapper.mapToDomain({ ...base, global_discount_percentage_value: null, global_discount_percentage_scale: 2 } as unknown as RecurringProformaModel).isFailure, + mapper.mapToDomain({ + ...base, + global_discount_percentage_value: null, + global_discount_percentage_scale: 2, + } as unknown as RecurringProformaModel).isFailure, true ); }); test("SequelizeRecurringProformaDomainMapper conserva preferencias de series por código", () => { - const source = required(RecurringProforma.create({ - companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000301")), - name: "Cuota", - proformaSeries: Maybe.some(required(InvoiceSerie.create("PRO"))), - issuedInvoiceSeries: Maybe.some(required(InvoiceSerie.create("INV"))), - })); + const source = required( + RecurringProforma.create({ + companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000301")), + name: "Cuota", + proformaSeries: Maybe.some(required(InvoiceSerie.create("PRO"))), + issuedInvoiceSeries: Maybe.some(required(InvoiceSerie.create("INV"))), + }) + ); const mapper = new SequelizeRecurringProformaDomainMapper(); const persistence = required(mapper.mapToPersistence(source)); - const rehydrated = required(mapper.mapToDomain({ ...persistence, items: [] } as unknown as RecurringProformaModel)); + const rehydrated = required( + mapper.mapToDomain({ ...persistence, items: [] } as unknown as RecurringProformaModel) + ); assert.equal(persistence.proforma_series_code, "PRO"); assert.equal(persistence.issued_invoice_series_code, "INV"); diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.ts index 28df924f..4c8b70f3 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-domain.mapper.ts @@ -29,10 +29,14 @@ const orDefault = (value: Maybe, map: (item: T) => U, fallback: U): U = export class SequelizeRecurringProformaDomainMapper { public mapToDomain(raw: RecurringProformaModel): Result { const scheduleResult = this.mapSchedule(raw); - if (scheduleResult.isFailure) return Result.fail(scheduleResult.error); + if (scheduleResult.isFailure) { + return Result.fail(scheduleResult.error); + } const itemsResult = this.mapItems(raw.items ?? []); - if (itemsResult.isFailure) return Result.fail(itemsResult.error); + if (itemsResult.isFailure) { + return Result.fail(itemsResult.error); + } const companyId = UniqueID.create(raw.company_id); const id = UniqueID.create(raw.id); @@ -69,7 +73,9 @@ export class SequelizeRecurringProformaDomainMapper { globalDiscount, ]; const failure = results.find((result) => result.isFailure); - if (failure?.isFailure) return Result.fail(failure.error); + if (failure?.isFailure) { + return Result.fail(failure.error); + } const props: RecurringProformaInternalProps = { companyId: companyId.data, @@ -180,10 +186,12 @@ export class SequelizeRecurringProformaDomainMapper { const hasRequired = [raw.schedule_start_date, raw.schedule_unit, raw.schedule_interval].filter( (value) => value !== null ).length; - if (hasRequired === 0 && raw.schedule_end_date === null && raw.schedule_monthly_rule === null) + if (hasRequired === 0 && raw.schedule_end_date === null && raw.schedule_monthly_rule === null) { return Result.ok(Maybe.none()); - if (hasRequired !== 3) + } + if (hasRequired !== 3) { return Result.fail(new Error("Recurring proforma schedule columns are partially populated")); + } const result = RecurrenceSchedule.create({ startDate: toDate(raw.schedule_start_date!), endDate: raw.schedule_end_date === null ? null : toDate(raw.schedule_end_date), @@ -233,7 +241,9 @@ export class SequelizeRecurringProformaDomainMapper { }).map(Maybe.some); const results = [id, description, quantity, amount, discount]; const failure = results.find((result) => result.isFailure); - if (failure?.isFailure) return Result.fail(failure.error); + if (failure?.isFailure) { + return Result.fail(failure.error); + } mapped.push( RecurringProformaItem.rehydrate( { @@ -254,7 +264,9 @@ export class SequelizeRecurringProformaDomainMapper { } private mapOptionalId(value: string | null): Result, Error> { - if (value === null) return Result.ok(Maybe.none()); + if (value === null) { + return Result.ok(Maybe.none()); + } return UniqueID.create(value).map(Maybe.some); } @@ -267,12 +279,16 @@ export class SequelizeRecurringProformaDomainMapper { } private mapOptionalLanguage(value: string | null): Result, Error> { - if (value === null) return Result.ok(Maybe.none()); + if (value === null) { + return Result.ok(Maybe.none()); + } return LanguageCode.create(value).map(Maybe.some); } private mapOptionalCurrency(value: string | null): Result, Error> { - if (value === null) return Result.ok(Maybe.none()); + if (value === null) { + return Result.ok(Maybe.none()); + } return CurrencyCode.create(value).map(Maybe.some); } } diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.test.ts index 3280df15..2a8821e3 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.test.ts @@ -23,5 +23,8 @@ test("SequelizeRecurringProformaOccurrenceDomainMapper rechaza occurrence genera updated_at: new Date("2027-01-15T08:00:00.000Z"), } as unknown as RecurringProformaOccurrenceModel; - assert.equal(new SequelizeRecurringProformaOccurrenceDomainMapper().mapToDomain(raw).isFailure, true); + assert.equal( + new SequelizeRecurringProformaOccurrenceDomainMapper().mapToDomain(raw).isFailure, + true + ); }); diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.ts index e485ec1f..b860c859 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/mappers/recurring-proforma-occurrence-domain.mapper.ts @@ -23,7 +23,9 @@ export class SequelizeRecurringProformaOccurrenceDomainMapper { : UniqueID.create(raw.proforma_id).map(Maybe.some), ]; const failure = ids.find((result) => result.isFailure); - if (failure?.isFailure) return Result.fail(failure.error); + if (failure?.isFailure) { + return Result.fail(failure.error); + } return RecurringProformaOccurrence.rehydrate( { diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.test.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.test.ts index 576359b7..624de04a 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.test.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.test.ts @@ -12,15 +12,16 @@ const required = (result: { data: T }): T => result.data; const createOccurrence = () => required( - RecurringProformaOccurrence.create({ - companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000601")), - recurringProformaId: required( - UniqueID.create("01900000-0000-7000-8000-000000000602") - ), - scheduledDate: new Date("2027-01-15T00:00:00.000Z"), - createdAt: new Date("2027-01-01T00:00:00.000Z"), - updatedAt: new Date("2027-01-01T00:00:00.000Z"), - }, required(UniqueID.create("01900000-0000-7000-8000-000000000603"))) + RecurringProformaOccurrence.create( + { + companyId: required(UniqueID.create("01900000-0000-7000-8000-000000000601")), + recurringProformaId: required(UniqueID.create("01900000-0000-7000-8000-000000000602")), + scheduledDate: new Date("2027-01-15T00:00:00.000Z"), + createdAt: new Date("2027-01-01T00:00:00.000Z"), + updatedAt: new Date("2027-01-01T00:00:00.000Z"), + }, + required(UniqueID.create("01900000-0000-7000-8000-000000000603")) + ) ); test("SequelizeRecurringProformaOccurrenceRepository guarda el payload de occurrence", async () => { @@ -142,7 +143,8 @@ test("hasFailedOccurrences limita status failed por recurrence y company", async try { assert.equal( - (await repository.hasFailedOccurrences(occurrence.recurringProformaId, occurrence.companyId)).data, + (await repository.hasFailedOccurrences(occurrence.recurringProformaId, occurrence.companyId)) + .data, true ); assert.deepEqual(receivedWhere, { @@ -165,10 +167,21 @@ test("listByRecurringProformaInCompany limita tenant, parent y orden determinist return { count: 0, rows: [] } as never; }) as typeof RecurringProformaOccurrenceModel.findAndCountAll; try { - const result = await repository.listByRecurringProformaInCompany({ companyId: occurrence.companyId, recurringProformaId: occurrence.recurringProformaId, page: 3, limit: 5 }); + const result = await repository.listByRecurringProformaInCompany({ + companyId: occurrence.companyId, + recurringProformaId: occurrence.recurringProformaId, + page: 3, + limit: 5, + }); assert.equal(result.isSuccess, true); - assert.deepEqual(options?.where, { company_id: occurrence.companyId.toPrimitive(), recurring_proforma_id: occurrence.recurringProformaId.toPrimitive() }); - assert.deepEqual(options?.order, [["scheduled_date", "DESC"], ["id", "DESC"]]); + assert.deepEqual(options?.where, { + company_id: occurrence.companyId.toPrimitive(), + recurring_proforma_id: occurrence.recurringProformaId.toPrimitive(), + }); + assert.deepEqual(options?.order, [ + ["scheduled_date", "DESC"], + ["id", "DESC"], + ]); assert.equal(options?.limit, 5); assert.equal(options?.offset, 10); } finally { diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.ts index 9589071c..c982642e 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma-occurrence.repository.ts @@ -24,7 +24,9 @@ export class SequelizeRecurringProformaOccurrenceRepository ): Promise> { try { const persistence = this.mapper.mapToPersistence(occurrence); - if (persistence.isFailure) return Result.fail(persistence.error); + if (persistence.isFailure) { + return Result.fail(persistence.error); + } await RecurringProformaOccurrenceModel.upsert(persistence.data as any, { transaction: transaction as Transaction | undefined, }); @@ -70,7 +72,9 @@ export class SequelizeRecurringProformaOccurrenceRepository }, transaction: transaction as Transaction | undefined, }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } const occurrence = this.mapper.mapToDomain(row); return occurrence.isFailure ? Result.fail(occurrence.error) @@ -146,9 +150,13 @@ export class SequelizeRecurringProformaOccurrenceRepository const items: RecurringProformaOccurrenceReadModel[] = []; for (const row of rows) { const id = UniqueID.create(row.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } const proformaId = row.proforma_id ? UniqueID.create(row.proforma_id) : null; - if (proformaId?.isFailure) return Result.fail(proformaId.error); + if (proformaId?.isFailure) { + return Result.fail(proformaId.error); + } items.push({ id: id.data, scheduledDate: this.toDateOnly(row.scheduled_date), diff --git a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma.repository.ts b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma.repository.ts index f6ae97fc..90e309f8 100644 --- a/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/recurring-proformas/persistence/sequelize/repositories/sequelize-recurring-proforma.repository.ts @@ -1,7 +1,7 @@ import { EntityNotFoundError, translateSequelizeError } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { literal, Op, type Transaction } from "sequelize"; +import { Op, type Transaction, literal } from "sequelize"; import type { DueRecurringProforma, @@ -28,8 +28,12 @@ export class SequelizeRecurringProformaRepository implements IRecurringProformaR try { const persistence = this.mapper.mapToPersistence(recurringProforma); const items = this.mapper.mapItemsToPersistence(recurringProforma); - if (persistence.isFailure) return Result.fail(persistence.error); - if (items.isFailure) return Result.fail(items.error); + if (persistence.isFailure) { + return Result.fail(persistence.error); + } + if (items.isFailure) { + return Result.fail(items.error); + } const sqlTransaction = transaction as Transaction | undefined; await RecurringProformaModel.upsert(persistence.data as any, { transaction: sqlTransaction }); @@ -60,8 +64,9 @@ export class SequelizeRecurringProformaRepository implements IRecurringProformaR order: [[{ model: RecurringProformaItemModel, as: "items" }, "position", "ASC"]], transaction: transaction as Transaction | undefined, }); - if (!row) + if (!row) { return Result.fail(new EntityNotFoundError("RecurringProforma", "id", id.toString())); + } return this.mapper.mapToDomain(row); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -81,8 +86,9 @@ export class SequelizeRecurringProformaRepository implements IRecurringProformaR transaction: transaction as Transaction, lock: true, }); - if (!row) + if (!row) { return Result.fail(new EntityNotFoundError("RecurringProforma", "id", id.toString())); + } return this.mapper.mapToDomain(row); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -145,8 +151,12 @@ export class SequelizeRecurringProformaRepository implements IRecurringProformaR for (const row of rows) { const recurringProformaId = UniqueID.create(row.id); const companyId = UniqueID.create(row.company_id); - if (recurringProformaId.isFailure) return Result.fail(recurringProformaId.error); - if (companyId.isFailure) return Result.fail(companyId.error); + if (recurringProformaId.isFailure) { + return Result.fail(recurringProformaId.error); + } + if (companyId.isFailure) { + return Result.fail(companyId.error); + } due.push({ recurringProformaId: recurringProformaId.data, companyId: companyId.data, @@ -212,9 +222,13 @@ export class SequelizeRecurringProformaRepository implements IRecurringProformaR const items: RecurringProformaListItemReadModel[] = []; for (const row of rows) { const id = UniqueID.create(row.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } const customerId = row.customer_id ? UniqueID.create(row.customer_id) : null; - if (customerId?.isFailure) return Result.fail(customerId.error); + if (customerId?.isFailure) { + return Result.fail(customerId.error); + } const hasSchedule = row.schedule_start_date !== null; items.push({ id: id.data, diff --git a/modules/customer-invoices/src/api/infrastructure/renderers/issued-invoice.renderer.json.ts b/modules/customer-invoices/src/api/infrastructure/renderers/issued-invoice.renderer.json.ts index 5408b48e..f2b4f2b4 100644 --- a/modules/customer-invoices/src/api/infrastructure/renderers/issued-invoice.renderer.json.ts +++ b/modules/customer-invoices/src/api/infrastructure/renderers/issued-invoice.renderer.json.ts @@ -7,7 +7,7 @@ import type { import type { Proforma } from "../../domain"; export class IssuedInvoiceReportJSONRenderer extends Renderer> { - toOutput(invoice: Proforma, params: { companySlug: string }): Record { + toOutput(invoice: Proforma, _params: { companySlug: string }): Record { const dtoRenderer = this.presenterRegistry.getRenderer({ resource: "issued-invoice", projection: "FULL", diff --git a/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts index e9b318a2..37647eaa 100644 --- a/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/create-proforma.request.dto.ts @@ -7,7 +7,11 @@ import { } from "@erp/core"; import { z } from "zod/v4"; -import { ItemPositionSchema, ProformaTaxConfigPatchSchema, TaxCombinationCodeSchema } from "../../shared"; +import { + ItemPositionSchema, + ProformaTaxConfigPatchSchema, + TaxCombinationCodeSchema, +} from "../../shared"; export const CreateProformaItemRequestSchema = z.object({ position: ItemPositionSchema, 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 index 1b13b580..30a6068f 100644 --- 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 @@ -1,5 +1,5 @@ import { CriteriaSchema } from "@erp/core"; -import { z } from "zod/v4"; +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/recurring-proformas/index.ts b/modules/customer-invoices/src/common/dto/request/recurring-proformas/index.ts index b9178f24..ce0329ed 100644 --- a/modules/customer-invoices/src/common/dto/request/recurring-proformas/index.ts +++ b/modules/customer-invoices/src/common/dto/request/recurring-proformas/index.ts @@ -1,2 +1,2 @@ -export * from "./recurring-proformas-read.request.dto"; export * from "./create-recurring-proforma-from-proforma.request.dto"; +export * from "./recurring-proformas-read.request.dto"; diff --git a/modules/customer-invoices/src/common/dto/response/settings/company-verifactu-profile.response.dto.ts b/modules/customer-invoices/src/common/dto/response/settings/company-verifactu-profile.response.dto.ts index 66e1ee99..dfcdb312 100644 --- a/modules/customer-invoices/src/common/dto/response/settings/company-verifactu-profile.response.dto.ts +++ b/modules/customer-invoices/src/common/dto/response/settings/company-verifactu-profile.response.dto.ts @@ -1,9 +1,6 @@ import { z } from "zod/v4"; -import { - AutoRenewModeSchema, - CompanyVerifactuProfileStatusSchema, -} from "../../request/settings"; +import { AutoRenewModeSchema, CompanyVerifactuProfileStatusSchema } from "../../request/settings"; export const CompanyVerifactuProfileResponseSchema = z.object({ profile_status: CompanyVerifactuProfileStatusSchema, diff --git a/modules/customer-invoices/src/common/locales/en.json b/modules/customer-invoices/src/common/locales/en.json index 2895a7c9..c864a51b 100644 --- a/modules/customer-invoices/src/common/locales/en.json +++ b/modules/customer-invoices/src/common/locales/en.json @@ -1410,4 +1410,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/customer-invoices/src/common/locales/es.json b/modules/customer-invoices/src/common/locales/es.json index a6f8f187..155771d1 100644 --- a/modules/customer-invoices/src/common/locales/es.json +++ b/modules/customer-invoices/src/common/locales/es.json @@ -1402,4 +1402,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/controllers/use-issued-invoice-detail-page-controller.ts b/modules/customer-invoices/src/web/issued-invoices/detail/controllers/use-issued-invoice-detail-page-controller.ts index 874a042f..535e04b7 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/controllers/use-issued-invoice-detail-page-controller.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/controllers/use-issued-invoice-detail-page-controller.ts @@ -112,7 +112,7 @@ export const useIssuedInvoiceDetailPageController = () => { onClick: navigateBack, }, }), - [issuedInvoiceQuery.isFetching, issuedInvoiceQuery.isLoading, navigateBack, t] + [issuedInvoiceQuery.isFetching, issuedInvoiceQuery.isLoading, navigateBack, t, handleRetryPage] ); const state: IssuedInvoiceDetailPageState = { diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts b/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts index 9a5cb09e..ee93ee23 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/entities/issued-invoice-detail-view-models.entity.ts @@ -1,7 +1,6 @@ import type { PageErrorAction, PageErrorStateVariant } from "@erp/core/components"; -import type { IssuedInvoiceStatus } from "../../shared"; -import type { VerifactuRecordStatus } from "../../shared"; +import type { IssuedInvoiceStatus, VerifactuRecordStatus } from "../../shared"; export interface IssuedInvoiceDetailPageState { isInitialLoading: boolean; diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-header-actions.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-header-actions.tsx index f7db163f..f1dfbee3 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-header-actions.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-header-actions.tsx @@ -14,7 +14,10 @@ import { } from "lucide-react"; import { useTranslation } from "../../../../i18n"; -import type { IssuedInvoiceDetailActionKey, IssuedInvoiceDetailActionsViewModel } from "../../entities"; +import type { + IssuedInvoiceDetailActionKey, + IssuedInvoiceDetailActionsViewModel, +} from "../../entities"; type IssuedInvoiceDetailHeaderActionsProps = { actions: IssuedInvoiceDetailActionsViewModel; @@ -35,7 +38,6 @@ export const IssuedInvoiceDetailHeaderActions = ({ return DownloadIcon; case "print-pdf": return PrinterIcon; - case "open-pdf": default: return ExternalLinkIcon; } diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-inspector.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-inspector.tsx index 357090ee..6a4c6179 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-inspector.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-detail-inspector.tsx @@ -22,10 +22,7 @@ export const IssuedInvoiceDetailInspector = ({ description={t("pages.issued_invoices.detail.description")} title={t("pages.issued_invoices.detail.regions.details")} > - + ); }; diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-general-panel.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-general-panel.tsx index 81aab08e..32f22007 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-general-panel.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/blocks/issued-invoice-general-panel.tsx @@ -71,7 +71,11 @@ export const IssuedInvoiceGeneralPanel = ({ />
diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx index 8996ccc8..a6b06e7e 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/pages/issued-invoice-detail-page.test.tsx @@ -224,11 +224,7 @@ test("usa PageErrorState para el error inicial bloqueante", async () => { mock.module("@erp/core/components", { namedExports: { - PageErrorState: (props: { - title?: string; - variant?: string; - reference?: string; - }) => { + PageErrorState: (props: { title?: string; variant?: string; reference?: string }) => { capturedErrorProps = props; return null; }, diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-document-section.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-document-section.tsx index 5cb5d1c9..65e4cd73 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-document-section.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-document-section.tsx @@ -44,7 +44,10 @@ export const IssuedInvoiceDocumentSection = ({ /> { +export const IssuedInvoiceFiscalSection = ({ fiscal }: IssuedInvoiceFiscalSectionProps) => { const { t } = useTranslation(); return ( diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-items-section.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-items-section.tsx index f05e3fc9..e5e1636f 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-items-section.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-items-section.tsx @@ -34,7 +34,9 @@ export const IssuedInvoiceItemsSection = ({ items }: IssuedInvoiceItemsSectionPr /> { +export const IssuedInvoiceRecipientSection = ({ customer }: IssuedInvoiceRecipientSectionProps) => { const { t } = useTranslation(); return ( diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-summary-section.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-summary-section.tsx index cb5eded3..69a0503c 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-summary-section.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-summary-section.tsx @@ -49,7 +49,9 @@ export const IssuedInvoiceSummarySection = ({ /> ) : null} diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-verifactu-section.tsx b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-verifactu-section.tsx index 0083fb75..3587551c 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-verifactu-section.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/detail/ui/sections/issued-invoice-verifactu-section.tsx @@ -19,11 +19,7 @@ export const IssuedInvoiceVerifactuSection = ({ description={t("pages.issued_invoices.detail.section_descriptions.verifactu")} title={t("pages.issued_invoices.detail.sections.verifactu")} > - {!verifactu.hasRecord ? ( -

- {t("pages.issued_invoices.detail.verifactu.empty")} -

- ) : ( + {verifactu.hasRecord ? (
) : null}
+ ) : ( +

+ {t("pages.issued_invoices.detail.verifactu.empty")} +

)} ); diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts index fa4bd6c4..a070a553 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.test.ts @@ -2,6 +2,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import type { IssuedInvoice } from "../../shared"; + import { buildIssuedInvoiceDetailGeneralViewModel, buildIssuedInvoiceDetailPageErrorViewModel, diff --git a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts index 2bb949bf..60eb4629 100644 --- a/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts +++ b/modules/customer-invoices/src/web/issued-invoices/detail/utils/build-issued-invoice-detail-view-models.ts @@ -8,8 +8,8 @@ import type { IssuedInvoiceDetailContentViewModel, IssuedInvoiceDetailCustomerViewModel, IssuedInvoiceDetailDocumentViewModel, - IssuedInvoiceDetailFiscalFieldViewModel, IssuedInvoiceDetailFiscalBreakdownItemViewModel, + IssuedInvoiceDetailFiscalFieldViewModel, IssuedInvoiceDetailFiscalViewModel, IssuedInvoiceDetailGeneralViewModel, IssuedInvoiceDetailHeaderViewModel, @@ -117,8 +117,16 @@ const buildItemTaxesLabel = ( const recPercentageLabel = buildPercentageLabel(item.recPercentage); const retentionPercentageLabel = buildPercentageLabel(item.retentionPercentage); const parts = [ - item.taxCode ? (taxPercentageLabel ? `${item.taxCode} · ${taxPercentageLabel}` : item.taxCode) : null, - item.recCode ? (recPercentageLabel ? `${item.recCode} · ${recPercentageLabel}` : item.recCode) : null, + item.taxCode + ? taxPercentageLabel + ? `${item.taxCode} · ${taxPercentageLabel}` + : item.taxCode + : null, + item.recCode + ? recPercentageLabel + ? `${item.recCode} · ${recPercentageLabel}` + : item.recCode + : null, item.retentionCode ? retentionPercentageLabel ? `${item.retentionCode} · ${retentionPercentageLabel}` @@ -409,9 +417,9 @@ export const buildIssuedInvoiceDetailGeneralViewModel = ( discountLabel: item.itemDiscountAmount > 0 ? `${formatMoney(issuedInvoice, item.itemDiscountAmount)}${ - item.itemDiscountPercentage !== null - ? ` (${NumberHelper.formatNumber(item.itemDiscountPercentage, 2)}%)` - : "" + item.itemDiscountPercentage === null + ? "" + : ` (${NumberHelper.formatNumber(item.itemDiscountPercentage, 2)}%)` }` : null, taxableAmountLabel: formatMoney(issuedInvoice, item.taxableAmount), diff --git a/modules/customer-invoices/src/web/issued-invoices/download-pdf/controller/use-download-invoice-pdf.controller.ts b/modules/customer-invoices/src/web/issued-invoices/download-pdf/controller/use-download-invoice-pdf.controller.ts index a14d2fb1..acfd71b7 100644 --- a/modules/customer-invoices/src/web/issued-invoices/download-pdf/controller/use-download-invoice-pdf.controller.ts +++ b/modules/customer-invoices/src/web/issued-invoices/download-pdf/controller/use-download-invoice-pdf.controller.ts @@ -15,7 +15,9 @@ export function useDownloadInvoicePDFController() { // Efecto: cuando hay blob + pending, disparamos la descarga React.useEffect(() => { - if (!(pending && data)) return; + if (!(pending && data)) { + return; + } const blob = data; const url = URL.createObjectURL(blob); diff --git a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts b/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts index 0bef7f2d..d5b7250a 100644 --- a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts +++ b/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts @@ -21,7 +21,9 @@ export function useDownloadInvoicePDFQuery( return useQuery({ queryKey: ISSUED_INVOICE_QUERY_KEY(invoiceId ?? "unknown"), queryFn: async (context) => { - if (!invoiceId) throw new Error("invoiceId is required"); + if (!invoiceId) { + throw new Error("invoiceId is required"); + } const { signal } = context; return await downloadInvoicePDFApi(dataSource, invoiceId, { diff --git a/modules/customer-invoices/src/web/issued-invoices/list/controllers/use-issued-invoice-list.controller.ts b/modules/customer-invoices/src/web/issued-invoices/list/controllers/use-issued-invoice-list.controller.ts index 4ed63f9f..7defceb3 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/controllers/use-issued-invoice-list.controller.ts +++ b/modules/customer-invoices/src/web/issued-invoices/list/controllers/use-issued-invoice-list.controller.ts @@ -1,9 +1,6 @@ import { useDataSource, useDebounce } from "@erp/core/hooks"; import { INITIAL_PAGE_INDEX } from "@repo/rdx-criteria"; -import { - type DataTableSort, - useDataTablePreferences, -} from "@repo/rdx-ui/components"; +import { type DataTableSort, useDataTablePreferences } from "@repo/rdx-ui/components"; import { NumberHelper } from "@repo/rdx-utils"; import { useQueries } from "@tanstack/react-query"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -15,16 +12,14 @@ import type { ListIssuedInvoicesByCriteriaParams, VerifactuRecordStatus, } from "../../shared"; -import { - EMPTY_ISSUED_INVOICE_STATUS_COUNTS, - getListIssuedInvoicesByCriteria, -} from "../../shared"; +import { EMPTY_ISSUED_INVOICE_STATUS_COUNTS, getListIssuedInvoicesByCriteria } from "../../shared"; import { useIssuedInvoiceListQuery } from "../../shared/"; + import { DEFAULT_ISSUED_INVOICE_API_SORT_FIELD, DEFAULT_ISSUED_INVOICE_LIST_SORT, - type IssuedInvoiceListSortField, ISSUED_INVOICE_LIST_SORT_FIELDS, + type IssuedInvoiceListSortField, isIssuedInvoiceListSortField, isSortDirection, } from "./issued-invoice-list-sorting"; @@ -166,86 +161,107 @@ export const useIssuedInvoiceListController = () => { return counts; }, [query.data?.totalItems, statusCountQueries]); - const setStatusFilterValue = useCallback((value: string) => { - const nextValue = (value || "all") as VerifactuRecordListStatusFilter; - if (verifactuStatusFilter === nextValue) return; - - setVerifactuStatusFilter(nextValue); - setSearchParams((prev) => { - const params = new URLSearchParams(prev); - params.set("page", String(INITIAL_PAGE_INDEX + 1)); - - if (nextValue === "all") { - params.delete("status"); - } else { - params.set("status", nextValue); + const setStatusFilterValue = useCallback( + (value: string) => { + const nextValue = (value || "all") as VerifactuRecordListStatusFilter; + if (verifactuStatusFilter === nextValue) { + return; } - return params; - }); - }, [setSearchParams, verifactuStatusFilter]); + setVerifactuStatusFilter(nextValue); + setSearchParams((prev) => { + const params = new URLSearchParams(prev); + params.set("page", String(INITIAL_PAGE_INDEX + 1)); - const setSearchValue = useCallback((value: string) => { - const nextValue = value.trim().replace(/\s+/g, " "); - if (search === nextValue) return; + if (nextValue === "all") { + params.delete("status"); + } else { + params.set("status", nextValue); + } - setSearch(nextValue); - setSearchParams((prev) => { - const params = new URLSearchParams(prev); - params.set("page", String(INITIAL_PAGE_INDEX + 1)); + return params; + }); + }, + [setSearchParams, verifactuStatusFilter] + ); - if (nextValue) { - params.set("q", nextValue); - } else { - params.delete("q"); + const setSearchValue = useCallback( + (value: string) => { + const nextValue = value.trim().replace(/\s+/g, " "); + if (search === nextValue) { + return; } - return params; - }); - }, [search, setSearchParams]); + setSearch(nextValue); + setSearchParams((prev) => { + const params = new URLSearchParams(prev); + params.set("page", String(INITIAL_PAGE_INDEX + 1)); - const setPageIndexValue = useCallback((newPageIndex: number) => { - const nextPage = newPageIndex + 1; + if (nextValue) { + params.set("q", nextValue); + } else { + params.delete("q"); + } - setSearchParams((prev) => { - const params = new URLSearchParams(prev); - params.set("page", String(nextPage)); - return params; - }); - }, [setSearchParams]); + return params; + }); + }, + [search, setSearchParams] + ); - const setPageSizeValue = useCallback((value: number) => { - if (pageSize === value) return; + const setPageIndexValue = useCallback( + (newPageIndex: number) => { + const nextPage = newPageIndex + 1; - setSearchParams((prev) => { - const params = new URLSearchParams(prev); - params.set("page", String(INITIAL_PAGE_INDEX + 1)); - params.set("pageSize", String(value)); - return params; - }); + setSearchParams((prev) => { + const params = new URLSearchParams(prev); + params.set("page", String(nextPage)); + return params; + }); + }, + [setSearchParams] + ); - setPageSizePreference(value); - }, [pageSize, setPageSizePreference, setSearchParams]); + const setPageSizeValue = useCallback( + (value: number) => { + if (pageSize === value) { + return; + } - const setSortValue = useCallback((nextSort: DataTableSort) => { - if (!isIssuedInvoiceListSortField(nextSort.field)) { - return; - } + setSearchParams((prev) => { + const params = new URLSearchParams(prev); + params.set("page", String(INITIAL_PAGE_INDEX + 1)); + params.set("pageSize", String(value)); + return params; + }); - if (sort.field === nextSort.field && sort.direction === nextSort.direction) { - return; - } + setPageSizePreference(value); + }, + [pageSize, setPageSizePreference, setSearchParams] + ); - setSearchParams((prev) => { - const params = new URLSearchParams(prev); - params.set("sortField", nextSort.field); - params.set("sortDirection", nextSort.direction); - params.set("page", String(INITIAL_PAGE_INDEX + 1)); - return params; - }); + const setSortValue = useCallback( + (nextSort: DataTableSort) => { + if (!isIssuedInvoiceListSortField(nextSort.field)) { + return; + } - setSortPreference(nextSort); - }, [setSearchParams, setSortPreference, sort.direction, sort.field]); + if (sort.field === nextSort.field && sort.direction === nextSort.direction) { + return; + } + + setSearchParams((prev) => { + const params = new URLSearchParams(prev); + params.set("sortField", nextSort.field); + params.set("sortDirection", nextSort.direction); + params.set("page", String(INITIAL_PAGE_INDEX + 1)); + return params; + }); + + setSortPreference(nextSort); + }, + [setSearchParams, setSortPreference, sort.direction, sort.field] + ); return { queryData: query.data, diff --git a/modules/customer-invoices/src/web/issued-invoices/list/ui/blocks/issued-invoices-grid/use-issued-invoices-grid-columns.test.tsx b/modules/customer-invoices/src/web/issued-invoices/list/ui/blocks/issued-invoices-grid/use-issued-invoices-grid-columns.test.tsx index 8377af35..31d89052 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/ui/blocks/issued-invoices-grid/use-issued-invoices-grid-columns.test.tsx +++ b/modules/customer-invoices/src/web/issued-invoices/list/ui/blocks/issued-invoices-grid/use-issued-invoices-grid-columns.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { useIssuedInvoicesGridColumns } from "./use-issued-invoices-grid-columns"; diff --git a/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.test.ts b/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.test.ts index 32ef17e3..73cc3114 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.test.ts @@ -14,8 +14,7 @@ const translate = (key: string) => "No tienes acceso al listado de facturas", "pages.issued_invoices.list.errors.page.forbidden.description": "No dispones de permisos para consultar este listado.", - "pages.issued_invoices.list.errors.page.network.title": - "No se pudo conectar con el servidor", + "pages.issued_invoices.list.errors.page.network.title": "No se pudo conectar con el servidor", "pages.issued_invoices.list.errors.page.network.description": "Comprueba tu conexión e inténtalo de nuevo.", "pages.issued_invoices.list.errors.page.timeout.title": "La carga está tardando demasiado", @@ -33,8 +32,7 @@ const translate = (key: string) => "No se pudo cargar el listado de facturas", "pages.issued_invoices.list.errors.page.unknown.description": "Ha ocurrido un error inesperado al cargar el listado.", - "pages.issued_invoices.list.errors.refresh.title": - "No se pudo actualizar el listado", + "pages.issued_invoices.list.errors.refresh.title": "No se pudo actualizar el listado", "pages.issued_invoices.list.errors.refresh.message": "Se muestran los últimos datos disponibles mientras vuelves a intentarlo.", })[key] ?? key; diff --git a/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.ts b/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.ts index e894e9eb..84a61b64 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.ts +++ b/modules/customer-invoices/src/web/issued-invoices/list/utils/build-issued-invoice-list-page-error-view-model.ts @@ -5,13 +5,9 @@ import type { IssuedInvoiceListRefreshErrorViewModel, } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; -const buildVariant = ( - error: unknown -): IssuedInvoiceListPageErrorViewModel["variant"] | null => { +const buildVariant = (error: unknown): IssuedInvoiceListPageErrorViewModel["variant"] | null => { if (!isApiClientError(error)) { return "unknown"; } diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts index d26570c6..0393cbd7 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/get-issued-invoice-by-id.adapter.test.ts @@ -4,6 +4,7 @@ import test from "node:test"; import { MoneyDTOHelper, PercentageDTOHelper, QuantityDTOHelper } from "@erp/core"; import type { GetIssuedInvoiceByIdResponseDTO } from "../../../../common"; + import { GetIssuedInvoiceByIdAdapter } from "./get-issued-invoice-by-id.adapter"; const buildDto = (): GetIssuedInvoiceByIdResponseDTO => ({ diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/index.ts index 3756deb4..bd4ad08e 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/index.ts @@ -1,2 +1,2 @@ -export * from "./list-issued-invoice.adapter"; export * from "./get-issued-invoice-by-id.adapter"; +export * from "./list-issued-invoice.adapter"; diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.test.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.test.ts index 3e715c26..d194e70b 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.test.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.test.ts @@ -4,6 +4,7 @@ import test from "node:test"; import { MoneyDTOHelper } from "@erp/core"; import type { ListIssuedInvoicesResponseDTO } from "../../../../common"; + import { ListIssuedInvoicesAdapter } from "./list-issued-invoice.adapter"; const buildDto = (): ListIssuedInvoicesResponseDTO => ({ diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.ts b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.ts index 98dcb727..b9c1ca1f 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/adapters/list-issued-invoice.adapter.ts @@ -2,8 +2,8 @@ import { MoneyDTOHelper } from "@erp/core"; import { MoneyHelper } from "@repo/rdx-utils"; import type { ListIssuedInvoicesResponseDTO } from "../../../../common"; -import { EMPTY_ISSUED_INVOICE_STATUS_COUNTS } from "../entities"; import type { IssuedInvoiceList, IssuedInvoiceListRow, IssuedInvoiceStatus } from "../entities"; +import { EMPTY_ISSUED_INVOICE_STATUS_COUNTS } from "../entities"; import type { VerifactuRecordStatus } from "../entities/verifactu-record-status.entity"; export const ListIssuedInvoicesAdapter = { diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/api/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/api/index.ts index 2fd85e2a..286c9bd6 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/api/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/api/index.ts @@ -1,2 +1,2 @@ -export * from "./list-issued-invoices-by-criteria.api"; export * from "./get-issued-invoice-by-id.api"; +export * from "./list-issued-invoices-by-criteria.api"; diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/entities/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/entities/index.ts index 4e5f9f8e..b7be577a 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/entities/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/entities/index.ts @@ -1,7 +1,7 @@ +export * from "./issued-invoice.entity"; export * from "./issued-invoice-list.entity"; export * from "./issued-invoice-list-row.entity"; -export * from "./issued-invoice.entity"; export * from "./issued-invoice-status.entity"; export * from "./issued-invoice-status-counts.entity"; -export * from "./verifactu-record-status.entity"; export * from "./verifactu-record.entity"; +export * from "./verifactu-record-status.entity"; diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts index 22db9323..6d85e9f3 100644 --- a/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts @@ -1,2 +1,2 @@ -export * from "./use-issued-invoice-list-query"; export * from "./use-issued-invoice-get-query"; +export * from "./use-issued-invoice-list-query"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/entities/index.ts b/modules/customer-invoices/src/web/proformas/change-status/entities/index.ts index bfda69a3..6f06a3ff 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/entities/index.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/entities/index.ts @@ -1 +1 @@ -export * from './change-proforma-status-target.entity'; +export * from "./change-proforma-status-target.entity"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx index a69706ba..ab07233d 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx @@ -125,7 +125,7 @@ export const ChangeProformaStatusDialog = ({ const isSingleTarget = targets.length === 1; const currentTarget = isSingleTarget ? targets[0] : null; - const currentStatus = currentTarget?.status ?? null; + const _currentStatus = currentTarget?.status ?? null; const emptyState = (
diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx index 6753d239..3c136255 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/components/timeline-connector.tsx @@ -39,8 +39,12 @@ export function TimelineConnector({ nodeCount, statusColors }: TimelineConnector // Determinar opacidad basada en el tipo de estado const getOpacity = (statusType: string) => { - if (statusType === "past") return 0.3; - if (statusType === "unavailable") return 0.15; + if (statusType === "past") { + return 0.3; + } + if (statusType === "unavailable") { + return 0.15; + } return 1; }; diff --git a/modules/customer-invoices/src/web/proformas/change-status/utils/index.ts b/modules/customer-invoices/src/web/proformas/change-status/utils/index.ts index 569423bf..38e682c9 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/utils/index.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/utils/index.ts @@ -1,2 +1,2 @@ -export * from './get-common-available-proforma-statuses'; -export * from './prepare-change-proforma-status-targets'; +export * from "./get-common-available-proforma-statuses"; +export * from "./prepare-change-proforma-status-targets"; diff --git a/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts b/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts index d26a3bde..2bc1b973 100644 --- a/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts +++ b/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts @@ -90,13 +90,9 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle queryKey: buildCompanySettingsQueryKey(activeCompany?.id), enabled: Boolean(activeCompany?.id), queryFn: async ({ signal }) => { - const dto = await dataSource.getOne( - "companies", - "current", - { - signal, - } - ); + const dto = await dataSource.getOne("companies", "current", { + signal, + }); return { defaultPaymentTermId: dto.default_payment_term_id, @@ -158,8 +154,12 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle }, [proformaSeriesQuery.data]); useEffect(() => { - if (form.getValues("proformaSeriesCode")) return; - if (!defaultProformaSeries) return; + if (form.getValues("proformaSeriesCode")) { + return; + } + if (!defaultProformaSeries) { + return; + } form.setValue("proformaSeriesCode", defaultProformaSeries, { shouldDirty: false, diff --git a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts index f007de8c..5ad9f606 100644 --- a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts @@ -6,6 +6,7 @@ import { useTranslation } from "../../../i18n"; import type { DeleteProformaByIdParams } from "../../shared"; import { useProformaDeleteMutation } from "../../shared"; import type { DeleteProformaTarget } from "../entities"; + type DeleteErrorWithStatus = Error & { status?: number }; interface DeleteProformaDialogState { @@ -31,7 +32,9 @@ export const useDeleteProformaDialogController = () => { const [state, setState] = React.useState(INITIAL_STATE); const openDialog = React.useCallback((targets: DeleteProformaTarget[]) => { - if (targets.length === 0) return; + if (targets.length === 0) { + return; + } setState({ open: true, @@ -40,7 +43,9 @@ export const useDeleteProformaDialogController = () => { }, []); const closeDialog = React.useCallback(() => { - if (deleteMutation.isPending) return; + if (deleteMutation.isPending) { + return; + } setState(INITIAL_STATE); }, [deleteMutation.isPending]); diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx index 51fe187f..bd7bfe48 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx @@ -63,7 +63,9 @@ export const DeleteProformaDialog = ({ return ( { - if (isSubmitting) return; + if (isSubmitting) { + return; + } onOpenChange(nextOpen); }} open={open} @@ -74,9 +76,7 @@ export const DeleteProformaDialog = ({ {description} -

- {t(warningKey)} -

+

{t(warningKey)}

{total > 1 ? (
diff --git a/modules/customer-invoices/src/web/proformas/detail/controllers/index.ts b/modules/customer-invoices/src/web/proformas/detail/controllers/index.ts index b77bf276..8293dc6c 100644 --- a/modules/customer-invoices/src/web/proformas/detail/controllers/index.ts +++ b/modules/customer-invoices/src/web/proformas/detail/controllers/index.ts @@ -1,2 +1,2 @@ -export * from "./use-proforma-detail-page-controller"; export * from "./use-create-recurring-proforma-from-proforma-dialog-controller"; +export * from "./use-proforma-detail-page-controller"; diff --git a/modules/customer-invoices/src/web/proformas/detail/controllers/use-proforma-detail-page-controller.ts b/modules/customer-invoices/src/web/proformas/detail/controllers/use-proforma-detail-page-controller.ts index 7a0179b5..6024a597 100644 --- a/modules/customer-invoices/src/web/proformas/detail/controllers/use-proforma-detail-page-controller.ts +++ b/modules/customer-invoices/src/web/proformas/detail/controllers/use-proforma-detail-page-controller.ts @@ -38,6 +38,7 @@ import { buildProformaDetailHeaderViewModel, buildProformaDetailPageErrorViewModel, } from "../utils"; + import { useCreateRecurringProformaFromProformaDialogController } from "./use-create-recurring-proforma-from-proforma-dialog-controller"; const PROFORMAS_FALLBACK_PATH = "/proformas"; diff --git a/modules/customer-invoices/src/web/proformas/detail/entities/proforma-detail-view-models.entity.ts b/modules/customer-invoices/src/web/proformas/detail/entities/proforma-detail-view-models.entity.ts index 270be7a4..ca17e620 100644 --- a/modules/customer-invoices/src/web/proformas/detail/entities/proforma-detail-view-models.entity.ts +++ b/modules/customer-invoices/src/web/proformas/detail/entities/proforma-detail-view-models.entity.ts @@ -1,4 +1,5 @@ import type { PageErrorAction, PageErrorStateVariant } from "@erp/core/components"; + import type { ProformaStatus } from "../../shared"; export interface ProformaDetailPageState { diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/blocks/index.ts b/modules/customer-invoices/src/web/proformas/detail/ui/blocks/index.ts index b286def7..31d2a80d 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/blocks/index.ts +++ b/modules/customer-invoices/src/web/proformas/detail/ui/blocks/index.ts @@ -1,6 +1,6 @@ -export * from "./proforma-detail-header-actions"; export * from "./proforma-detail-content"; export * from "./proforma-detail-header"; +export * from "./proforma-detail-header-actions"; export * from "./proforma-detail-inspector"; -export * from "./proforma-general-panel"; export * from "./proforma-document-pane"; +export * from "./proforma-general-panel"; diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/blocks/proforma-detail-header-actions.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/blocks/proforma-detail-header-actions.tsx index 64671a4a..943838a1 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/blocks/proforma-detail-header-actions.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/blocks/proforma-detail-header-actions.tsx @@ -9,8 +9,8 @@ import { ArchiveIcon, CopyIcon, DownloadIcon, - FileOutputIcon, ExternalLinkIcon, + FileOutputIcon, MoreHorizontalIcon, PencilIcon, RefreshCwIcon, diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/components/index.ts b/modules/customer-invoices/src/web/proformas/detail/ui/components/index.ts index a0cb85ca..d3329c65 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/components/index.ts +++ b/modules/customer-invoices/src/web/proformas/detail/ui/components/index.ts @@ -1,5 +1,5 @@ -export * from "./proforma-detail-archived-badge"; export * from "./create-recurring-proforma-from-proforma-dialog"; +export * from "./proforma-detail-archived-badge"; export * from "./proforma-detail-field"; export * from "./proforma-detail-header-meta"; export * from "./proforma-detail-section"; diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-field.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-field.tsx index b59a284e..e5978b85 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-field.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-field.tsx @@ -7,11 +7,7 @@ export interface ProformaDetailFieldProps { valueClassName?: string; } -export const ProformaDetailField = ({ - label, - value, - valueClassName, -}: ProformaDetailFieldProps) => { +export const ProformaDetailField = ({ label, value, valueClassName }: ProformaDetailFieldProps) => { return (
{label} diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-header-meta.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-header-meta.tsx index 9b3cdf39..1d738c38 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-header-meta.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/components/proforma-detail-header-meta.tsx @@ -9,10 +9,7 @@ export const ProformaDetailHeaderMeta = ({ customerName, documentReference, }: ProformaDetailHeaderMetaProps) => { - const items = useMemo( - () => [customerName, documentReference], - [customerName, documentReference] - ); + const items = useMemo(() => [customerName, documentReference], [customerName, documentReference]); return (
diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-commercial-section.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-commercial-section.tsx index 8ea53673..402f3f9a 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-commercial-section.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-commercial-section.tsx @@ -6,9 +6,7 @@ type ProformaCommercialSectionProps = { commercial: ProformaDetailCommercialViewModel; }; -export const ProformaCommercialSection = ({ - commercial, -}: ProformaCommercialSectionProps) => { +export const ProformaCommercialSection = ({ commercial }: ProformaCommercialSectionProps) => { const { t } = useTranslation(); return ( diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-content-section.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-content-section.tsx index ac2d1416..6dd8a25d 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-content-section.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-content-section.tsx @@ -6,12 +6,10 @@ type ProformaContentSectionProps = { content: ProformaDetailContentViewModel; }; -export const ProformaContentSection = ({ - content, -}: ProformaContentSectionProps) => { +export const ProformaContentSection = ({ content }: ProformaContentSectionProps) => { const { t } = useTranslation(); - if (!content.description && !content.notes) { + if (!(content.description || content.notes)) { return null; } @@ -37,9 +35,7 @@ export const ProformaContentSection = ({

{t("proformas.detail.fields.notes")}

-

- {content.notes} -

+

{content.notes}

) : null}
diff --git a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-customer-section.tsx b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-customer-section.tsx index 2e8abd04..faab8112 100644 --- a/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-customer-section.tsx +++ b/modules/customer-invoices/src/web/proformas/detail/ui/sections/proforma-customer-section.tsx @@ -33,7 +33,10 @@ export const ProformaCustomerSection = ({ title={t("proformas.detail.sections.customer")} >
- + ): string; -} +export type ProformaDetailTranslate = ( + key: string, + params?: Record +) => string; export interface BuildProformaDetailViewModelsContext { translate: ProformaDetailTranslate; @@ -53,7 +54,9 @@ const formatDate = (value: string | null) => { const buildLanguageLabel = (languageCode: string) => { try { - return new Intl.DisplayNames([languageCode], { type: "language" }).of(languageCode) ?? languageCode; + return ( + new Intl.DisplayNames([languageCode], { type: "language" }).of(languageCode) ?? languageCode + ); } catch { return languageCode; } @@ -91,7 +94,7 @@ const buildTaxLabel = ( translate: ProformaDetailTranslate ): string => { const percentage = - item.ivaPercentage !== null ? `${NumberHelper.formatNumber(item.ivaPercentage, 2)}%` : null; + item.ivaPercentage === null ? null : `${NumberHelper.formatNumber(item.ivaPercentage, 2)}%`; const code = item.ivaCode ?? translate("proformas.detail.fiscal.unknown_tax_code"); return percentage ? `${code} · ${percentage}` : code; @@ -220,28 +223,32 @@ export const buildProformaDetailGeneralViewModel = ( const usesRec = proforma.recAmount > 0 || proforma.taxConfig.usesEquivalenceSurcharge; const usesRetention = proforma.retentionAmount > 0 || proforma.taxConfig.usesRetention; - const breakdown: ProformaDetailFiscalBreakdownItemViewModel[] = proforma.taxes.map((tax, index) => ({ - key: `${tax.ivaCode ?? "tax"}-${index}`, - taxLabel: buildTaxLabel(tax, context.translate), - taxableAmountLabel: formatMoney(proforma, tax.taxableAmount), - taxAmountLabel: formatMoney(proforma, tax.ivaAmount), - recLabel: - tax.recPercentage !== null - ? `${tax.recCode ?? context.translate("proformas.detail.fiscal.unknown_tax_code")} · ${NumberHelper.formatNumber(tax.recPercentage, 2)}%` - : null, - recAmountLabel: tax.recPercentage !== null ? formatMoney(proforma, tax.recAmount) : null, - retentionLabel: - tax.retentionPercentage !== null - ? `${tax.retentionCode ?? context.translate("proformas.detail.fiscal.unknown_tax_code")} · ${NumberHelper.formatNumber(tax.retentionPercentage, 2)}%` - : null, - retentionAmountLabel: - tax.retentionPercentage !== null ? formatMoney(proforma, tax.retentionAmount) : null, - })); + const breakdown: ProformaDetailFiscalBreakdownItemViewModel[] = proforma.taxes.map( + (tax, index) => ({ + key: `${tax.ivaCode ?? "tax"}-${index}`, + taxLabel: buildTaxLabel(tax, context.translate), + taxableAmountLabel: formatMoney(proforma, tax.taxableAmount), + taxAmountLabel: formatMoney(proforma, tax.ivaAmount), + recLabel: + tax.recPercentage === null + ? null + : `${tax.recCode ?? context.translate("proformas.detail.fiscal.unknown_tax_code")} · ${NumberHelper.formatNumber(tax.recPercentage, 2)}%`, + recAmountLabel: tax.recPercentage === null ? null : formatMoney(proforma, tax.recAmount), + retentionLabel: + tax.retentionPercentage === null + ? null + : `${tax.retentionCode ?? context.translate("proformas.detail.fiscal.unknown_tax_code")} · ${NumberHelper.formatNumber(tax.retentionPercentage, 2)}%`, + retentionAmountLabel: + tax.retentionPercentage === null ? null : formatMoney(proforma, tax.retentionAmount), + }) + ); const linkedInvoice: ProformaDetailLinkedInvoiceViewModel = { state: linkedInvoiceState, linkedInvoiceId: proforma.linkedInvoiceId ?? null, - primaryText: context.translate(`proformas.detail.linked_invoice.states.${linkedInvoiceState}.title`), + primaryText: context.translate( + `proformas.detail.linked_invoice.states.${linkedInvoiceState}.title` + ), secondaryText: context.translate( `proformas.detail.linked_invoice.states.${linkedInvoiceState}.description` ), @@ -283,7 +290,9 @@ export const buildProformaDetailGeneralViewModel = ( const fiscal: ProformaDetailFiscalViewModel = { taxRegimeLabel: proforma.taxRegimeCode, - taxModeLabel: context.translate(`proformas.detail.fiscal.tax_mode.${proforma.taxConfig.taxMode}`), + taxModeLabel: context.translate( + `proformas.detail.fiscal.tax_mode.${proforma.taxConfig.taxMode}` + ), subtotalLabel: formatMoney(proforma, proforma.subtotalAmount), discountLabel: formatMoney(proforma, proforma.totalDiscountAmount), taxableAmountLabel: formatMoney(proforma, proforma.taxableAmount), diff --git a/modules/customer-invoices/src/web/proformas/download-pdf/controller/use-download-proforma-pdf.controller.ts b/modules/customer-invoices/src/web/proformas/download-pdf/controller/use-download-proforma-pdf.controller.ts index c498521a..15840b75 100644 --- a/modules/customer-invoices/src/web/proformas/download-pdf/controller/use-download-proforma-pdf.controller.ts +++ b/modules/customer-invoices/src/web/proformas/download-pdf/controller/use-download-proforma-pdf.controller.ts @@ -15,7 +15,9 @@ export function useDownloadProformaPDFController() { // Efecto: cuando hay blob + pending, disparamos la descarga React.useEffect(() => { - if (!(pending && data)) return; + if (!(pending && data)) { + return; + } const blob = data; const url = URL.createObjectURL(blob); diff --git a/modules/customer-invoices/src/web/proformas/download-pdf/hooks/use-download-proforma-pdf-query.ts b/modules/customer-invoices/src/web/proformas/download-pdf/hooks/use-download-proforma-pdf-query.ts index 7fba49d9..762ecc51 100644 --- a/modules/customer-invoices/src/web/proformas/download-pdf/hooks/use-download-proforma-pdf-query.ts +++ b/modules/customer-invoices/src/web/proformas/download-pdf/hooks/use-download-proforma-pdf-query.ts @@ -1,8 +1,8 @@ import { useDataSource } from "@erp/core/hooks"; import { useQuery } from "@tanstack/react-query"; -import { downloadProformaPDFApi } from "../api"; import { PROFORMA_PDF_QUERY_KEY } from "../../shared/hooks/keys"; +import { downloadProformaPDFApi } from "../api"; type DownloadProformaPDFOptions = { enabled?: boolean; @@ -18,7 +18,9 @@ export function useDownloadProformaPDFQuery( return useQuery({ queryKey: PROFORMA_PDF_QUERY_KEY(proformaId), queryFn: async (context) => { - if (!proformaId) throw new Error("proformaId is required"); + if (!proformaId) { + throw new Error("proformaId is required"); + } const { signal } = context; return await downloadProformaPDFApi(dataSource, proformaId, { diff --git a/modules/customer-invoices/src/web/proformas/index.ts b/modules/customer-invoices/src/web/proformas/index.ts index 8a02684c..079816f2 100644 --- a/modules/customer-invoices/src/web/proformas/index.ts +++ b/modules/customer-invoices/src/web/proformas/index.ts @@ -1,4 +1,4 @@ -export * from "./list"; export * from "./create"; -export * from "./update"; export * from "./detail"; +export * from "./list"; +export * from "./update"; diff --git a/modules/customer-invoices/src/web/proformas/issue-proforma/entities/index.ts b/modules/customer-invoices/src/web/proformas/issue-proforma/entities/index.ts index 421be849..7ef2ce50 100644 --- a/modules/customer-invoices/src/web/proformas/issue-proforma/entities/index.ts +++ b/modules/customer-invoices/src/web/proformas/issue-proforma/entities/index.ts @@ -1 +1 @@ -export * from './issue-proforma-target.entity'; +export * from "./issue-proforma-target.entity"; diff --git a/modules/customer-invoices/src/web/proformas/issue-proforma/utils/index.ts b/modules/customer-invoices/src/web/proformas/issue-proforma/utils/index.ts index 3a3023a5..13f366f0 100644 --- a/modules/customer-invoices/src/web/proformas/issue-proforma/utils/index.ts +++ b/modules/customer-invoices/src/web/proformas/issue-proforma/utils/index.ts @@ -1 +1 @@ -export * from './prepare-issue-proforma-targets'; +export * from "./prepare-issue-proforma-targets"; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts index 4135fb00..eec22dc5 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts @@ -63,7 +63,9 @@ export const useListProformasPageController = (scope: ProformaListScope) => { }, []); const closeArchiveDialog = useCallback(() => { - if (archiveMutation.isPending) return; + if (archiveMutation.isPending) { + return; + } setArchiveTarget(null); }, [archiveMutation.isPending]); @@ -95,7 +97,9 @@ export const useListProformasPageController = (scope: ProformaListScope) => { }, []); const closeUnarchiveDialog = useCallback(() => { - if (unarchiveMutation.isPending) return; + if (unarchiveMutation.isPending) { + return; + } setUnarchiveTarget(null); }, [unarchiveMutation.isPending]); @@ -144,7 +148,7 @@ export const useListProformasPageController = (scope: ProformaListScope) => { } return buildProformaListRefreshErrorViewModel(listCtrl.error, (key) => t(key)); - }, [listCtrl.error, listCtrl.hasUsableData, listCtrl.isError, t]); + }, [listCtrl.error, listCtrl.hasUsableData, listCtrl.isError, t, listCtrl]); const feedbackState = resolveProformaListFeedbackState(listCtrl); diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts index ef5032fe..24525af4 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts @@ -146,7 +146,9 @@ export const useListProformasController = (scope: ProformaListScope) => { const setStatusFilterValue = useCallback( (value: string) => { const nextValue = (value || "all") as ProformaListStatusFilter; - if (statusFilter === nextValue) return; + if (statusFilter === nextValue) { + return; + } setStatusFilter(nextValue); setSearchParams((prev) => { @@ -166,7 +168,9 @@ export const useListProformasController = (scope: ProformaListScope) => { const setSearchValue = useCallback( (value: string) => { const nextValue = value.trim().replace(/\s+/g, " "); - if (search === nextValue) return; + if (search === nextValue) { + return; + } setSearch(nextValue); setSearchParams((prev) => { @@ -197,7 +201,9 @@ export const useListProformasController = (scope: ProformaListScope) => { const setPageSizeValue = useCallback( (value: number) => { - if (pageSize === value) return; + if (pageSize === value) { + return; + } // Reset page to 1 and update pageSize when it changes setSearchParams((prev) => { diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.test.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.test.tsx index c4b1fc83..df650a75 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.test.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.test.tsx @@ -1,9 +1,11 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { PROFORMA_STATUS, type Proforma } from "../../../../shared"; + import { ProformaSummaryPanel } from "./proforma-summary-panel"; const mockProforma: Proforma = { diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx index 9f125e4e..0030ebd0 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proforma-summary-panel/proforma-summary-panel.tsx @@ -9,7 +9,7 @@ import type { Proforma } from "../../../../shared"; import { ProformaFooterActions } from "./proforma-footer-actions"; import { ProformaSummaryContent } from "./proforma-summary-content"; -const mockERPData = { +const _mockERPData = { totalPurchases: 45750.8, purchasesThisYear: 12350.25, lastInvoices: [ diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/orders-table.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/orders-table.tsx index 3aa586ac..72a533cc 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/orders-table.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/orders-table.tsx @@ -201,7 +201,9 @@ export function OrdersTable() { useEffect(() => { const el = scrollRef.current; - if (!el) return; + if (!el) { + return; + } const check = () => { setIsScrolled(el.scrollLeft > 0); diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/scoped-list-proformas-page-view.test.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/scoped-list-proformas-page-view.test.tsx index 2093ea90..1053650a 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/scoped-list-proformas-page-view.test.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/scoped-list-proformas-page-view.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; diff --git a/modules/customer-invoices/src/web/proformas/list/utils/build-proforma-list-page-error-view-model.ts b/modules/customer-invoices/src/web/proformas/list/utils/build-proforma-list-page-error-view-model.ts index 8b62bc66..b0c3171a 100644 --- a/modules/customer-invoices/src/web/proformas/list/utils/build-proforma-list-page-error-view-model.ts +++ b/modules/customer-invoices/src/web/proformas/list/utils/build-proforma-list-page-error-view-model.ts @@ -5,9 +5,7 @@ import type { ProformaListRefreshErrorViewModel, } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildVariant = (error: unknown): ProformaListPageErrorViewModel["variant"] | null => { if (!isApiClientError(error)) { diff --git a/modules/customer-invoices/src/web/proformas/shared/api/archive-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/archive-proforma-by-id.api.ts index 7b0b8ec5..5a4e6570 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/archive-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/archive-proforma-by-id.api.ts @@ -15,7 +15,9 @@ export const archiveProformaById = ( ): Promise => { const { proformaId, signal } = params; - if (!proformaId) throw new Error("proformaId is required"); + if (!proformaId) { + throw new Error("proformaId is required"); + } return dataSource.custom({ path: `proformas/${proformaId}/archive`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts index c617528b..383594eb 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/change-proforma-status-by-id.api.ts @@ -7,7 +7,7 @@ export interface ChangeProformaStatusByIdParams { data: ChangeStatusProformaByIdRequestDTO; } -export type ChangeProformaStatusByIdResult = void; +export type ChangeProformaStatusByIdResult = undefined; export async function changeProformaStatusById( dataSource: IDataSource, @@ -15,7 +15,9 @@ export async function changeProformaStatusById( ): Promise { const { id, data } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.custom({ path: `proformas/${id}/status`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts index 7e7b2ee9..1dabc58b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/create-proforma.api.ts @@ -24,7 +24,9 @@ export const createProforma = ( ): Promise => { const { id, data } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.createOne( "proformas", diff --git a/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts index 07ab844e..f214b8c8 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/delete-proforma-by-id.api.ts @@ -14,7 +14,7 @@ export interface DeleteProformaByIdParams { signal?: AbortSignal; } -export type DeleteProformaByIdResult = void; +export type DeleteProformaByIdResult = undefined; export const deleteProformaById = ( dataSource: IDataSource, @@ -22,7 +22,9 @@ export const deleteProformaById = ( ): Promise => { const { proformaId, signal } = params; - if (!proformaId) throw new Error("proformaId is required"); + if (!proformaId) { + throw new Error("proformaId is required"); + } return dataSource.deleteOne("proformas", proformaId, { signal }); }; diff --git a/modules/customer-invoices/src/web/proformas/shared/api/duplicate-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/duplicate-proforma-by-id.api.ts index e405c2a3..b7de37bc 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/duplicate-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/duplicate-proforma-by-id.api.ts @@ -15,7 +15,9 @@ export const duplicateProformaById = ( ): Promise => { const { proformaId, signal } = params; - if (!proformaId) throw new Error("proformaId is required"); + if (!proformaId) { + throw new Error("proformaId is required"); + } return dataSource.custom({ path: `proformas/${proformaId}/duplicate`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts index 9f1cc308..8e985d84 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-id.api.ts @@ -24,7 +24,9 @@ export function getProformaById( ): Promise { const { id, signal } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.getOne("proformas", id, { signal }); } diff --git a/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts index db9eea2f..36b0724e 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/issue-proforma-by-id.api.ts @@ -13,7 +13,9 @@ export const issueProformaById = ( ): Promise => { const { id } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.custom, IssueProformaByIdResponseDTO>({ path: `proformas/${id}/issue`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts index d9222614..51a22ba8 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/list-proformas-by-criteria.api.ts @@ -29,7 +29,11 @@ export function getListProformasByCriteria( const { criteria, scope, signal } = params; const resource = - scope === "archived" ? "proformas/archived" : scope === "deleted" ? "proformas/deleted" : "proformas"; + scope === "archived" + ? "proformas/archived" + : scope === "deleted" + ? "proformas/deleted" + : "proformas"; return dataSource.getList(resource, { signal, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts index d0902029..6a8e5ac9 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/report-proforma-by-id.api.ts @@ -12,7 +12,9 @@ export const reportProformaById = ( ): Promise => { const { id } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.custom({ path: `proformas/${id}/report/pdf`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/unarchive-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/unarchive-proforma-by-id.api.ts index 85c67451..187536a9 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/unarchive-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/unarchive-proforma-by-id.api.ts @@ -15,7 +15,9 @@ export const unarchiveProformaById = ( ): Promise => { const { proformaId, signal } = params; - if (!proformaId) throw new Error("proformaId is required"); + if (!proformaId) { + throw new Error("proformaId is required"); + } return dataSource.custom({ path: `proformas/${proformaId}/unarchive`, diff --git a/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts b/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts index 7e9a6afc..14253ca9 100644 --- a/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts +++ b/modules/customer-invoices/src/web/proformas/shared/api/update-proforma-by-id.api.ts @@ -27,7 +27,9 @@ export const updateProformaById = ( ): Promise => { const { id, data } = params; - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } return dataSource.updateOne( "proformas", diff --git a/modules/customer-invoices/src/web/proformas/shared/entities/index.ts b/modules/customer-invoices/src/web/proformas/shared/entities/index.ts index 15a9ff04..7369da20 100644 --- a/modules/customer-invoices/src/web/proformas/shared/entities/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/entities/index.ts @@ -2,10 +2,10 @@ export * from "./document-series-summary.entity"; export * from "./duplicate-proforma-result.entity"; export * from "./forms/proforma-item-form.entity"; export * from "./proforma.entity"; +export * from "./proforma-issuance-readiness.entity"; export * from "./proforma-item.entity"; export * from "./proforma-list.entity"; export * from "./proforma-list-row.entity"; -export * from "./proforma-issuance-readiness.entity"; export * from "./proforma-recipient.entity"; export * from "./proforma-status.entity"; export * from "./proforma-status-counts.entity"; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts index 2818baf4..207275c1 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/index.ts @@ -6,8 +6,8 @@ export * from "./use-proforma-change-status-mutation"; export * from "./use-proforma-create-mutation"; export * from "./use-proforma-delete-mutation"; export * from "./use-proforma-get-query"; -export * from "./use-proforma-issue-mutation"; export * from "./use-proforma-issuance-readiness-query"; +export * from "./use-proforma-issue-mutation"; export * from "./use-proforma-unarchive-mutation"; export * from "./use-proforma-update-mutation"; export * from "./use-proformas-list-query"; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts index f17075f6..cd6138b9 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/proforma-cache-strategy.ts @@ -115,10 +115,14 @@ export function upsertProformaInListCaches( for (const key of keys) { const page = queryClient.getQueryData(key); - if (!hasListItems(page)) continue; + if (!hasListItems(page)) { + continue; + } const index = page.items.findIndex((row) => row.id === proforma.id); - if (index === -1) continue; + if (index === -1) { + continue; + } const nextItems = page.items.slice(); nextItems[index] = { @@ -145,7 +149,9 @@ export function removeProformaFromListCaches( })); for (const { key, page } of snapshots) { - if (!hasListItems(page)) continue; + if (!hasListItems(page)) { + continue; + } queryClient.setQueryData(key, { ...page, @@ -214,7 +220,9 @@ export function rollbackDeleteProformaOptimisticUpdate( queryClient: QueryClient, context?: DeleteProformaCacheContext ) { - if (!context) return; + if (!context) { + return; + } restoreProformaListCaches(queryClient, context.snapshots); } diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts index a569817b..82f6e654 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-delete-mutation.ts @@ -23,28 +23,26 @@ export const useProformaDeleteMutation = () => { DefaultError, DeleteProformaByIdParams, DeleteProformaContext - >( - { - mutationKey: DELETE_PROFORMA_MUTATION_KEY, - mutationFn: async ({ proformaId, signal }) => { - if (!proformaId) { - throw new Error("proformaId is required"); - } + >({ + mutationKey: DELETE_PROFORMA_MUTATION_KEY, + mutationFn: async ({ proformaId, signal }) => { + if (!proformaId) { + throw new Error("proformaId is required"); + } - await deleteProformaById(dataSource, { proformaId, signal }); - return { proformaId }; - }, - onMutate: async ({ proformaId }) => { - return prepareDeleteProformaOptimisticUpdate(queryClient, proformaId); - }, + await deleteProformaById(dataSource, { proformaId, signal }); + return { proformaId }; + }, + onMutate: async ({ proformaId }) => { + return prepareDeleteProformaOptimisticUpdate(queryClient, proformaId); + }, - onError: (_error, _params, context) => { - rollbackDeleteProformaOptimisticUpdate(queryClient, context); - }, + onError: (_error, _params, context) => { + rollbackDeleteProformaOptimisticUpdate(queryClient, context); + }, - onSettled: async (_data, _error, { proformaId }) => { - await finalizeDeletedProformaCaches(queryClient, proformaId); - }, - } - ); + onSettled: async (_data, _error, { proformaId }) => { + await finalizeDeletedProformaCaches(queryClient, proformaId); + }, + }); }; diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts index 32e38a33..cea692f6 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts @@ -22,7 +22,9 @@ export const useProformaGetQuery = ( return useQuery({ queryKey: PROFORMA_QUERY_KEY(id), queryFn: async ({ signal }) => { - if (!id) throw new Error("proformaId is required"); + if (!id) { + throw new Error("proformaId is required"); + } const dto = await getProformaById(dataSource, { id: id!, diff --git a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proformas-list-query.ts b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proformas-list-query.ts index a06d469b..21580f7b 100644 --- a/modules/customer-invoices/src/web/proformas/shared/hooks/use-proformas-list-query.ts +++ b/modules/customer-invoices/src/web/proformas/shared/hooks/use-proformas-list-query.ts @@ -2,12 +2,12 @@ import type { CriteriaDTO } from "@erp/core"; import { useDataSource } from "@erp/core/hooks"; import { type DefaultError, type UseQueryResult, useQuery } from "@tanstack/react-query"; +import type { ProformaListScope } from "../../list/types/proforma-list-filters"; import { ListProformasAdapter } from "../adapters"; import { getListProformasByCriteria } from "../api"; import type { ProformaList } from "../entities"; import { LIST_PROFORMAS_QUERY_KEY } from "./keys"; -import type { ProformaListScope } from "../../list/types/proforma-list-filters"; export interface ProformasListQueryOptions { enabled?: boolean; diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/ensure-current-value-in-select-items.ts b/modules/customer-invoices/src/web/proformas/shared/utils/ensure-current-value-in-select-items.ts index 951bbf77..7d86c4c3 100644 --- a/modules/customer-invoices/src/web/proformas/shared/utils/ensure-current-value-in-select-items.ts +++ b/modules/customer-invoices/src/web/proformas/shared/utils/ensure-current-value-in-select-items.ts @@ -4,10 +4,14 @@ export const ensureCurrentValueInSelectItems = ( items: SelectFieldItem[], currentValue?: string | null ): SelectFieldItem[] => { - if (!currentValue) return items; + if (!currentValue) { + return items; + } const exists = items.some((item) => item.value === currentValue); - if (exists) return items; + if (exists) { + return items; + } return [ { diff --git a/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts b/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts index 84e45d9e..ad69114a 100644 --- a/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts +++ b/modules/customer-invoices/src/web/proformas/shared/utils/proforma-fiscal-options.utils.ts @@ -50,7 +50,9 @@ export const getProformaTaxCode = ( ): string | null => { const parsedTaxPercentage = parseProformaTaxPercentage(taxPercentage); - if (parsedTaxPercentage === null) return null; + if (parsedTaxPercentage === null) { + return null; + } return PROFORMA_TAX_DEFINITIONS[parsedTaxPercentage].taxCode; }; @@ -58,7 +60,9 @@ export const getProformaTaxCode = ( export const getProformaRecPercentage = ( taxPercentage: ProformaTaxPercentageOption | null | undefined ): ProformaRecPercentageOption | null => { - if (taxPercentage === null || taxPercentage === undefined) return null; + if (taxPercentage === null || taxPercentage === undefined) { + return null; + } return PROFORMA_TAX_DEFINITIONS[taxPercentage].recPercentage; }; @@ -79,7 +83,9 @@ export const getProformaRecCode = ( return definition?.recCode ?? null; }; -export const getProformaTaxPercentageFromCode = (taxCode: string | null | undefined): number | null => { +export const getProformaTaxPercentageFromCode = ( + taxCode: string | null | undefined +): number | null => { if (!taxCode) { return null; } @@ -91,7 +97,9 @@ export const getProformaTaxPercentageFromCode = (taxCode: string | null | undefi return definition?.taxPercentage ?? null; }; -export const getProformaRecPercentageFromCode = (recCode: string | null | undefined): number | null => { +export const getProformaRecPercentageFromCode = ( + recCode: string | null | undefined +): number | null => { if (!recCode) { return null; } @@ -108,7 +116,9 @@ export const getProformaRetentionCode = ( ): string | null => { const parsedRetentionPercentage = parseProformaRetentionPercentage(retentionPercentage); - if (parsedRetentionPercentage === null) return null; + if (parsedRetentionPercentage === null) { + return null; + } return PROFORMA_RETENTION_DEFINITIONS[parsedRetentionPercentage].retentionCode; }; diff --git a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts index 2e018bee..ccfb3451 100644 --- a/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts +++ b/modules/customer-invoices/src/web/proformas/update/adapters/map-proforma-to-proforma-update-form.adapter.ts @@ -33,7 +33,9 @@ export const mapProformaToProformaUpdateForm = (proforma: Proforma): ProformaUpd proformaReference: proforma.proformaReference, targetInvoiceSeriesCode: - proforma.targetInvoiceSeriesCode ?? proforma.series ?? proformaDefaults.targetInvoiceSeriesCode, + proforma.targetInvoiceSeriesCode ?? + proforma.series ?? + proformaDefaults.targetInvoiceSeriesCode, proformaDate: proforma.proformaDate ?? proformaDefaults.proformaDate, operationDate: proforma.operationDate ?? proformaDefaults.operationDate, diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts index c9bdd7bc..828ad27a 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-controller.ts @@ -99,7 +99,9 @@ export const useUpdateProformaController = ( const targetInvoiceSeriesQuery = useDocumentSeriesQuery("issued_invoice"); const initialValues = useMemo(() => { - if (!proformaData) return buildProformaUpdateDefault(); + if (!proformaData) { + return buildProformaUpdateDefault(); + } return mapProformaToProformaUpdateForm(proformaData); }, [proformaData]); @@ -116,7 +118,9 @@ export const useUpdateProformaController = ( /** Reiniciar el form al recibir datos */ useEffect(() => { - if (!proformaData) return; + if (!proformaData) { + return; + } form.reset(mapProformaToProformaUpdateForm(proformaData), { keepDirty: false, // <-- importante: no marca el form como "dirty" al cargar los datos reales diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-items-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-items-controller.ts index 72558919..c25713fd 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-items-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-items-controller.ts @@ -115,7 +115,9 @@ export const useUpdateProformaItemsController = ({ (index: number) => { const currentItems = getValues("items") ?? []; - if (index < 0 || index >= currentItems.length) return; + if (index < 0 || index >= currentItems.length) { + return; + } replaceItems(currentItems.filter((_, currentIndex) => currentIndex !== index)); }, @@ -127,7 +129,9 @@ export const useUpdateProformaItemsController = ({ const currentItems = getValues("items") ?? []; const item = currentItems[index]; - if (!item) return; + if (!item) { + return; + } const duplicatedItem: ProformaItemUpdateForm = { ...item, @@ -149,7 +153,9 @@ export const useUpdateProformaItemsController = ({ (index: number) => { const currentItems = getValues("items") ?? []; - if (index <= 0 || index >= currentItems.length) return; + if (index <= 0 || index >= currentItems.length) { + return; + } const nextItems = [...currentItems]; [nextItems[index - 1], nextItems[index]] = [nextItems[index], nextItems[index - 1]]; @@ -163,7 +169,9 @@ export const useUpdateProformaItemsController = ({ (index: number) => { const currentItems = getValues("items") ?? []; - if (index < 0 || index >= currentItems.length - 1) return; + if (index < 0 || index >= currentItems.length - 1) { + return; + } const nextItems = [...currentItems]; [nextItems[index], nextItems[index + 1]] = [nextItems[index + 1], nextItems[index]]; @@ -240,14 +248,18 @@ export const useUpdateProformaItemsController = ({ const itemErrors = React.useMemo(() => { //console.log("itemErrors => ", errors.items); - if (!errors.items) return []; + if (!errors.items) { + return []; + } if (errors.items.root) { return [errors.items.root]; //console.error("Errores de validación en items.root:", errors.items.root); } - if (!Array.isArray(errors.items)) return []; + if (!Array.isArray(errors.items)) { + return []; + } return errors.items.map((error) => error ?? {}); }, [errors.items]); diff --git a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts index 55df794f..bce89ca1 100644 --- a/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts +++ b/modules/customer-invoices/src/web/proformas/update/controllers/use-update-proforma-tax-controller.ts @@ -58,7 +58,9 @@ export const useUpdateProformaTaxController = ({ form }: UseUpdateProformaTaxCon }); useEffect(() => { - if (taxMode !== "single") return; + if (taxMode !== "single") { + return; + } const currentItems = getValues("items") ?? []; diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/editor-sidebar.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/editor-sidebar.tsx index 5bd042b6..e675658e 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/editor-sidebar.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/editor-sidebar.tsx @@ -36,17 +36,17 @@ export const EditorSidebar = ({ className, }: EditorSidebarProps) => { - const [isTotalsExpanded, setIsTotalsExpanded] = useState(false); + const [_isTotalsExpanded, _setIsTotalsExpanded] = useState(false); const { t } = useTranslation(); - const formatMoney = (value: number) => { + const _formatMoney = (value: number) => { return new Intl.NumberFormat("es-ES", { style: "currency", currency: proforma.config.currency, }).format(value); }; - const formatPercent = (value: number) => { + const _formatPercent = (value: number) => { return new Intl.NumberFormat("es-ES", { style: "percent", minimumFractionDigits: 2, @@ -144,7 +144,7 @@ interface TotalsRowProps { variant?: "default" | "discount" | "tax"; } -function TotalsRow({ label, value, className, small, variant = "default" }: TotalsRowProps) { +function _TotalsRow({ label, value, className, small, variant = "default" }: TotalsRowProps) { const valueColorClass = { default: "text-foreground", discount: "text-amber-700 dark:text-amber-400", diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx index d6b754cd..c37a74fb 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-line-editor.tsx @@ -121,7 +121,9 @@ export const ProformaLineEditor = ({ //cell: ({ index }) => MoneyHelper.formatCurrency(getItemAmounts(index).total, 2, currency), cell: ({ index }) => { const isValued = getItemIsValued(index); - if (!isValued) return ""; + if (!isValued) { + return ""; + } const { subtotal } = getItemAmounts(index); return MoneyHelper.formatCurrency(subtotal, 2, currency); @@ -150,7 +152,9 @@ export const ProformaLineEditor = ({ //cell: ({ index }) => MoneyHelper.formatCurrency(getItemAmounts(index).total, 2, currency), cell: ({ index }) => { const isValued = getItemIsValued(index); - if (!isValued) return ""; + if (!isValued) { + return ""; + } const { itemDiscountAmount } = getItemAmounts(index); return MoneyHelper.formatCurrency(itemDiscountAmount, 2, currency); @@ -185,7 +189,9 @@ export const ProformaLineEditor = ({ className: "text-right font-semibold tabular-nums pt-4", cell: ({ index }: { index: number }) => { const isValued = getItemIsValued(index); - if (!isValued) return ""; + if (!isValued) { + return ""; + } const { taxAmount } = getItemAmounts(index); return MoneyHelper.formatCurrency(taxAmount); @@ -218,7 +224,9 @@ export const ProformaLineEditor = ({ className: "text-right font-semibold tabular-nums pt-4", cell: ({ index }: { index: number }) => { const isValued = getItemIsValued(index); - if (!isValued) return ""; + if (!isValued) { + return ""; + } const { total } = getItemAmounts(index); return MoneyHelper.formatCurrency(total, 2, currency); @@ -237,7 +245,9 @@ export const ProformaLineEditor = ({ className: "text-right font-semibold tabular-nums pt-4", cell: ({ index }: { index: number }) => { const isValued = getItemIsValued(index); - if (!isValued) return ""; + if (!isValued) { + return ""; + } const { subtotalTaxable } = getItemAmounts(index); return MoneyHelper.formatCurrency(subtotalTaxable, 2, currency); diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-header.test.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-header.test.tsx index 99df5a1b..88baaa4c 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-header.test.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-header.test.tsx @@ -1,10 +1,12 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { FormProvider, useForm } from "react-hook-form"; import type { ProformaUpdateForm } from "../../entities"; import { buildProformaUpdateDefault } from "../../utils"; + import { ProformaUpdateHeader } from "./proforma-update-header"; const baseLabels = { diff --git a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx index a77c7cd1..3be589e9 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/blocks/selected-recipient/customer-card.tsx @@ -22,8 +22,12 @@ function buildAddress(customer: CustomerSummary) { if (customer.address.postal_code && customer.address.city) { line2Parts.push(`${customer.address.postal_code}\u00A0${customer.address.city}`); // CP Ciudad } else { - if (customer.address.postal_code) line2Parts.push(customer.address.postal_code); - if (customer.address.city) line2Parts.push(customer.address.city); + if (customer.address.postal_code) { + line2Parts.push(customer.address.postal_code); + } + if (customer.address.city) { + line2Parts.push(customer.address.city); + } } const line2 = line2Parts.join(" "); diff --git a/modules/customer-invoices/src/web/proformas/update/ui/components/proforma-update-customer-change-dialog.tsx b/modules/customer-invoices/src/web/proformas/update/ui/components/proforma-update-customer-change-dialog.tsx index f33d34b4..466e7b3f 100644 --- a/modules/customer-invoices/src/web/proformas/update/ui/components/proforma-update-customer-change-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/update/ui/components/proforma-update-customer-change-dialog.tsx @@ -59,14 +59,20 @@ export const ProformaUpdateCustomerChangeDialog = ({ {status === "incompatible" - ? t("proformas.update.customer_change.dialog.incompatible_title", "No es posible cambiar de cliente") + ? t( + "proformas.update.customer_change.dialog.incompatible_title", + "No es posible cambiar de cliente" + ) : t("proformas.update.customer_change.dialog.compatible_title", "Cambiar cliente")} {status === "loading" ? ( - {t("proformas.update.customer_change.dialog.loading", "Comprobando compatibilidad del cliente...")} + {t( + "proformas.update.customer_change.dialog.loading", + "Comprobando compatibilidad del cliente..." + )} ) : null} diff --git a/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.test.ts b/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.test.ts index 5943dcb5..4c0cc2e0 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.test.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.test.ts @@ -13,12 +13,10 @@ const translate = (key: string) => "pages.proformas.update.page_error.notFound.title": "Proforma no encontrada", "pages.proformas.update.page_error.notFound.description": "La proforma ya no existe o no está disponible.", - "pages.proformas.update.page_error.forbidden.title": - "No tienes acceso a esta proforma", + "pages.proformas.update.page_error.forbidden.title": "No tienes acceso a esta proforma", "pages.proformas.update.page_error.forbidden.description": "No dispones de permisos para editar este documento.", - "pages.proformas.update.page_error.network.title": - "No se pudo conectar con el servidor", + "pages.proformas.update.page_error.network.title": "No se pudo conectar con el servidor", "pages.proformas.update.page_error.network.description": "Comprueba tu conexión e inténtalo de nuevo.", "pages.proformas.update.page_error.timeout.title": "La carga está tardando demasiado", @@ -27,8 +25,7 @@ const translate = (key: string) => "pages.proformas.update.page_error.server.title": "No se pudo cargar la proforma", "pages.proformas.update.page_error.server.description": "Se produjo un error interno al recuperar la proforma.", - "pages.proformas.update.page_error.unavailable.title": - "Servicio temporalmente no disponible", + "pages.proformas.update.page_error.unavailable.title": "Servicio temporalmente no disponible", "pages.proformas.update.page_error.unavailable.description": "El servicio de proformas no está disponible en este momento.", "pages.proformas.update.page_error.unknown.title": "No se pudo cargar la proforma", diff --git a/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.ts b/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.ts index c3682cba..e352b4f9 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/build-proforma-update-page-error-view-model.ts @@ -2,9 +2,7 @@ import { isApiClientError } from "@erp/core/client"; import type { ProformaUpdatePageErrorViewModel } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildErrorVariant = ( error: unknown diff --git a/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.test.ts b/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.test.ts index ac0cd355..b59fc813 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.test.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/build-update-proforma-by-id-params.test.ts @@ -2,6 +2,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import type { ProformaUpdateForm } from "../entities"; + import { buildUpdateProformaByIdParams } from "./build-update-proforma-by-id-params"; const buildFormData = (value: Partial = {}): ProformaUpdateForm => diff --git a/modules/customer-invoices/src/web/proformas/update/utils/index.ts b/modules/customer-invoices/src/web/proformas/update/utils/index.ts index af485dc1..abe5b9e7 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/index.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/index.ts @@ -3,6 +3,6 @@ export * from "./build-proforma-update-default"; export * from "./build-proforma-update-page-error-view-model"; export * from "./build-proforma-update-patch"; export * from "./build-update-proforma-by-id-params"; -export * from "./evaluate-proforma-customer-change"; export * from "./calculate-proforma-totals"; +export * from "./evaluate-proforma-customer-change"; export * from "./resolve-proforma-update-page-feedback-state"; diff --git a/modules/customer-invoices/src/web/proformas/update/utils/resolve-proforma-update-page-feedback-state.ts b/modules/customer-invoices/src/web/proformas/update/utils/resolve-proforma-update-page-feedback-state.ts index ddc6b2e3..88762bde 100644 --- a/modules/customer-invoices/src/web/proformas/update/utils/resolve-proforma-update-page-feedback-state.ts +++ b/modules/customer-invoices/src/web/proformas/update/utils/resolve-proforma-update-page-feedback-state.ts @@ -24,6 +24,6 @@ export const resolveProformaUpdatePageFeedbackState = ({ return { isInitialLoading: isLoading || (isLoadError && isCancelled && !hasProformaData), hasBlockingError: isLoadError && !isCancelled && !hasProformaData, - hasDataInconsistency: !isLoading && !isLoadError && !hasProformaData, + hasDataInconsistency: !(isLoading || isLoadError || hasProformaData), }; }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-page.tsx b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-page.tsx index 991c59f7..e2d11c4a 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-page.tsx +++ b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-page.tsx @@ -41,7 +41,9 @@ export const RecurringProformaCreatePage = () => { }); const onSubmit = form.handleSubmit(async (values) => { - if (mutation.isPending) return; + if (mutation.isPending) { + return; + } setSubmitError(null); try { const created = await mutation.mutateAsync(buildCreateRecurringProformaInput(values)); diff --git a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-route.test.tsx b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-route.test.tsx index 0e0f54fb..650a2c7f 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-route.test.tsx +++ b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-route.test.tsx @@ -19,7 +19,9 @@ const readMarkup = async (stream: ReadableStream) => { for (;;) { const { done, value } = await reader.read(); - if (done) return markup; + if (done) { + return markup; + } markup += decoder.decode(value, { stream: true }); } }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-utils.test.ts b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-utils.test.ts index c96e7db5..3bd0d947 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-utils.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/create/recurring-proforma-create-utils.test.ts @@ -2,9 +2,9 @@ import assert from "node:assert/strict"; import test from "node:test"; import { + RecurringProformaCreateFormSchema, buildCreateRecurringProformaInput, getCreatedRecurringProformaPath, - RecurringProformaCreateFormSchema, } from "./recurring-proforma-create-utils"; test("crea un request minimo sin status, schedule ni overrides", () => { @@ -15,9 +15,15 @@ test("crea un request minimo sin status, schedule ni overrides", () => { }); test("rechaza el nombre requerido", () => { - assert.equal(RecurringProformaCreateFormSchema.safeParse({ name: "", description: "" }).success, false); + assert.equal( + RecurringProformaCreateFormSchema.safeParse({ name: "", description: "" }).success, + false + ); }); test("navega al detalle tras crear", () => { - assert.equal(getCreatedRecurringProformaPath("recurring-id"), "/recurring-proformas/recurring-id"); + assert.equal( + getCreatedRecurringProformaPath("recurring-id"), + "/recurring-proformas/recurring-id" + ); }); diff --git a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-detail-utils.ts b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-detail-utils.ts index 0fb5a5a7..256e1230 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-detail-utils.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-detail-utils.ts @@ -24,7 +24,9 @@ export const formatRecurringProformaOverride = (value: string | null, t: Transla value ?? t("recurring_proformas.detail.inherited"); export const formatRecurringProformaPercentage = (value: RecurringProformaPercentage | null) => { - if (!value) return null; + if (!value) { + return null; + } const scale = Number(value.scale); const numericValue = Number(value.value) / 10 ** scale; @@ -42,8 +44,12 @@ export const getRecurringProformaDetailFeedbackState = ({ isError: boolean; hasData: boolean; }) => { - if (isLoading) return "loading" as const; - if (isError || !hasData) return "error" as const; + if (isLoading) { + return "loading" as const; + } + if (isError || !hasData) { + return "error" as const; + } return "ready" as const; }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.test.ts b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.test.ts index f1130d9e..1de2a930 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.test.ts @@ -1,30 +1,61 @@ import assert from "node:assert/strict"; import test from "node:test"; +import { formatRecurringProformaDateOnly } from "./recurring-proforma-detail-utils"; import { buildGeneratedProformaPath, - getRecurringProformaOccurrencesFeedbackState, getRecurringProformaOccurrenceStatusKey, + getRecurringProformaOccurrencesFeedbackState, } from "./recurring-proforma-occurrences-section-utils"; -import { formatRecurringProformaDateOnly } from "./recurring-proforma-detail-utils"; test("resuelve los estados locales de la seccion", () => { - assert.equal(getRecurringProformaOccurrencesFeedbackState({ isLoading: true, isError: false, totalItems: 0 }), "loading"); - assert.equal(getRecurringProformaOccurrencesFeedbackState({ isLoading: false, isError: true, totalItems: 0 }), "error"); - assert.equal(getRecurringProformaOccurrencesFeedbackState({ isLoading: false, isError: false, totalItems: 0 }), "empty"); + assert.equal( + getRecurringProformaOccurrencesFeedbackState({ + isLoading: true, + isError: false, + totalItems: 0, + }), + "loading" + ); + assert.equal( + getRecurringProformaOccurrencesFeedbackState({ + isLoading: false, + isError: true, + totalItems: 0, + }), + "error" + ); + assert.equal( + getRecurringProformaOccurrencesFeedbackState({ + isLoading: false, + isError: false, + totalItems: 0, + }), + "empty" + ); }); test("presenta occurrence generated con estado, fecha prevista y enlace de proforma", () => { - assert.equal(getRecurringProformaOccurrenceStatusKey("generated"), "recurring_proformas.occurrences.status.generated"); + assert.equal( + getRecurringProformaOccurrenceStatusKey("generated"), + "recurring_proformas.occurrences.status.generated" + ); assert.equal(formatRecurringProformaDateOnly("2026-09-01", "es-ES"), "01/09/2026"); assert.equal(buildGeneratedProformaPath("proforma-id"), "/proformas/proforma-id"); assert.equal(buildGeneratedProformaPath(null), null); }); test("presenta occurrence failed con estado y conserva el diagnostico", () => { - const error = { status: "failed" as const, attempt_count: 3, last_error_message: "Customer unavailable" }; + const error = { + status: "failed" as const, + attempt_count: 3, + last_error_message: "Customer unavailable", + }; - assert.equal(getRecurringProformaOccurrenceStatusKey(error.status), "recurring_proformas.occurrences.status.failed"); + assert.equal( + getRecurringProformaOccurrenceStatusKey(error.status), + "recurring_proformas.occurrences.status.failed" + ); assert.equal(error.attempt_count, 3); assert.equal(error.last_error_message, "Customer unavailable"); }); diff --git a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.ts b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.ts index 1b7e051d..42adb13a 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/detail/recurring-proforma-occurrences-section-utils.ts @@ -21,7 +21,11 @@ export const getRecurringProformaOccurrencesFeedbackState = ({ isError: boolean; totalItems: number; }) => { - if (isLoading) return "loading" as const; - if (isError) return "error" as const; + if (isLoading) { + return "loading" as const; + } + if (isError) { + return "error" as const; + } return totalItems === 0 ? ("empty" as const) : ("ready" as const); }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.test.ts b/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.test.ts index 18004cd1..a83f2110 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.test.ts @@ -4,8 +4,8 @@ import test from "node:test"; import { formatRecurringProformaDateOnly, formatRecurringProformaSchedule, - getRecurringProformaStatusKey, getRecurringProformaListFeedbackState, + getRecurringProformaStatusKey, } from "./recurring-proforma-list-utils"; test("resuelve loading, error, empty y rows para el listado", () => { diff --git a/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.ts b/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.ts index 4f8c454d..a8222734 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/list/recurring-proforma-list-utils.ts @@ -13,7 +13,9 @@ export const formatRecurringProformaSchedule = ( schedule: RecurringProformaListRow["schedule"] | RecurringProformaDetail["schedule"], t: Translate ) => { - if (!schedule) return t("recurring_proformas.list.schedule.none"); + if (!schedule) { + return t("recurring_proformas.list.schedule.none"); + } const cadence = t(`recurring_proformas.list.schedule.${schedule.unit}`, { count: schedule.interval, @@ -32,7 +34,9 @@ export const formatRecurringProformaDateTime = (value: string, locale: string) = }).format(new Date(value)); export const formatRecurringProformaDateOnly = (value: string | null, locale: string) => { - if (!value) return "—"; + if (!value) { + return "—"; + } const [year, month, day] = value.split("-").map(Number); return new Intl.DateTimeFormat(locale, { dateStyle: "short" }).format( new Date(year, month - 1, day) @@ -48,7 +52,11 @@ export const getRecurringProformaListFeedbackState = ({ isError: boolean; totalItems: number; }) => { - if (isLoading) return "loading" as const; - if (isError) return "error" as const; + if (isLoading) { + return "loading" as const; + } + if (isError) { + return "error" as const; + } return totalItems === 0 ? ("empty" as const) : ("ready" as const); }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/list/ui/components/index.ts b/modules/customer-invoices/src/web/recurring-proformas/list/ui/components/index.ts index 3fba0ada..cee9e04c 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/list/ui/components/index.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/list/ui/components/index.ts @@ -1 +1 @@ -export * from './recurring-proforma-status-segmented-filter'; +export * from "./recurring-proforma-status-segmented-filter"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/index.ts b/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/index.ts index 38073b93..6a929d65 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/index.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/index.ts @@ -1 +1 @@ -export * from './scoped-list-recurring-proformas-page-view'; +export * from "./scoped-list-recurring-proformas-page-view"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/scoped-list-recurring-proformas-page-view.test.tsx b/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/scoped-list-recurring-proformas-page-view.test.tsx index 47b9e1cc..d394f194 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/scoped-list-recurring-proformas-page-view.test.tsx +++ b/modules/customer-invoices/src/web/recurring-proformas/list/ui/pages/scoped-list-recurring-proformas-page-view.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.test.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.test.ts index 0688fab0..ea054d36 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.test.ts @@ -1,9 +1,10 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { getRecurringProforma } from "./get-recurring-proforma.api"; import { recurringProformaQueryKeys } from "../hooks/recurring-proforma-query-keys"; +import { getRecurringProforma } from "./get-recurring-proforma.api"; + test("getRecurringProforma consulta el recurso detail con el id de ruta", async () => { let resource = ""; let receivedId = ""; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.ts index 1d0eb945..c21c371b 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/api/get-recurring-proforma.api.ts @@ -11,9 +11,7 @@ export const getRecurringProforma = ( throw new Error("recurringProformaId is required"); } - return dataSource.getOne( - "recurring-proformas", - recurringProformaId, - { signal } - ); + return dataSource.getOne("recurring-proformas", recurringProformaId, { + signal, + }); }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proforma-occurrences.api.test.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proforma-occurrences.api.test.ts index 30b83a7f..a01f658c 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proforma-occurrences.api.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proforma-occurrences.api.test.ts @@ -2,6 +2,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import { recurringProformaQueryKeys } from "../hooks/recurring-proforma-query-keys"; + import { listRecurringProformaOccurrences } from "./list-recurring-proforma-occurrences.api"; test("consulta occurrences con parent y paginacion backend", async () => { @@ -15,7 +16,10 @@ test("consulta occurrences con parent y paginacion backend", async () => { }, }; - await listRecurringProformaOccurrences(dataSource as never, "recurring-id", { page: 2, limit: 10 }); + await listRecurringProformaOccurrences(dataSource as never, "recurring-id", { + page: 2, + limit: 10, + }); assert.equal(resource, "recurring-proformas/recurring-id/occurrences"); assert.deepEqual(received, { page: 2, limit: 10, signal: undefined }); diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proformas.api.test.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proformas.api.test.ts index 944d4538..a63151fb 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proformas.api.test.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/api/list-recurring-proformas.api.test.ts @@ -14,10 +14,12 @@ test("listRecurringProformas envía search, status y paginación al backend", as }, }; - await listRecurringProformas( - dataSource as never, - { page: 2, limit: 20, search: "mantenimiento", status: "active" } - ); + await listRecurringProformas(dataSource as never, { + page: 2, + limit: 20, + search: "mantenimiento", + status: "active", + }); assert.equal(resource, "recurring-proformas"); assert.deepEqual(received, { diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/index.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/index.ts index c3bb32ff..fc55ff25 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/index.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/index.ts @@ -1,7 +1,7 @@ -export * from './recurring-proforma-query-keys'; -export * from './use-create-recurring-proforma-from-proforma'; -export * from './use-create-recurring-proforma'; -export * from './use-recurring-proforma-action-mutation'; -export * from './use-recurring-proforma-occurrences'; -export * from './use-recurring-proforma'; -export * from './use-recurring-proformas'; +export * from "./recurring-proforma-query-keys"; +export * from "./use-create-recurring-proforma"; +export * from "./use-create-recurring-proforma-from-proforma"; +export * from "./use-recurring-proforma"; +export * from "./use-recurring-proforma-action-mutation"; +export * from "./use-recurring-proforma-occurrences"; +export * from "./use-recurring-proformas"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/recurring-proforma-query-keys.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/recurring-proforma-query-keys.ts index 1947e9d3..9cf5aa0f 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/recurring-proforma-query-keys.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/recurring-proforma-query-keys.ts @@ -4,10 +4,7 @@ import type { } from "../recurring-proforma-list"; const RECURRING_PROFORMAS_QUERY_KEY = ["recurring-proformas"] as const; -const RECURRING_PROFORMAS_LISTS_QUERY_KEY = [ - ...RECURRING_PROFORMAS_QUERY_KEY, - "list", -] as const; +const RECURRING_PROFORMAS_LISTS_QUERY_KEY = [...RECURRING_PROFORMAS_QUERY_KEY, "list"] as const; export const recurringProformaQueryKeys = { all: RECURRING_PROFORMAS_QUERY_KEY, @@ -19,6 +16,5 @@ export const recurringProformaQueryKeys = { occurrences: ( recurringProformaId: string | undefined, filters: RecurringProformaOccurrencesFilters - ) => - [...RECURRING_PROFORMAS_QUERY_KEY, "occurrences", recurringProformaId, filters] as const, + ) => [...RECURRING_PROFORMAS_QUERY_KEY, "occurrences", recurringProformaId, filters] as const, }; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma-from-proforma.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma-from-proforma.ts index a50acbe8..120e74fc 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma-from-proforma.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma-from-proforma.ts @@ -4,11 +4,11 @@ import { ValidationErrorCollection } from "@repo/rdx-ddd"; import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; import type { ZodError } from "zod"; -import type { RecurringProformaDetail } from "../recurring-proforma-list"; import { - createRecurringProformaFromProforma, type CreateRecurringProformaFromProformaParams, + createRecurringProformaFromProforma, } from "../api/create-recurring-proforma-from-proforma.api"; +import type { RecurringProformaDetail } from "../recurring-proforma-list"; import { recurringProformaQueryKeys } from "./recurring-proforma-query-keys"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma.ts index 7550973f..e2a01066 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-create-recurring-proforma.ts @@ -2,8 +2,8 @@ import { useDataSource } from "@erp/core/hooks"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { - createRecurringProforma, type CreateRecurringProformaInput, + createRecurringProforma, } from "../api/create-recurring-proforma.api"; import { recurringProformaQueryKeys } from "./recurring-proforma-query-keys"; diff --git a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-recurring-proformas.ts b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-recurring-proformas.ts index c6d5552c..03ab4e69 100644 --- a/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-recurring-proformas.ts +++ b/modules/customer-invoices/src/web/recurring-proformas/shared/hooks/use-recurring-proformas.ts @@ -1,8 +1,8 @@ import { useDataSource } from "@erp/core/hooks"; import { useQuery } from "@tanstack/react-query"; -import { listRecurringProformas } from "../api/list-recurring-proformas.api"; import { ListRecurringProformasAdapter } from "../adapters/list-recurring-proformas.adapter"; +import { listRecurringProformas } from "../api/list-recurring-proformas.api"; import type { RecurringProformaListFilters } from "../recurring-proforma-list"; import { recurringProformaQueryKeys } from "./recurring-proforma-query-keys"; diff --git a/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-profile.controller.ts b/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-profile.controller.ts index 5af85c3a..43aaf41b 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-profile.controller.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-profile.controller.ts @@ -1,7 +1,7 @@ +import { getTaxRegimeOptions, useTaxRegimesListQuery } from "@erp/catalogs/client/tax-regimes"; import { applyValidationErrorCollection } from "@erp/core"; import { formHasAnyDirty } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; -import { getTaxRegimeOptions, useTaxRegimesListQuery } from "@erp/catalogs/client/tax-regimes"; import { type ValidationErrorCollection, isValidationErrorCollection } from "@repo/rdx-ddd"; import { focusFirstInputFormError, @@ -10,16 +10,13 @@ import { showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; -import { useEffect, useId, useMemo, type SubmitEvent } from "react"; +import { type SubmitEvent, useEffect, useId, useMemo } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../../i18n"; -import { useCompanyVerifactuProfileUpdateMutation } from "../shared"; +import { type CompanyVerifactuProfileForm, CompanyVerifactuProfileFormSchema } from "../entities"; import type { CompanyVerifactuProfile } from "../shared"; -import { - type CompanyVerifactuProfileForm, - CompanyVerifactuProfileFormSchema, -} from "../entities"; +import { useCompanyVerifactuProfileUpdateMutation } from "../shared"; import { buildCompanyVerifactuProfileForm, buildUpdateCompanyVerifactuProfileParams, diff --git a/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-settings-page-controller.ts b/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-settings-page-controller.ts index 84d3d643..f0eb9a01 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-settings-page-controller.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/controllers/use-company-verifactu-settings-page-controller.ts @@ -39,12 +39,7 @@ export const useCompanyVerifactuSettingsPageController = () => { } return null; - }, [ - feedbackState.hasBlockingError, - feedbackState.hasDataInconsistency, - profileQuery.error, - t, - ]); + }, [feedbackState.hasBlockingError, feedbackState.hasDataInconsistency, profileQuery.error, t]); return { profile: profileQuery.data, diff --git a/modules/customer-invoices/src/web/settings/verifactu/entities/company-verifactu-profile-form.schema.ts b/modules/customer-invoices/src/web/settings/verifactu/entities/company-verifactu-profile-form.schema.ts index 49726483..8299a06f 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/entities/company-verifactu-profile-form.schema.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/entities/company-verifactu-profile-form.schema.ts @@ -63,7 +63,10 @@ export const CompanyVerifactuProfileFormSchema = z }); } - if (formValues.exemptionReasonCode !== null || formValues.exemptionReasonDetail.trim() !== "") { + if ( + formValues.exemptionReasonCode !== null || + formValues.exemptionReasonDetail.trim() !== "" + ) { ctx.addIssue({ code: "custom", path: ["exemptionReasonDetail"], diff --git a/modules/customer-invoices/src/web/settings/verifactu/shared/api/update-company-verifactu-profile.api.ts b/modules/customer-invoices/src/web/settings/verifactu/shared/api/update-company-verifactu-profile.api.ts index bc5f4587..2ceb85e7 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/shared/api/update-company-verifactu-profile.api.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/shared/api/update-company-verifactu-profile.api.ts @@ -15,11 +15,12 @@ export const updateCompanyVerifactuProfile = ( dataSource: IDataSource, params: UpdateCompanyVerifactuProfileParams ): Promise => { - return dataSource.custom( - { - path: "customer-invoices/settings/verifactu", - method: "put", - data: params.data, - } - ); + return dataSource.custom< + UpdateCompanyVerifactuProfileRequestDTO, + CompanyVerifactuProfileResponseDTO + >({ + path: "customer-invoices/settings/verifactu", + method: "put", + data: params.data, + }); }; diff --git a/modules/customer-invoices/src/web/settings/verifactu/shared/hooks/use-company-verifactu-profile-update-mutation.ts b/modules/customer-invoices/src/web/settings/verifactu/shared/hooks/use-company-verifactu-profile-update-mutation.ts index 402832a7..d69bb1c5 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/shared/hooks/use-company-verifactu-profile-update-mutation.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/shared/hooks/use-company-verifactu-profile-update-mutation.ts @@ -8,7 +8,10 @@ import type { UpdateCompanyVerifactuProfileParams } from "../api"; import { updateCompanyVerifactuProfile } from "../api"; import type { CompanyVerifactuProfile } from "../entities"; -import { COMPANY_VERIFACTU_PROFILE_QUERY_KEY, UPDATE_COMPANY_VERIFACTU_PROFILE_MUTATION_KEY } from "./keys"; +import { + COMPANY_VERIFACTU_PROFILE_QUERY_KEY, + UPDATE_COMPANY_VERIFACTU_PROFILE_MUTATION_KEY, +} from "./keys"; import { toValidationErrors } from "./to-validation-errors"; export const useCompanyVerifactuProfileUpdateMutation = () => { diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-profile-form.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-profile-form.ts index 96db881b..e70d03cc 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-profile-form.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-profile-form.ts @@ -1,5 +1,5 @@ -import type { CompanyVerifactuProfile } from "../shared"; import type { CompanyVerifactuProfileForm } from "../entities"; +import type { CompanyVerifactuProfile } from "../shared"; const toCivilDate = (value: string | null): string | null => { if (!value) { diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.test.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.test.ts index 58741577..37b73de5 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.test.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.test.ts @@ -13,8 +13,7 @@ const translate = (key: string) => "settings.verifactu.page_error.notFound.title": "Configuración no disponible", "settings.verifactu.page_error.notFound.description": "La configuración solicitada no está disponible.", - "settings.verifactu.page_error.forbidden.title": - "No tienes acceso a esta configuración", + "settings.verifactu.page_error.forbidden.title": "No tienes acceso a esta configuración", "settings.verifactu.page_error.forbidden.description": "No dispones de permisos para consultar la configuración VeriFactu de la empresa activa.", "settings.verifactu.page_error.network.title": "No se pudo conectar con el servidor", @@ -26,8 +25,7 @@ const translate = (key: string) => "settings.verifactu.page_error.server.title": "No se pudo cargar la configuración", "settings.verifactu.page_error.server.description": "Se produjo un error interno al recuperar la configuración VeriFactu.", - "settings.verifactu.page_error.unavailable.title": - "Servicio temporalmente no disponible", + "settings.verifactu.page_error.unavailable.title": "Servicio temporalmente no disponible", "settings.verifactu.page_error.unavailable.description": "El servicio de VeriFactu no está disponible en este momento.", "settings.verifactu.page_error.unknown.title": "No se pudo cargar la configuración", diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.ts index 044f392c..94bab62c 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/build-company-verifactu-settings-page-error-view-model.ts @@ -2,9 +2,7 @@ import { isApiClientError } from "@erp/core/client"; import type { CompanyVerifactuSettingsPageErrorViewModel } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildErrorVariant = ( error: unknown diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/build-update-company-verifactu-profile-params.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/build-update-company-verifactu-profile-params.ts index 95c017f1..1a715aa1 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/build-update-company-verifactu-profile-params.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/build-update-company-verifactu-profile-params.ts @@ -1,5 +1,5 @@ -import type { UpdateCompanyVerifactuProfileParams } from "../shared"; import type { CompanyVerifactuProfileForm } from "../entities"; +import type { UpdateCompanyVerifactuProfileParams } from "../shared"; const normalizeNullableText = (value: string | null | undefined): string | null => { if (value === null || value === undefined) { diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/index.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/index.ts index a0461a3e..16cbaeb9 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/index.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/index.ts @@ -1,4 +1,4 @@ export * from "./build-company-verifactu-profile-form"; -export * from "./build-update-company-verifactu-profile-params"; export * from "./build-company-verifactu-settings-page-error-view-model"; +export * from "./build-update-company-verifactu-profile-params"; export * from "./resolve-company-verifactu-settings-page-feedback-state"; diff --git a/modules/customer-invoices/src/web/settings/verifactu/utils/resolve-company-verifactu-settings-page-feedback-state.ts b/modules/customer-invoices/src/web/settings/verifactu/utils/resolve-company-verifactu-settings-page-feedback-state.ts index 915d88e3..9b3ec3b9 100644 --- a/modules/customer-invoices/src/web/settings/verifactu/utils/resolve-company-verifactu-settings-page-feedback-state.ts +++ b/modules/customer-invoices/src/web/settings/verifactu/utils/resolve-company-verifactu-settings-page-feedback-state.ts @@ -22,6 +22,6 @@ export const resolveCompanyVerifactuSettingsPageFeedbackState = ({ return { isInitialLoading: isLoading || (isLoadError && isCancelled && !hasProfileData), hasBlockingError: isLoadError && !isCancelled && !hasProfileData, - hasDataInconsistency: !isLoading && !isLoadError && !hasProfileData, + hasDataInconsistency: !(isLoading || isLoadError || hasProfileData), }; }; diff --git a/modules/customers/package.json b/modules/customers/package.json index 183c6a3a..4c602141 100644 --- a/modules/customers/package.json +++ b/modules/customers/package.json @@ -59,4 +59,4 @@ "use-debounce": "^10.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/customers/src/api/application/di/customer-use-cases.di.ts b/modules/customers/src/api/application/di/customer-use-cases.di.ts index fde222c5..b9ed107f 100644 --- a/modules/customers/src/api/application/di/customer-use-cases.di.ts +++ b/modules/customers/src/api/application/di/customer-use-cases.di.ts @@ -7,8 +7,8 @@ import type { ICustomerFinder, ICustomerUpdater, } from "../services"; -import type { CustomerPaymentResolver } from "../services/catalog-resolver"; import type { ICustomerFullReadModelAssembler } from "../services/assemblers"; +import type { CustomerPaymentResolver } from "../services/catalog-resolver"; import type { ICustomerFullSnapshotBuilder, ICustomerSummarySnapshotBuilder, diff --git a/modules/customers/src/api/application/mappers/update-customer-input.mapper.ts b/modules/customers/src/api/application/mappers/update-customer-input.mapper.ts index 69bcaf2d..260b0a1d 100644 --- a/modules/customers/src/api/application/mappers/update-customer-input.mapper.ts +++ b/modules/customers/src/api/application/mappers/update-customer-input.mapper.ts @@ -53,7 +53,7 @@ export interface IUpdateCustomerInputMapper { export class UpdateCustomerInputMapper implements IUpdateCustomerInputMapper { public map( dto: UpdateCustomerByIdRequestDTO, - params: { companyId: UniqueID } + _params: { companyId: UniqueID } ): Result { try { const errors: ValidationErrorDetail[] = []; diff --git a/modules/customers/src/api/application/services/customer-default-payment-term-resolver.ts b/modules/customers/src/api/application/services/customer-default-payment-term-resolver.ts index a17952ea..50e003dd 100644 --- a/modules/customers/src/api/application/services/customer-default-payment-term-resolver.ts +++ b/modules/customers/src/api/application/services/customer-default-payment-term-resolver.ts @@ -1,8 +1,8 @@ import type { ICompanyPublicFinder } from "@erp/companies/api"; -import { UniqueID } from "@repo/rdx-ddd"; import type { UniqueID as UniqueIDType } from "@repo/rdx-ddd"; -import { Maybe, Result } from "@repo/rdx-utils"; +import { UniqueID } from "@repo/rdx-ddd"; import type { Maybe as MaybeType } from "@repo/rdx-utils"; +import { Maybe, Result } from "@repo/rdx-utils"; import type { CustomerPaymentResolver } from "./catalog-resolver"; @@ -21,9 +21,7 @@ export interface ICustomerDefaultPaymentTermResolver { * - Si lo omite, se intenta aplicar el default de Company. * - El valor final siempre se valida contra catálogos antes de persistir. */ -export class CustomerDefaultPaymentTermResolver - implements ICustomerDefaultPaymentTermResolver -{ +export class CustomerDefaultPaymentTermResolver implements ICustomerDefaultPaymentTermResolver { public constructor( private readonly deps: { companyFinder: ICompanyPublicFinder; diff --git a/modules/customers/src/api/application/use-cases/create-customer.use-case.ts b/modules/customers/src/api/application/use-cases/create-customer.use-case.ts index 3ef8cd54..4f5d08d0 100644 --- a/modules/customers/src/api/application/use-cases/create-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/create-customer.use-case.ts @@ -4,10 +4,7 @@ import { Result } from "@repo/rdx-utils"; import type { CreateCustomerRequestDTO } from "../../../common"; import type { ICreateCustomerInputMapper } from "../mappers"; -import type { - ICustomerCreator, - ICustomerDefaultPaymentTermResolver, -} from "../services"; +import type { ICustomerCreator, ICustomerDefaultPaymentTermResolver } from "../services"; import type { ICustomerFullReadModelAssembler } from "../services/assemblers"; import type { ICustomerFullSnapshotBuilder } from "../snapshot-builders"; @@ -40,11 +37,13 @@ export class CreateCustomerUseCase { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const effectivePaymentTermResult = await this.deps.defaultPaymentTermResolver.resolveForCreate({ - companyId, - explicitPaymentTermId: dto.payment_term_id === undefined ? undefined : props.paymentTermId, - transaction, - }); + const effectivePaymentTermResult = + await this.deps.defaultPaymentTermResolver.resolveForCreate({ + companyId, + explicitPaymentTermId: + dto.payment_term_id === undefined ? undefined : props.paymentTermId, + transaction, + }); if (effectivePaymentTermResult.isFailure) { return Result.fail(effectivePaymentTermResult.error); diff --git a/modules/customers/src/api/application/use-cases/update-customer.use-case.test.ts b/modules/customers/src/api/application/use-cases/update-customer.use-case.test.ts index 05057e31..03bde5b8 100644 --- a/modules/customers/src/api/application/use-cases/update-customer.use-case.test.ts +++ b/modules/customers/src/api/application/use-cases/update-customer.use-case.test.ts @@ -2,16 +2,11 @@ import assert from "node:assert/strict"; import test from "node:test"; import type { ITransactionManager } from "@erp/core/api"; -import { - CountryCode, - CurrencyCode, - LanguageCode, - Name, - UniqueID, -} from "@repo/rdx-ddd"; +import { CountryCode, CurrencyCode, LanguageCode, Name, UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { Customer, CustomerStatus } from "../../domain"; + import { UpdateCustomerUseCase } from "./update-customer.use-case"; const buildTransactionManager = (): ITransactionManager => ({ diff --git a/modules/customers/src/api/application/use-cases/update-customer.use-case.ts b/modules/customers/src/api/application/use-cases/update-customer.use-case.ts index 89f2a15c..4d73c919 100644 --- a/modules/customers/src/api/application/use-cases/update-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/update-customer.use-case.ts @@ -3,12 +3,11 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import type { UpdateCustomerByIdRequestDTO } from "../../../common/dto"; -import type { Customer } from "../../domain"; -import type { CustomerPatchProps } from "../../domain"; +import type { Customer, CustomerPatchProps } from "../../domain"; import type { IUpdateCustomerInputMapper } from "../mappers"; import type { ICustomerFinder, ICustomerUpdater } from "../services"; -import type { CustomerPaymentResolver } from "../services/catalog-resolver"; import type { ICustomerFullReadModelAssembler } from "../services/assemblers"; +import type { CustomerPaymentResolver } from "../services/catalog-resolver"; import type { ICustomerFullSnapshotBuilder } from "../snapshot-builders"; type UpdateCustomerUseCaseInput = { diff --git a/modules/customers/src/api/domain/aggregates/customer.aggregate.ts b/modules/customers/src/api/domain/aggregates/customer.aggregate.ts index 5b61ad1a..79a3d5d0 100644 --- a/modules/customers/src/api/domain/aggregates/customer.aggregate.ts +++ b/modules/customers/src/api/domain/aggregates/customer.aggregate.ts @@ -140,7 +140,7 @@ export class Customer extends AggregateRoot implements IC return Result.ok(contact); } - private static validateCreateProps(props: ICustomerCreateProps): Result { + private static validateCreateProps(_props: ICustomerCreateProps): Result { return Result.ok(); } diff --git a/modules/customers/src/api/domain/value-objects/index.ts b/modules/customers/src/api/domain/value-objects/index.ts index 968969b6..d47d89cd 100644 --- a/modules/customers/src/api/domain/value-objects/index.ts +++ b/modules/customers/src/api/domain/value-objects/index.ts @@ -2,4 +2,4 @@ export * from "./customer-address-type.vo"; export * from "./customer-number.vo"; export * from "./customer-serie.vo"; export * from "./customer-status.vo"; -export * from "./customer-taxes.vo"; \ No newline at end of file +export * from "./customer-taxes.vo"; diff --git a/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/domain/sequelize-customer-domain.mapper.ts b/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/domain/sequelize-customer-domain.mapper.ts index 3148d3f1..a177c6ba 100644 --- a/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/domain/sequelize-customer-domain.mapper.ts +++ b/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/domain/sequelize-customer-domain.mapper.ts @@ -31,7 +31,7 @@ export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper< CustomerCreationAttributes, Customer > { - public mapToDomain(source: CustomerModel, params?: MapperParamsType): Result { + public mapToDomain(source: CustomerModel, _params?: MapperParamsType): Result { try { const errors: ValidationErrorDetail[] = []; @@ -251,7 +251,7 @@ export class SequelizeCustomerDomainMapper extends SequelizeDomainMapper< public mapToPersistence( source: Customer, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const customerValues: Partial = { id: source.id.toPrimitive(), diff --git a/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-customer-summary.mapper.ts b/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-customer-summary.mapper.ts index 42f7d759..8a3f8447 100644 --- a/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-customer-summary.mapper.ts +++ b/modules/customers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-customer-summary.mapper.ts @@ -32,7 +32,7 @@ export class SequelizeCustomerSummaryMapper extends SequelizeQueryMapper< > { public mapToReadModel( raw: CustomerModel, - params?: MapperParamsType + _params?: MapperParamsType ): Result { const errors: ValidationErrorDetail[] = []; @@ -146,7 +146,7 @@ export class SequelizeCustomerSummaryMapper extends SequelizeQueryMapper< errors ); - const legalRecord = extractOrPushError( + const _legalRecord = extractOrPushError( maybeFromNullableResult(raw.legal_record, (value) => TextValue.create(value)), "legal_record", errors @@ -190,7 +190,7 @@ export class SequelizeCustomerSummaryMapper extends SequelizeQueryMapper< return Result.ok({ id: customerId!, companyId: companyId!, - isActive: status!.isActive(), + isActive: status?.isActive(), reference: reference!, isCompany: isCompany, diff --git a/modules/customers/src/api/infrastructure/persistence/sequelize/repositories/customer.repository.ts b/modules/customers/src/api/infrastructure/persistence/sequelize/repositories/customer.repository.ts index 9151354d..634c7f78 100644 --- a/modules/customers/src/api/infrastructure/persistence/sequelize/repositories/customer.repository.ts +++ b/modules/customers/src/api/infrastructure/persistence/sequelize/repositories/customer.repository.ts @@ -1,12 +1,12 @@ import { EntityNotFoundError, SequelizeRepository, translateSequelizeError } from "@erp/core/api"; import { type Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server"; -import { UniqueID } from "@repo/rdx-ddd"; import type { TINNumber } from "@repo/rdx-ddd"; +import { UniqueID } from "@repo/rdx-ddd"; import { type Collection, Result } from "@repo/rdx-utils"; import { - Op, type FindOptions, type InferAttributes, + Op, type OrderItem, type Sequelize, type Transaction, @@ -40,7 +40,9 @@ export class CustomerRepository ): Promise> { try { const uniqueIds = [...new Set(ids.map((id) => id.toPrimitive()))]; - if (uniqueIds.length === 0) return Result.ok([]); + if (uniqueIds.length === 0) { + return Result.ok([]); + } const rows = await CustomerModel.findAll({ attributes: ["id", "name", "tin"], @@ -53,7 +55,9 @@ export class CustomerRepository const summaries: CustomerPublicSummary[] = []; for (const row of rows) { const id = UniqueID.create(row.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } summaries.push({ id: id.data, name: row.name, tin: row.tin ?? null }); } return Result.ok(summaries); diff --git a/modules/customers/src/common/features/customer-selection/controllers/use-select-customer-dialog-controller.ts b/modules/customers/src/common/features/customer-selection/controllers/use-select-customer-dialog-controller.ts index a9dfdaf4..74c2011b 100644 --- a/modules/customers/src/common/features/customer-selection/controllers/use-select-customer-dialog-controller.ts +++ b/modules/customers/src/common/features/customer-selection/controllers/use-select-customer-dialog-controller.ts @@ -39,7 +39,9 @@ export const useSelectCustomerDialogController = ( const nextValue = value.trim().replace(/\s+/g, " "); setSearch((prev) => { - if (prev === nextValue) return prev; + if (prev === nextValue) { + return prev; + } // Sólo si la búsqueda realmente cambia, // reseteamos la página a 0 para evitar inconsistencias @@ -50,7 +52,9 @@ export const useSelectCustomerDialogController = ( const setPageSizeValue = (value: number) => { setPageSize((prev) => { - if (prev === value) return prev; + if (prev === value) { + return prev; + } // Sólo si el tamaño de página realmente cambia, // reseteamos la página a 0 para evitar inconsistencias diff --git a/modules/customers/src/common/features/customer-selection/utils/index.ts b/modules/customers/src/common/features/customer-selection/utils/index.ts index 42234997..982a1017 100644 --- a/modules/customers/src/common/features/customer-selection/utils/index.ts +++ b/modules/customers/src/common/features/customer-selection/utils/index.ts @@ -1 +1 @@ -export * from './build-customer-selection-option'; +export * from "./build-customer-selection-option"; diff --git a/modules/customers/src/common/features/get-customer/entities/index.ts b/modules/customers/src/common/features/get-customer/entities/index.ts index 3d233006..aed467e0 100644 --- a/modules/customers/src/common/features/get-customer/entities/index.ts +++ b/modules/customers/src/common/features/get-customer/entities/index.ts @@ -1 +1 @@ -export * from './customer-data.entity'; +export * from "./customer-data.entity"; diff --git a/modules/customers/src/common/features/quick-create-customer/entities/index.ts b/modules/customers/src/common/features/quick-create-customer/entities/index.ts index 3f2f6e0a..a72ab7e8 100644 --- a/modules/customers/src/common/features/quick-create-customer/entities/index.ts +++ b/modules/customers/src/common/features/quick-create-customer/entities/index.ts @@ -1,3 +1,3 @@ -export * from './quick-customer-create-form.entity'; +export * from "./quick-customer-create-form.entity"; export * from "./quick-customer-create-form.schema"; -export * from './quick-customer-create-result.entity'; +export * from "./quick-customer-create-result.entity"; diff --git a/modules/customers/src/common/features/quick-create-customer/ui/dialogs/quick-create-customer-dialog.test.tsx b/modules/customers/src/common/features/quick-create-customer/ui/dialogs/quick-create-customer-dialog.test.tsx index 29781865..55694540 100644 --- a/modules/customers/src/common/features/quick-create-customer/ui/dialogs/quick-create-customer-dialog.test.tsx +++ b/modules/customers/src/common/features/quick-create-customer/ui/dialogs/quick-create-customer-dialog.test.tsx @@ -1,10 +1,12 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { FormProvider, useForm } from "react-hook-form"; import type { QuickCustomerCreateForm } from "../../entities"; import { buildQuickCustomerCreateDefaultValues } from "../../utils"; + import { QuickCreateCustomerDialog } from "./quick-create-customer-dialog"; const renderDialog = () => { diff --git a/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.test.ts b/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.test.ts index 77d3d3ae..64d11c35 100644 --- a/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.test.ts +++ b/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.test.ts @@ -30,16 +30,20 @@ test("construye el request oficial de customers a partir del formulario rápido" }); test("permite sobrescribir los defaults de contexto sin pedirlos al usuario", () => { - const params = buildCreateCustomerParamsFromQuickForm("bf0d3f59-7b96-4ab6-a74b-2f2b8bcd0fb5", { - isCompany: true, - name: "Acme", - tin: "B12345678", - primaryEmail: "", - }, { - country: "pt", - languageCode: "EN", - currencyCode: "usd", - }); + const params = buildCreateCustomerParamsFromQuickForm( + "bf0d3f59-7b96-4ab6-a74b-2f2b8bcd0fb5", + { + isCompany: true, + name: "Acme", + tin: "B12345678", + primaryEmail: "", + }, + { + country: "pt", + languageCode: "EN", + currencyCode: "usd", + } + ); assert.equal(params.id, "bf0d3f59-7b96-4ab6-a74b-2f2b8bcd0fb5"); assert.deepEqual(params.data, { diff --git a/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.ts b/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.ts index 371df4c0..36769804 100644 --- a/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.ts +++ b/modules/customers/src/common/features/quick-create-customer/utils/build-create-customer-params-from-quick-form.ts @@ -1,5 +1,6 @@ -import { buildCustomerCreateDefault } from "../../../../web/create/utils/build-customer-create-defaults"; import { toNullableText, toRequiredText } from "@erp/core/client"; + +import { buildCustomerCreateDefault } from "../../../../web/create/utils/build-customer-create-defaults"; import type { CreateCustomerParams } from "../../../../web/shared"; import type { QuickCustomerCreateForm } from "../entities"; diff --git a/modules/customers/src/common/features/quick-create-customer/utils/build-quick-customer-create-default-values.ts b/modules/customers/src/common/features/quick-create-customer/utils/build-quick-customer-create-default-values.ts index fbab081b..9cfb6244 100644 --- a/modules/customers/src/common/features/quick-create-customer/utils/build-quick-customer-create-default-values.ts +++ b/modules/customers/src/common/features/quick-create-customer/utils/build-quick-customer-create-default-values.ts @@ -1,5 +1,5 @@ -import type { QuickCustomerCreateForm } from "../entities"; import { buildCustomerCreateDefault } from "../../../../web/create/utils/build-customer-create-defaults"; +import type { QuickCustomerCreateForm } from "../entities"; export const buildQuickCustomerCreateDefaultValues = (): QuickCustomerCreateForm => { const defaults = buildCustomerCreateDefault(); diff --git a/modules/customers/src/common/features/quick-create-customer/utils/index.ts b/modules/customers/src/common/features/quick-create-customer/utils/index.ts index d546092f..9671da4e 100644 --- a/modules/customers/src/common/features/quick-create-customer/utils/index.ts +++ b/modules/customers/src/common/features/quick-create-customer/utils/index.ts @@ -1,2 +1,2 @@ -export * from './build-create-customer-params-from-quick-form'; -export * from './build-quick-customer-create-default-values'; +export * from "./build-create-customer-params-from-quick-form"; +export * from "./build-quick-customer-create-default-values"; diff --git a/modules/customers/src/web/create/controllers/use-customer-create-fiscal-controller.ts b/modules/customers/src/web/create/controllers/use-customer-create-fiscal-controller.ts index 41990f82..ad374dc6 100644 --- a/modules/customers/src/web/create/controllers/use-customer-create-fiscal-controller.ts +++ b/modules/customers/src/web/create/controllers/use-customer-create-fiscal-controller.ts @@ -1,14 +1,11 @@ -import type { CompanyResponseDTO } from "@erp/companies"; -import { useDataSource } from "@erp/core/hooks"; import { getPaymentMethodOptions, usePaymentMethodsListQuery, } from "@erp/catalogs/client/payment-methods"; -import { - getPaymentTermOptions, - usePaymentTermListQuery, -} from "@erp/catalogs/client/payment-terms"; +import { getPaymentTermOptions, usePaymentTermListQuery } from "@erp/catalogs/client/payment-terms"; import { getTaxRegimeOptions, useTaxRegimesListQuery } from "@erp/catalogs/client/tax-regimes"; +import type { CompanyResponseDTO } from "@erp/companies"; +import { useDataSource } from "@erp/core/hooks"; import { useIdentityAuthSession } from "@erp/identity/client"; import { useQuery } from "@tanstack/react-query"; import { useMemo } from "react"; diff --git a/modules/customers/src/web/create/entities/customer-create-data.entity.ts b/modules/customers/src/web/create/entities/customer-create-data.entity.ts index 3c1fa134..cd37aace 100644 --- a/modules/customers/src/web/create/entities/customer-create-data.entity.ts +++ b/modules/customers/src/web/create/entities/customer-create-data.entity.ts @@ -8,7 +8,7 @@ * - no es DTO backend. */ -export type CustomerCreateData = { +export type CustomerCreateData = { isCompany: boolean; name: string; tradeName?: string; diff --git a/modules/customers/src/web/create/ui/blocks/customer-create-header.test.tsx b/modules/customers/src/web/create/ui/blocks/customer-create-header.test.tsx index ec9d9e17..caa8a11e 100644 --- a/modules/customers/src/web/create/ui/blocks/customer-create-header.test.tsx +++ b/modules/customers/src/web/create/ui/blocks/customer-create-header.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; diff --git a/modules/customers/src/web/create/ui/editor/customer-create-editor.test.tsx b/modules/customers/src/web/create/ui/editor/customer-create-editor.test.tsx index 72b21cd7..42909603 100644 --- a/modules/customers/src/web/create/ui/editor/customer-create-editor.test.tsx +++ b/modules/customers/src/web/create/ui/editor/customer-create-editor.test.tsx @@ -1,10 +1,12 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { FormProvider, useForm } from "react-hook-form"; import type { CustomerCreateForm } from "../../entities"; import { buildCustomerCreateDefault } from "../../utils"; + import { CustomerCreateEditorForm } from "./customer-create-editor"; const renderForm = () => { diff --git a/modules/customers/src/web/create/utils/build-create-customer-params.ts b/modules/customers/src/web/create/utils/build-create-customer-params.ts index 51aa3013..2740c8d7 100644 --- a/modules/customers/src/web/create/utils/build-create-customer-params.ts +++ b/modules/customers/src/web/create/utils/build-create-customer-params.ts @@ -62,23 +62,49 @@ export const buildCreateCustomerParams = ( } // --- address --- - if (newData.street !== undefined) data.address.street = newData.street; - if (newData.street2 !== undefined) data.address.street2 = newData.street2; - if (newData.city !== undefined) data.address.city = newData.city; - if (newData.province !== undefined) data.address.province = newData.province; - if (newData.postalCode !== undefined) data.address.postal_code = newData.postalCode; + if (newData.street !== undefined) { + data.address.street = newData.street; + } + if (newData.street2 !== undefined) { + data.address.street2 = newData.street2; + } + if (newData.city !== undefined) { + data.address.city = newData.city; + } + if (newData.province !== undefined) { + data.address.province = newData.province; + } + if (newData.postalCode !== undefined) { + data.address.postal_code = newData.postalCode; + } // --- contact --- const contact: Record = {}; - if (newData.primaryEmail !== undefined) contact.email_primary = newData.primaryEmail; - if (newData.secondaryEmail !== undefined) contact.email_secondary = newData.secondaryEmail; - if (newData.primaryPhone !== undefined) contact.phone_primary = newData.primaryPhone; - if (newData.secondaryPhone !== undefined) contact.phone_secondary = newData.secondaryPhone; - if (newData.primaryMobile !== undefined) contact.mobile_primary = newData.primaryMobile; - if (newData.secondaryMobile !== undefined) contact.mobile_secondary = newData.secondaryMobile; - if (newData.fax !== undefined) contact.fax = newData.fax; - if (newData.website !== undefined) contact.website = newData.website; + if (newData.primaryEmail !== undefined) { + contact.email_primary = newData.primaryEmail; + } + if (newData.secondaryEmail !== undefined) { + contact.email_secondary = newData.secondaryEmail; + } + if (newData.primaryPhone !== undefined) { + contact.phone_primary = newData.primaryPhone; + } + if (newData.secondaryPhone !== undefined) { + contact.phone_secondary = newData.secondaryPhone; + } + if (newData.primaryMobile !== undefined) { + contact.mobile_primary = newData.primaryMobile; + } + if (newData.secondaryMobile !== undefined) { + contact.mobile_secondary = newData.secondaryMobile; + } + if (newData.fax !== undefined) { + contact.fax = newData.fax; + } + if (newData.website !== undefined) { + contact.website = newData.website; + } if (Object.keys(contact).length > 0) { data.contact = contact; diff --git a/modules/customers/src/web/create/utils/build-customer-create-data.ts b/modules/customers/src/web/create/utils/build-customer-create-data.ts index 5d62e80f..a747afc9 100644 --- a/modules/customers/src/web/create/utils/build-customer-create-data.ts +++ b/modules/customers/src/web/create/utils/build-customer-create-data.ts @@ -53,11 +53,11 @@ export const buildCustomerCreateData = ( paymentMethodId: toNullableText(formData.paymentMethodId) ?? undefined, paymentTermId: - resolvedPaymentTermId !== null - ? resolvedPaymentTermId - : options?.isPaymentTermDirty && options.companyDefaultPaymentTermId + resolvedPaymentTermId === null + ? options?.isPaymentTermDirty && options.companyDefaultPaymentTermId ? null - : undefined, + : undefined + : resolvedPaymentTermId, taxRegimeCode: toNullableText(formData.taxRegimeCode) ?? undefined, usesEquivalenceSurcharge: diff --git a/modules/customers/src/web/list/controllers/use-list-customers-page.controller.ts b/modules/customers/src/web/list/controllers/use-list-customers-page.controller.ts index 7cf83ee8..7374f931 100644 --- a/modules/customers/src/web/list/controllers/use-list-customers-page.controller.ts +++ b/modules/customers/src/web/list/controllers/use-list-customers-page.controller.ts @@ -50,7 +50,7 @@ export const useListCustomersPageController = () => { } return buildCustomerListRefreshErrorViewModel(listCtrl.error, (key) => t(key)); - }, [listCtrl.error, listCtrl.hasUsableData, listCtrl.isError, t]); + }, [listCtrl.error, listCtrl.hasUsableData, listCtrl.isError, t, listCtrl]); const feedbackState = resolveCustomerListFeedbackState(listCtrl); diff --git a/modules/customers/src/web/list/controllers/use-list-customers.controller.ts b/modules/customers/src/web/list/controllers/use-list-customers.controller.ts index 3c162d79..9223e847 100644 --- a/modules/customers/src/web/list/controllers/use-list-customers.controller.ts +++ b/modules/customers/src/web/list/controllers/use-list-customers.controller.ts @@ -132,7 +132,9 @@ export const useListCustomersController = () => { const nextValue = (value || "all") as CustomerListStatusFilter; setStatusFilter((prev) => { - if (prev === nextValue) return prev; + if (prev === nextValue) { + return prev; + } // Reset page to 1 when status filter changes setSearchParams((prev) => { @@ -151,7 +153,9 @@ export const useListCustomersController = () => { const nextValue = value.trim().replace(/\s+/g, " "); setSearch((prev) => { - if (prev === nextValue) return prev; + if (prev === nextValue) { + return prev; + } // Reset page to 1 when search changes setSearchParams((prev) => { @@ -179,7 +183,9 @@ export const useListCustomersController = () => { const setPageSizeValue = useCallback( (value: number) => { - if (pageSize === value) return; + if (pageSize === value) { + return; + } // Reset page to 1 and update pageSize when it changes setSearchParams((prev) => { diff --git a/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-proformas-section.tsx b/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-proformas-section.tsx index efbd9c03..ff952183 100644 --- a/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-proformas-section.tsx +++ b/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-proformas-section.tsx @@ -33,7 +33,7 @@ const formatCurrency = (amount: number) => { }).format(amount); }; -const formatDate = (dateString: string) => { +const _formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString("es-ES", { day: "2-digit", month: "short", diff --git a/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-summary-panel.test.tsx b/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-summary-panel.test.tsx index 272ff120..86606cb9 100644 --- a/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-summary-panel.test.tsx +++ b/modules/customers/src/web/list/ui/blocks/customer-summary-panel/customer-summary-panel.test.tsx @@ -1,9 +1,11 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import type { Customer } from "../../../../shared"; + import { CustomerSummaryPanel } from "./customer-summary-panel"; const mockCustomer: Customer = { diff --git a/modules/customers/src/web/list/ui/blocks/customers-grid/use-customer-grid-columns.test.tsx b/modules/customers/src/web/list/ui/blocks/customers-grid/use-customer-grid-columns.test.tsx index d4a2a849..6d31d812 100644 --- a/modules/customers/src/web/list/ui/blocks/customers-grid/use-customer-grid-columns.test.tsx +++ b/modules/customers/src/web/list/ui/blocks/customers-grid/use-customer-grid-columns.test.tsx @@ -1,12 +1,13 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { isValidElement } from "react"; -import { renderToStaticMarkup } from "react-dom/server"; -import type { CellContext } from "@tanstack/react-table"; import { RdxRowActions, type RdxRowActionsProps } from "@repo/rdx-ui/components"; +import type { CellContext } from "@tanstack/react-table"; +import { isValidElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; import type { CustomerListRow } from "../../../../shared"; + import { useCustomersGridColumns } from "./use-customer-grid-columns"; const customerFixture: CustomerListRow = { diff --git a/modules/customers/src/web/list/ui/pages/list-customers-page-view.test.tsx b/modules/customers/src/web/list/ui/pages/list-customers-page-view.test.tsx index e03b373d..488d987e 100644 --- a/modules/customers/src/web/list/ui/pages/list-customers-page-view.test.tsx +++ b/modules/customers/src/web/list/ui/pages/list-customers-page-view.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; diff --git a/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.test.ts b/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.test.ts index 95032094..a0da60fd 100644 --- a/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.test.ts +++ b/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.test.ts @@ -16,17 +16,14 @@ const translate = (key: string) => "list.errors.page.network.title": "No se pudo conectar con el servidor", "list.errors.page.network.description": "Comprueba tu conexión e inténtalo de nuevo.", "list.errors.page.timeout.title": "La carga está tardando demasiado", - "list.errors.page.timeout.description": - "El servidor no respondió dentro del tiempo esperado.", + "list.errors.page.timeout.description": "El servidor no respondió dentro del tiempo esperado.", "list.errors.page.server.title": "No se pudo cargar la lista de clientes", - "list.errors.page.server.description": - "Se produjo un error interno al recuperar los clientes.", + "list.errors.page.server.description": "Se produjo un error interno al recuperar los clientes.", "list.errors.page.unavailable.title": "Servicio temporalmente no disponible", "list.errors.page.unavailable.description": "El servicio de clientes no está disponible en este momento.", "list.errors.page.unknown.title": "No se pudo cargar la lista de clientes", - "list.errors.page.unknown.description": - "Ha ocurrido un error inesperado al cargar el listado.", + "list.errors.page.unknown.description": "Ha ocurrido un error inesperado al cargar el listado.", "list.errors.refresh.title": "No se pudo actualizar la lista de clientes", "list.errors.refresh.message": "Se muestran los últimos datos disponibles mientras vuelves a intentarlo.", diff --git a/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.ts b/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.ts index c234ba12..35f9ecde 100644 --- a/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.ts +++ b/modules/customers/src/web/list/utils/build-customer-list-page-error-view-model.ts @@ -5,9 +5,7 @@ import type { CustomerListRefreshErrorViewModel, } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildVariant = (error: unknown): CustomerListPageErrorViewModel["variant"] | null => { if (!isApiClientError(error)) { diff --git a/modules/customers/src/web/list/utils/error-alert-deduplication.test.ts b/modules/customers/src/web/list/utils/error-alert-deduplication.test.ts index 3d438078..94d404fa 100644 --- a/modules/customers/src/web/list/utils/error-alert-deduplication.test.ts +++ b/modules/customers/src/web/list/utils/error-alert-deduplication.test.ts @@ -5,14 +5,8 @@ import test from "node:test"; import { fileURLToPath } from "node:url"; const currentDir = dirname(fileURLToPath(import.meta.url)); -const sharedErrorAlertPath = resolve( - currentDir, - "../../shared/ui/components/error-alert.tsx" -); -const sharedComponentsIndexPath = resolve( - currentDir, - "../../shared/ui/components/index.ts" -); +const sharedErrorAlertPath = resolve(currentDir, "../../shared/ui/components/error-alert.tsx"); +const sharedComponentsIndexPath = resolve(currentDir, "../../shared/ui/components/index.ts"); const listPagePath = resolve(currentDir, "../ui/pages/list-customers-page.tsx"); test("el módulo customers ya no mantiene un ErrorAlert local duplicado", () => { diff --git a/modules/customers/src/web/shared/adapters/get-customer-by-id.adapter.ts b/modules/customers/src/web/shared/adapters/get-customer-by-id.adapter.ts index 9374ef56..3034a58e 100644 --- a/modules/customers/src/web/shared/adapters/get-customer-by-id.adapter.ts +++ b/modules/customers/src/web/shared/adapters/get-customer-by-id.adapter.ts @@ -11,7 +11,7 @@ import type { Customer } from "../entities"; */ export const GetCustomerByIdAdapter = { - fromDTO(dto: GetCustomerByIdResult, context?: unknown): Customer { + fromDTO(dto: GetCustomerByIdResult, _context?: unknown): Customer { return { id: dto.id, companyId: dto.company_id, diff --git a/modules/customers/src/web/shared/adapters/list-customers.adapter.ts b/modules/customers/src/web/shared/adapters/list-customers.adapter.ts index 16bea312..54d80d31 100644 --- a/modules/customers/src/web/shared/adapters/list-customers.adapter.ts +++ b/modules/customers/src/web/shared/adapters/list-customers.adapter.ts @@ -39,7 +39,7 @@ export const ListCustomersAdapter = { type CustomerListItemOutput = ListCustomersResult["items"][number]; const ListCustomersRowAdapter = { - fromDTO(rowDto: CustomerListItemOutput, context?: unknown): CustomerListRow { + fromDTO(rowDto: CustomerListItemOutput, _context?: unknown): CustomerListRow { return { id: rowDto.id, companyId: rowDto.company_id, diff --git a/modules/customers/src/web/shared/api/create-customer.api.ts b/modules/customers/src/web/shared/api/create-customer.api.ts index 44eb4245..620967e0 100644 --- a/modules/customers/src/web/shared/api/create-customer.api.ts +++ b/modules/customers/src/web/shared/api/create-customer.api.ts @@ -21,9 +21,13 @@ export type CreateCustomerResult = CreateCustomerResponseDTO; export function createCustomer(dataSource: IDataSource, params: CreateCustomerParams) { const { id, data } = params; - if (!id) throw new Error("customerId is required"); + if (!id) { + throw new Error("customerId is required"); + } - if (id !== data.id) throw new Error("customerId in params must match id in data"); + if (id !== data.id) { + throw new Error("customerId in params must match id in data"); + } return dataSource.createOne( "customers", diff --git a/modules/customers/src/web/shared/api/delete-customer-by-id.api.ts b/modules/customers/src/web/shared/api/delete-customer-by-id.api.ts index 032bd4a0..f71bcc98 100644 --- a/modules/customers/src/web/shared/api/delete-customer-by-id.api.ts +++ b/modules/customers/src/web/shared/api/delete-customer-by-id.api.ts @@ -14,7 +14,7 @@ export interface DeleteCustomerByIdParams { signal?: AbortSignal; } -export type DeleteCustomerByIdResult = void; +export type DeleteCustomerByIdResult = undefined; export function deleteCustomerById( dataSource: IDataSource, @@ -22,7 +22,9 @@ export function deleteCustomerById( ): Promise { const { id, signal } = params; - if (!id) throw new Error("customerId is required"); + if (!id) { + throw new Error("customerId is required"); + } return dataSource.deleteOne("customers", id, { signal, diff --git a/modules/customers/src/web/shared/api/get-customer-by-id.api.ts b/modules/customers/src/web/shared/api/get-customer-by-id.api.ts index c9ffb55a..52e09127 100644 --- a/modules/customers/src/web/shared/api/get-customer-by-id.api.ts +++ b/modules/customers/src/web/shared/api/get-customer-by-id.api.ts @@ -23,6 +23,8 @@ export function getCustomerById( params: GetCustomerByIdParams ): Promise { const { id, signal } = params; - if (!id) throw new Error("customerId is required"); + if (!id) { + throw new Error("customerId is required"); + } return dataSource.getOne("customers", id, { signal }); } diff --git a/modules/customers/src/web/shared/api/update-customer-by-id.api.ts b/modules/customers/src/web/shared/api/update-customer-by-id.api.ts index 14c19749..b3e6d8a6 100644 --- a/modules/customers/src/web/shared/api/update-customer-by-id.api.ts +++ b/modules/customers/src/web/shared/api/update-customer-by-id.api.ts @@ -24,7 +24,9 @@ export function updateCustomerById( ): Promise { const { id, data } = params; - if (!id) throw new Error("customerId is required"); + if (!id) { + throw new Error("customerId is required"); + } return dataSource.updateOne( "customers", diff --git a/modules/customers/src/web/shared/hooks/customer-cache-strategy.ts b/modules/customers/src/web/shared/hooks/customer-cache-strategy.ts index c93037f6..f9a37633 100644 --- a/modules/customers/src/web/shared/hooks/customer-cache-strategy.ts +++ b/modules/customers/src/web/shared/hooks/customer-cache-strategy.ts @@ -70,10 +70,14 @@ export function upsertCustomerInListCaches( for (const key of keys) { const page = queryClient.getQueryData(key); - if (!page) continue; + if (!page) { + continue; + } const index = page.items.findIndex((row) => row.id === customer.id); - if (index === -1) continue; + if (index === -1) { + continue; + } const nextItems = page.items.slice(); nextItems[index] = { @@ -98,7 +102,9 @@ export function removeCustomerFromListCaches( })); for (const { key, page } of snapshots) { - if (!page) continue; + if (!page) { + continue; + } queryClient.setQueryData(key, { ...page, @@ -144,7 +150,9 @@ export function rollbackDeleteCustomerOptimisticUpdate( queryClient: QueryClient, context?: DeleteCustomerCacheContext ) { - if (!context) return; + if (!context) { + return; + } restoreCustomerListCaches(queryClient, context.snapshots); } diff --git a/modules/customers/src/web/shared/hooks/use-customer-get-query.ts b/modules/customers/src/web/shared/hooks/use-customer-get-query.ts index 795fe822..f341d924 100644 --- a/modules/customers/src/web/shared/hooks/use-customer-get-query.ts +++ b/modules/customers/src/web/shared/hooks/use-customer-get-query.ts @@ -22,7 +22,9 @@ export const useCustomerGetQuery = ( return useQuery({ queryKey: CUSTOMER_QUERY_KEY(id), queryFn: async ({ signal }) => { - if (!id) throw new Error("customerId is required"); + if (!id) { + throw new Error("customerId is required"); + } const dto: GetCustomerByIdResult = await getCustomerById(dataSource, { id: id!, diff --git a/modules/customers/src/web/update/controllers/use-customer-update-fiscal-controller.ts b/modules/customers/src/web/update/controllers/use-customer-update-fiscal-controller.ts index 44028c5e..e6b8da11 100644 --- a/modules/customers/src/web/update/controllers/use-customer-update-fiscal-controller.ts +++ b/modules/customers/src/web/update/controllers/use-customer-update-fiscal-controller.ts @@ -2,14 +2,12 @@ import { getPaymentMethodOptions, usePaymentMethodsListQuery, } from "@erp/catalogs/client/payment-methods"; -import { - getPaymentTermOptions, - usePaymentTermListQuery, -} from "@erp/catalogs/client/payment-terms"; +import { getPaymentTermOptions, usePaymentTermListQuery } from "@erp/catalogs/client/payment-terms"; import { getTaxRegimeOptions, useTaxRegimesListQuery } from "@erp/catalogs/client/tax-regimes"; -import type { Customer } from "../../shared"; import { useMemo } from "react"; + import { useTranslation } from "../../i18n"; +import type { Customer } from "../../shared"; export const useCustomerUpdateFiscalController = (params?: { currentPaymentTerm?: Customer["paymentTerm"]; diff --git a/modules/customers/src/web/update/controllers/use-customer-update-page.controller.ts b/modules/customers/src/web/update/controllers/use-customer-update-page.controller.ts index c0e61eef..42905d34 100644 --- a/modules/customers/src/web/update/controllers/use-customer-update-page.controller.ts +++ b/modules/customers/src/web/update/controllers/use-customer-update-page.controller.ts @@ -1,5 +1,5 @@ -import { useCallback, useMemo } from "react"; import { useUrlParamId } from "@erp/core/hooks"; +import { useCallback, useMemo } from "react"; import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom"; import { useTranslation } from "../../i18n"; @@ -8,6 +8,7 @@ import { buildCustomerUpdatePageErrorViewModel, resolveCustomerUpdatePageFeedbackState, } from "../utils"; + import { useCustomerUpdateController } from "./use-customer-update.controller"; /** diff --git a/modules/customers/src/web/update/controllers/use-customer-update.controller.ts b/modules/customers/src/web/update/controllers/use-customer-update.controller.ts index 321b8d6d..791faddc 100644 --- a/modules/customers/src/web/update/controllers/use-customer-update.controller.ts +++ b/modules/customers/src/web/update/controllers/use-customer-update.controller.ts @@ -74,7 +74,9 @@ export const useCustomerUpdateController = ( } = useCustomerUpdateMutation(); const initialValues = useMemo(() => { - if (!customerData) return buildCustomerUpdateDefault(); + if (!customerData) { + return buildCustomerUpdateDefault(); + } return mapCustomerToCustomerUpdateForm(customerData); }, [customerData]); @@ -88,7 +90,9 @@ export const useCustomerUpdateController = ( /** Reiniciar el form al recibir datos */ useEffect(() => { - if (!customerData) return; + if (!customerData) { + return; + } console.log("Reseteando form con datos del cliente:", customerData); form.reset(mapCustomerToCustomerUpdateForm(customerData), { diff --git a/modules/customers/src/web/update/entities/index.ts b/modules/customers/src/web/update/entities/index.ts index 623b99a7..576d446c 100644 --- a/modules/customers/src/web/update/entities/index.ts +++ b/modules/customers/src/web/update/entities/index.ts @@ -1,4 +1,4 @@ export * from "./customer-update-form.entity"; export * from "./customer-update-form.schema"; -export * from "./customer-update-patch.entity"; export * from "./customer-update-page-error-view-model.entity"; +export * from "./customer-update-patch.entity"; diff --git a/modules/customers/src/web/update/ui/editor/customer-edit-form.test.tsx b/modules/customers/src/web/update/ui/editor/customer-edit-form.test.tsx index 70f3a0bd..11581212 100644 --- a/modules/customers/src/web/update/ui/editor/customer-edit-form.test.tsx +++ b/modules/customers/src/web/update/ui/editor/customer-edit-form.test.tsx @@ -1,10 +1,12 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { renderToStaticMarkup } from "react-dom/server"; import { FormProvider, useForm } from "react-hook-form"; import type { CustomerUpdateForm } from "../../entities"; import { buildCustomerUpdateDefault } from "../../utils"; + import { CustomerUpdateEditorForm } from "./customer-edit-form"; const renderForm = () => { diff --git a/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.test.ts b/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.test.ts index 76d1fe4b..773c46f4 100644 --- a/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.test.ts +++ b/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.test.ts @@ -13,16 +13,13 @@ const translate = (key: string) => "update.page_error.notFound.title": "Cliente no encontrado", "update.page_error.notFound.description": "El cliente ya no existe o no está disponible.", "update.page_error.forbidden.title": "No tienes acceso a este cliente", - "update.page_error.forbidden.description": - "No dispones de permisos para editar este cliente.", + "update.page_error.forbidden.description": "No dispones de permisos para editar este cliente.", "update.page_error.network.title": "No se pudo conectar con el servidor", "update.page_error.network.description": "Comprueba tu conexión e inténtalo de nuevo.", "update.page_error.timeout.title": "La carga está tardando demasiado", - "update.page_error.timeout.description": - "El servidor no respondió dentro del tiempo esperado.", + "update.page_error.timeout.description": "El servidor no respondió dentro del tiempo esperado.", "update.page_error.server.title": "No se pudo cargar el cliente", - "update.page_error.server.description": - "Se produjo un error interno al recuperar el cliente.", + "update.page_error.server.description": "Se produjo un error interno al recuperar el cliente.", "update.page_error.unavailable.title": "Servicio temporalmente no disponible", "update.page_error.unavailable.description": "El servicio de clientes no está disponible en este momento.", diff --git a/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.ts b/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.ts index 14924aa4..884d2f0f 100644 --- a/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.ts +++ b/modules/customers/src/web/update/utils/build-customer-update-page-error-view-model.ts @@ -2,9 +2,7 @@ import { isApiClientError } from "@erp/core/client"; import type { CustomerUpdatePageErrorViewModel } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildErrorVariant = ( error: unknown diff --git a/modules/customers/src/web/update/utils/build-update-customer-by-id-params.ts b/modules/customers/src/web/update/utils/build-update-customer-by-id-params.ts index e27fae79..8a8f599d 100644 --- a/modules/customers/src/web/update/utils/build-update-customer-by-id-params.ts +++ b/modules/customers/src/web/update/utils/build-update-customer-by-id-params.ts @@ -82,12 +82,24 @@ export const buildUpdateCustomerByIdParams = ( // --- address --- const address: Record = {}; - if (patchData.street !== undefined) address.street = patchData.street; - if (patchData.street2 !== undefined) address.street2 = patchData.street2; - if (patchData.city !== undefined) address.city = patchData.city; - if (patchData.province !== undefined) address.province = patchData.province; - if (patchData.postalCode !== undefined) address.postal_code = patchData.postalCode; - if (patchData.country !== undefined) address.country = patchData.country; + if (patchData.street !== undefined) { + address.street = patchData.street; + } + if (patchData.street2 !== undefined) { + address.street2 = patchData.street2; + } + if (patchData.city !== undefined) { + address.city = patchData.city; + } + if (patchData.province !== undefined) { + address.province = patchData.province; + } + if (patchData.postalCode !== undefined) { + address.postal_code = patchData.postalCode; + } + if (patchData.country !== undefined) { + address.country = patchData.country; + } if (Object.keys(address).length > 0) { data.address = address; @@ -96,14 +108,30 @@ export const buildUpdateCustomerByIdParams = ( // --- contact --- const contact: Record = {}; - if (patchData.primaryEmail !== undefined) contact.email_primary = patchData.primaryEmail; - if (patchData.secondaryEmail !== undefined) contact.email_secondary = patchData.secondaryEmail; - if (patchData.primaryPhone !== undefined) contact.phone_primary = patchData.primaryPhone; - if (patchData.secondaryPhone !== undefined) contact.phone_secondary = patchData.secondaryPhone; - if (patchData.primaryMobile !== undefined) contact.mobile_primary = patchData.primaryMobile; - if (patchData.secondaryMobile !== undefined) contact.mobile_secondary = patchData.secondaryMobile; - if (patchData.fax !== undefined) contact.fax = patchData.fax; - if (patchData.website !== undefined) contact.website = patchData.website; + if (patchData.primaryEmail !== undefined) { + contact.email_primary = patchData.primaryEmail; + } + if (patchData.secondaryEmail !== undefined) { + contact.email_secondary = patchData.secondaryEmail; + } + if (patchData.primaryPhone !== undefined) { + contact.phone_primary = patchData.primaryPhone; + } + if (patchData.secondaryPhone !== undefined) { + contact.phone_secondary = patchData.secondaryPhone; + } + if (patchData.primaryMobile !== undefined) { + contact.mobile_primary = patchData.primaryMobile; + } + if (patchData.secondaryMobile !== undefined) { + contact.mobile_secondary = patchData.secondaryMobile; + } + if (patchData.fax !== undefined) { + contact.fax = patchData.fax; + } + if (patchData.website !== undefined) { + contact.website = patchData.website; + } if (Object.keys(contact).length > 0) { data.contact = contact; diff --git a/modules/customers/src/web/update/utils/index.ts b/modules/customers/src/web/update/utils/index.ts index 8a7b4400..858165a5 100644 --- a/modules/customers/src/web/update/utils/index.ts +++ b/modules/customers/src/web/update/utils/index.ts @@ -1,5 +1,5 @@ export * from "./build-customer-update-default"; -export * from "./build-customer-update-patch"; export * from "./build-customer-update-page-error-view-model"; +export * from "./build-customer-update-patch"; export * from "./build-update-customer-by-id-params"; export * from "./resolve-customer-update-page-feedback-state"; diff --git a/modules/customers/src/web/update/utils/resolve-customer-update-page-feedback-state.ts b/modules/customers/src/web/update/utils/resolve-customer-update-page-feedback-state.ts index 166c2631..8d7a1ce5 100644 --- a/modules/customers/src/web/update/utils/resolve-customer-update-page-feedback-state.ts +++ b/modules/customers/src/web/update/utils/resolve-customer-update-page-feedback-state.ts @@ -24,6 +24,6 @@ export const resolveCustomerUpdatePageFeedbackState = ({ return { isInitialLoading: isLoading || (isLoadError && isCancelled && !hasCustomerData), hasBlockingError: isLoadError && !isCancelled && !hasCustomerData, - hasDataInconsistency: !isLoading && !isLoadError && !hasCustomerData, + hasDataInconsistency: !(isLoading || isLoadError || hasCustomerData), }; }; diff --git a/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.test.ts b/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.test.ts index 7dda2b5b..2da958d4 100644 --- a/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.test.ts +++ b/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.test.ts @@ -13,22 +13,18 @@ const translate = (key: string) => "view.page_error.notFound.title": "Cliente no encontrado", "view.page_error.notFound.description": "El cliente ya no existe o no está disponible.", "view.page_error.forbidden.title": "No tienes acceso a este cliente", - "view.page_error.forbidden.description": - "No dispones de permisos para consultar este cliente.", + "view.page_error.forbidden.description": "No dispones de permisos para consultar este cliente.", "view.page_error.network.title": "No se pudo conectar con el servidor", "view.page_error.network.description": "Comprueba tu conexión e inténtalo de nuevo.", "view.page_error.timeout.title": "La carga está tardando demasiado", - "view.page_error.timeout.description": - "El servidor no respondió dentro del tiempo esperado.", + "view.page_error.timeout.description": "El servidor no respondió dentro del tiempo esperado.", "view.page_error.server.title": "No se pudo cargar el cliente", - "view.page_error.server.description": - "Se produjo un error interno al recuperar el cliente.", + "view.page_error.server.description": "Se produjo un error interno al recuperar el cliente.", "view.page_error.unavailable.title": "Servicio temporalmente no disponible", "view.page_error.unavailable.description": "El servicio de clientes no está disponible en este momento.", "view.page_error.unknown.title": "No se pudo cargar el cliente", - "view.page_error.unknown.description": - "Ha ocurrido un error inesperado al cargar el cliente.", + "view.page_error.unknown.description": "Ha ocurrido un error inesperado al cargar el cliente.", "view.page_error.missing_entity.title": "No se pudo cargar el cliente", "view.page_error.missing_entity.description": "La respuesta recibida no contiene un cliente válido.", diff --git a/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.ts b/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.ts index 3ae0ea15..6313910b 100644 --- a/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.ts +++ b/modules/customers/src/web/view/utils/build-customer-view-page-error-view-model.ts @@ -2,9 +2,7 @@ import { isApiClientError } from "@erp/core/client"; import type { CustomerViewPageErrorViewModel } from "../entities"; -interface Translate { - (key: string): string; -} +type Translate = (key: string) => string; const buildErrorVariant = ( error: unknown diff --git a/modules/customers/src/web/view/utils/resolve-customer-view-page-feedback-state.ts b/modules/customers/src/web/view/utils/resolve-customer-view-page-feedback-state.ts index 9cdb1dae..316440b3 100644 --- a/modules/customers/src/web/view/utils/resolve-customer-view-page-feedback-state.ts +++ b/modules/customers/src/web/view/utils/resolve-customer-view-page-feedback-state.ts @@ -24,6 +24,6 @@ export const resolveCustomerViewPageFeedbackState = ({ return { isInitialLoading: isLoading || (isLoadError && isCancelled && !hasCustomerData), hasBlockingError: isLoadError && !isCancelled && !hasCustomerData, - hasDataInconsistency: !isLoading && !isLoadError && !hasCustomerData, + hasDataInconsistency: !(isLoading || isLoadError || hasCustomerData), }; }; diff --git a/modules/document-series/package.json b/modules/document-series/package.json index 6febf3d4..7f184fe5 100644 --- a/modules/document-series/package.json +++ b/modules/document-series/package.json @@ -32,4 +32,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/document-series/src/api/application/mappers/create-document-series-command.mapper.ts b/modules/document-series/src/api/application/mappers/create-document-series-command.mapper.ts index 6105d1b4..5d7d874a 100644 --- a/modules/document-series/src/api/application/mappers/create-document-series-command.mapper.ts +++ b/modules/document-series/src/api/application/mappers/create-document-series-command.mapper.ts @@ -1,8 +1,22 @@ -import { Name, TextValue, UniqueID, UtcDate, extractOrPushError, maybeFromNullableResult, type ValidationErrorDetail, ValidationErrorCollection } from "@repo/rdx-ddd"; +import { + Name, + TextValue, + UniqueID, + UtcDate, + ValidationErrorCollection, + type ValidationErrorDetail, + extractOrPushError, + maybeFromNullableResult, +} from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import type { CreateDocumentSeriesRequestDTO } from "../../../common"; -import { DocumentSeries, DocumentSeriesCode, DocumentSeriesNumber, DocumentType } from "../../domain"; +import { + DocumentSeries, + DocumentSeriesCode, + DocumentSeriesNumber, + DocumentType, +} from "../../domain"; import type { CreateDocumentSeriesCommand } from "../commands"; export class CreateDocumentSeriesCommandMapper { diff --git a/modules/document-series/src/api/application/mappers/update-document-series-command.mapper.ts b/modules/document-series/src/api/application/mappers/update-document-series-command.mapper.ts index afc4f641..209e0565 100644 --- a/modules/document-series/src/api/application/mappers/update-document-series-command.mapper.ts +++ b/modules/document-series/src/api/application/mappers/update-document-series-command.mapper.ts @@ -1,4 +1,13 @@ -import { Name, TextValue, UniqueID, UtcDate, extractOrPushError, maybeFromNullableResult, type ValidationErrorDetail, ValidationErrorCollection } from "@repo/rdx-ddd"; +import { + Name, + TextValue, + UniqueID, + UtcDate, + ValidationErrorCollection, + type ValidationErrorDetail, + extractOrPushError, + maybeFromNullableResult, +} from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import type { UpdateDocumentSeriesRequestDTO } from "../../../common"; @@ -71,24 +80,32 @@ export class UpdateDocumentSeriesCommandMapper { ); if (errors.length > 0) { - return Result.fail(new ValidationErrorCollection("Document series patch mapping failed", errors)); + return Result.fail( + new ValidationErrorCollection("Document series patch mapping failed", errors) + ); } return Result.ok({ - branchId: command.branchId === undefined ? undefined : branchId ?? Maybe.none(), + branchId: command.branchId === undefined ? undefined : (branchId ?? Maybe.none()), documentType: - command.documentType === undefined ? undefined : documentType ?? Maybe.none(), - code: command.code === undefined ? undefined : code ?? Maybe.none(), - name: command.name === undefined ? undefined : name ?? Maybe.none(), + command.documentType === undefined + ? undefined + : (documentType ?? Maybe.none()), + code: command.code === undefined ? undefined : (code ?? Maybe.none()), + name: command.name === undefined ? undefined : (name ?? Maybe.none()), description: - command.description === undefined ? undefined : description ?? Maybe.none(), - prefix: command.prefix === undefined ? undefined : Maybe.fromNullable(command.prefix ?? undefined), - suffix: command.suffix === undefined ? undefined : Maybe.fromNullable(command.suffix ?? undefined), + command.description === undefined ? undefined : (description ?? Maybe.none()), + prefix: + command.prefix === undefined ? undefined : Maybe.fromNullable(command.prefix ?? undefined), + suffix: + command.suffix === undefined ? undefined : Maybe.fromNullable(command.suffix ?? undefined), nextNumber: - command.nextNumber === undefined ? undefined : nextNumber ?? Maybe.none(), + command.nextNumber === undefined + ? undefined + : (nextNumber ?? Maybe.none()), padding: command.padding, - validFrom: command.validFrom === undefined ? undefined : validFrom ?? Maybe.none(), - validTo: command.validTo === undefined ? undefined : validTo ?? Maybe.none(), + validFrom: command.validFrom === undefined ? undefined : (validFrom ?? Maybe.none()), + validTo: command.validTo === undefined ? undefined : (validTo ?? Maybe.none()), isDefault: command.isDefault, isActive: command.isActive, updatedAt: new Date(), diff --git a/modules/document-series/src/api/application/repositories/document-series-repository.interface.ts b/modules/document-series/src/api/application/repositories/document-series-repository.interface.ts index b1c20b91..b11cb160 100644 --- a/modules/document-series/src/api/application/repositories/document-series-repository.interface.ts +++ b/modules/document-series/src/api/application/repositories/document-series-repository.interface.ts @@ -2,8 +2,12 @@ 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 { DocumentSeries } from "../../domain"; -import type { AssignedDocumentNumber, DocumentSeriesCode, DocumentType } from "../../domain"; +import type { + AssignedDocumentNumber, + DocumentSeries, + DocumentSeriesCode, + DocumentType, +} from "../../domain"; export type FindDocumentSeriesByCodeParams = { companyId: UniqueID; diff --git a/modules/document-series/src/api/application/services/document-series-number-assigner.ts b/modules/document-series/src/api/application/services/document-series-number-assigner.ts index 76c5bdc8..a0c3a934 100644 --- a/modules/document-series/src/api/application/services/document-series-number-assigner.ts +++ b/modules/document-series/src/api/application/services/document-series-number-assigner.ts @@ -1,14 +1,21 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { DocumentSeriesTransactionRequiredError, DocumentType, type AssignedDocumentNumber, DocumentSeriesCode } from "../../domain"; -import type { IDocumentSeriesRepository } from "../repositories"; +import { + type AssignedDocumentNumber, + DocumentSeriesCode, + DocumentSeriesTransactionRequiredError, + DocumentType, +} from "../../domain"; import type { AssignNextDocumentNumberCommand } from "../commands"; +import type { IDocumentSeriesRepository } from "../repositories"; export interface AssignNextDocumentNumberParams extends AssignNextDocumentNumberCommand {} export interface IDocumentSeriesNumberAssigner { - assignNext(params: AssignNextDocumentNumberParams): Promise>; + assignNext( + params: AssignNextDocumentNumberParams + ): Promise>; } export class DocumentSeriesNumberAssigner implements IDocumentSeriesNumberAssigner { diff --git a/modules/document-series/src/api/application/use-cases/assign-next-document-number.use-case.ts b/modules/document-series/src/api/application/use-cases/assign-next-document-number.use-case.ts index 7cbca0e9..a6a9b7c1 100644 --- a/modules/document-series/src/api/application/use-cases/assign-next-document-number.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/assign-next-document-number.use-case.ts @@ -1,8 +1,8 @@ import { Result } from "@repo/rdx-utils"; import type { AssignNextDocumentNumberRequestDTO } from "../../../common"; -import { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; -import { DocumentSeriesNumberAssigner } from "../services"; +import type { DocumentSeriesNumberAssigner } from "../services"; +import type { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; export class AssignNextDocumentNumberUseCase { public constructor( diff --git a/modules/document-series/src/api/application/use-cases/create-document-series.use-case.ts b/modules/document-series/src/api/application/use-cases/create-document-series.use-case.ts index e2618543..aca160d7 100644 --- a/modules/document-series/src/api/application/use-cases/create-document-series.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/create-document-series.use-case.ts @@ -3,9 +3,9 @@ import type { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import type { CreateDocumentSeriesRequestDTO } from "../../../common"; -import { CreateDocumentSeriesCommandMapper } from "../mappers"; -import { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; -import { DocumentSeriesWriter } from "../services"; +import type { CreateDocumentSeriesCommandMapper } from "../mappers"; +import type { DocumentSeriesWriter } from "../services"; +import type { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; export class CreateDocumentSeriesUseCase { public constructor( diff --git a/modules/document-series/src/api/application/use-cases/disable-document-series.use-case.ts b/modules/document-series/src/api/application/use-cases/disable-document-series.use-case.ts index 385b9f83..0944d765 100644 --- a/modules/document-series/src/api/application/use-cases/disable-document-series.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/disable-document-series.use-case.ts @@ -2,7 +2,7 @@ import type { ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { DocumentSeriesWriter } from "../services"; +import type { DocumentSeriesWriter } from "../services"; export class DisableDocumentSeriesUseCase { public constructor( diff --git a/modules/document-series/src/api/application/use-cases/get-document-series-by-id.use-case.ts b/modules/document-series/src/api/application/use-cases/get-document-series-by-id.use-case.ts index f63d7350..b1eb44ec 100644 --- a/modules/document-series/src/api/application/use-cases/get-document-series-by-id.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/get-document-series-by-id.use-case.ts @@ -1,8 +1,8 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; -import { DocumentSeriesFinder } from "../services"; +import type { DocumentSeriesFinder } from "../services"; +import type { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; export class GetDocumentSeriesByIdUseCase { public constructor( diff --git a/modules/document-series/src/api/application/use-cases/list-document-series.use-case.ts b/modules/document-series/src/api/application/use-cases/list-document-series.use-case.ts index 3d8fc494..8a144e3b 100644 --- a/modules/document-series/src/api/application/use-cases/list-document-series.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/list-document-series.use-case.ts @@ -3,8 +3,8 @@ import type { Criteria } from "@repo/rdx-criteria/server"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; -import { DocumentSeriesFinder } from "../services"; +import type { DocumentSeriesFinder } from "../services"; +import type { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; export class ListDocumentSeriesUseCase { public constructor( diff --git a/modules/document-series/src/api/application/use-cases/update-document-series.use-case.ts b/modules/document-series/src/api/application/use-cases/update-document-series.use-case.ts index b700ab50..9af2b261 100644 --- a/modules/document-series/src/api/application/use-cases/update-document-series.use-case.ts +++ b/modules/document-series/src/api/application/use-cases/update-document-series.use-case.ts @@ -3,9 +3,9 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import type { UpdateDocumentSeriesRequestDTO } from "../../../common"; -import { UpdateDocumentSeriesCommandMapper } from "../mappers"; -import { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; -import { DocumentSeriesFinder, DocumentSeriesWriter } from "../services"; +import type { UpdateDocumentSeriesCommandMapper } from "../mappers"; +import type { DocumentSeriesFinder, DocumentSeriesWriter } from "../services"; +import type { DocumentSeriesSnapshotBuilder } from "../snapshot-builders"; export class UpdateDocumentSeriesUseCase { public constructor( diff --git a/modules/document-series/src/api/domain/value-objects/document-series-code.vo.ts b/modules/document-series/src/api/domain/value-objects/document-series-code.vo.ts index 03a2bfec..b97fccf1 100644 --- a/modules/document-series/src/api/domain/value-objects/document-series-code.vo.ts +++ b/modules/document-series/src/api/domain/value-objects/document-series-code.vo.ts @@ -12,11 +12,7 @@ export class DocumentSeriesCode extends ValueObject { if (!validation.success) { return Result.fail( - new DomainValidationError( - "INVALID_DOCUMENT_SERIES_CODE", - "code", - validation.error.message - ) + new DomainValidationError("INVALID_DOCUMENT_SERIES_CODE", "code", validation.error.message) ); } diff --git a/modules/document-series/src/api/infrastructure/express/controllers/assign-next-document-number.controller.ts b/modules/document-series/src/api/infrastructure/express/controllers/assign-next-document-number.controller.ts index a0003a6c..61c70026 100644 --- a/modules/document-series/src/api/infrastructure/express/controllers/assign-next-document-number.controller.ts +++ b/modules/document-series/src/api/infrastructure/express/controllers/assign-next-document-number.controller.ts @@ -15,7 +15,9 @@ export class AssignNextDocumentNumberController extends ExpressController { protected async executeImpl() { const companyId = this.getTenantId(); - if (!companyId) return this.forbiddenError("Tenant ID not found"); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const result = await this.transactionManager.complete((transaction: unknown) => this.useCase.execute({ companyId: companyId.toString(), diff --git a/modules/document-series/src/api/infrastructure/express/document-series-api-error-mapper.ts b/modules/document-series/src/api/infrastructure/express/document-series-api-error-mapper.ts index da354391..161079b3 100644 --- a/modules/document-series/src/api/infrastructure/express/document-series-api-error-mapper.ts +++ b/modules/document-series/src/api/infrastructure/express/document-series-api-error-mapper.ts @@ -1,7 +1,7 @@ import { ApiErrorMapper, - type ErrorToApiRule, ConflictApiError, + type ErrorToApiRule, NotFoundApiError, ValidationApiError, } from "@erp/core/api"; diff --git a/modules/document-series/src/api/infrastructure/express/index.ts b/modules/document-series/src/api/infrastructure/express/index.ts index 7977818e..e64da948 100644 --- a/modules/document-series/src/api/infrastructure/express/index.ts +++ b/modules/document-series/src/api/infrastructure/express/index.ts @@ -1,3 +1,3 @@ +export * from "./controllers"; export * from "./document-series.routes"; export * from "./document-series-api-error-mapper"; -export * from "./controllers"; diff --git a/modules/document-series/src/api/infrastructure/persistence/sequelize/mappers/sequelize-document-series-domain.mapper.ts b/modules/document-series/src/api/infrastructure/persistence/sequelize/mappers/sequelize-document-series-domain.mapper.ts index eaf9a536..ce6f3e96 100644 --- a/modules/document-series/src/api/infrastructure/persistence/sequelize/mappers/sequelize-document-series-domain.mapper.ts +++ b/modules/document-series/src/api/infrastructure/persistence/sequelize/mappers/sequelize-document-series-domain.mapper.ts @@ -1,12 +1,21 @@ -import { Name, TextValue, UniqueID, UtcDate, ValidationErrorCollection, extractOrPushError, maybeFromNullableResult, type ValidationErrorDetail } from "@repo/rdx-ddd"; +import { + Name, + TextValue, + UniqueID, + UtcDate, + ValidationErrorCollection, + type ValidationErrorDetail, + extractOrPushError, + maybeFromNullableResult, +} from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; import { DocumentSeries, DocumentSeriesCode, + type DocumentSeriesInternalProps, DocumentSeriesNumber, DocumentType, - type DocumentSeriesInternalProps, } from "../../../../domain"; import type { DocumentSeriesModel } from "../../../common"; @@ -21,7 +30,11 @@ export class SequelizeDocumentSeriesDomainMapper { "branch_id", errors ); - const documentType = extractOrPushError(DocumentType.create(raw.document_type), "document_type", errors); + const documentType = extractOrPushError( + DocumentType.create(raw.document_type), + "document_type", + errors + ); const code = extractOrPushError(DocumentSeriesCode.create(raw.code), "code", errors); const name = extractOrPushError(Name.create(raw.name), "name", errors); const description = extractOrPushError( diff --git a/modules/document-series/src/common/dto/request/create-document-series.request.dto.ts b/modules/document-series/src/common/dto/request/create-document-series.request.dto.ts index 563ccb7f..085d13c6 100644 --- a/modules/document-series/src/common/dto/request/create-document-series.request.dto.ts +++ b/modules/document-series/src/common/dto/request/create-document-series.request.dto.ts @@ -17,12 +17,9 @@ export const CreateDocumentSeriesRequestSchema = z is_default: z.boolean().optional().default(false), is_active: z.boolean().optional().default(true), }) - .refine( - (value) => !value.valid_from || !value.valid_to || value.valid_from <= value.valid_to, - { - path: ["valid_to"], - message: "valid_to must be greater than or equal to valid_from", - } - ); + .refine((value) => !(value.valid_from && value.valid_to) || value.valid_from <= value.valid_to, { + path: ["valid_to"], + message: "valid_to must be greater than or equal to valid_from", + }); export type CreateDocumentSeriesRequestDTO = z.infer; diff --git a/modules/document-series/src/common/dto/request/update-document-series.request.dto.ts b/modules/document-series/src/common/dto/request/update-document-series.request.dto.ts index 275928d2..136e26f0 100644 --- a/modules/document-series/src/common/dto/request/update-document-series.request.dto.ts +++ b/modules/document-series/src/common/dto/request/update-document-series.request.dto.ts @@ -20,13 +20,10 @@ export const UpdateDocumentSeriesRequestSchema = z is_default: z.boolean().optional(), is_active: z.boolean().optional(), }) - .refine( - (value) => !value.valid_from || !value.valid_to || value.valid_from <= value.valid_to, - { - path: ["valid_to"], - message: "valid_to must be greater than or equal to valid_from", - } - ); + .refine((value) => !(value.valid_from && value.valid_to) || value.valid_from <= value.valid_to, { + path: ["valid_to"], + message: "valid_to must be greater than or equal to valid_from", + }); export type UpdateDocumentSeriesRequestDTO = z.infer; export type UpdateDocumentSeriesParamsRequestDTO = z.infer< diff --git a/modules/factuges/package.json b/modules/factuges/package.json index d7b73d8c..8b094199 100644 --- a/modules/factuges/package.json +++ b/modules/factuges/package.json @@ -35,4 +35,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/factuges/src/api/application/services/json-data/factuges-payment-method/factuges-payment-method-reference.json b/modules/factuges/src/api/application/services/json-data/factuges-payment-method/factuges-payment-method-reference.json index 3ee64122..b0bfadda 100644 --- a/modules/factuges/src/api/application/services/json-data/factuges-payment-method/factuges-payment-method-reference.json +++ b/modules/factuges/src/api/application/services/json-data/factuges-payment-method/factuges-payment-method-reference.json @@ -34,4 +34,4 @@ "description": "30/60/90/120", "group": "General" } -] \ No newline at end of file +] diff --git a/modules/factuges/src/api/domain/aggregates/factuges-proforma-link.aggregate.ts b/modules/factuges/src/api/domain/aggregates/factuges-proforma-link.aggregate.ts index 89c0b111..eda7c5c1 100644 --- a/modules/factuges/src/api/domain/aggregates/factuges-proforma-link.aggregate.ts +++ b/modules/factuges/src/api/domain/aggregates/factuges-proforma-link.aggregate.ts @@ -36,7 +36,7 @@ export class FactuGESProformaLink } private static validateCreateProps( - props: IFactuGESProformaLinkCreateProps | FactuGESProformaLinkInternalProps + _props: IFactuGESProformaLinkCreateProps | FactuGESProformaLinkInternalProps ): Result { return Result.ok(); } diff --git a/modules/factuges/src/api/infraestructure/express/mappers/index.ts b/modules/factuges/src/api/infraestructure/express/mappers/index.ts index 8b137891..e69de29b 100644 --- a/modules/factuges/src/api/infraestructure/express/mappers/index.ts +++ b/modules/factuges/src/api/infraestructure/express/mappers/index.ts @@ -1 +0,0 @@ - diff --git a/modules/identity/package.json b/modules/identity/package.json index 365c1248..e65b7c72 100644 --- a/modules/identity/package.json +++ b/modules/identity/package.json @@ -54,4 +54,4 @@ "rimraf": "^6.1.3", "typescript": "^6.0.2" } -} \ No newline at end of file +} diff --git a/modules/identity/src/api/application/accounts/contracts/account-repository.interface.ts b/modules/identity/src/api/application/accounts/contracts/account-repository.interface.ts index 6d241c08..b61b5586 100644 --- a/modules/identity/src/api/application/accounts/contracts/account-repository.interface.ts +++ b/modules/identity/src/api/application/accounts/contracts/account-repository.interface.ts @@ -7,8 +7,5 @@ export interface IAccountRepository { save(account: Account, transaction?: unknown): Promise>; update(account: Account, transaction?: unknown): Promise>; findById(id: Account["id"], transaction?: unknown): Promise, Error>>; - findByEmail( - email: EmailAddress, - transaction?: unknown - ): Promise, Error>>; + findByEmail(email: EmailAddress, transaction?: unknown): Promise, Error>>; } diff --git a/modules/identity/src/api/application/accounts/use-cases/change-current-account-password.use-case.ts b/modules/identity/src/api/application/accounts/use-cases/change-current-account-password.use-case.ts index 63c3e1a7..a66b1bb6 100644 --- a/modules/identity/src/api/application/accounts/use-cases/change-current-account-password.use-case.ts +++ b/modules/identity/src/api/application/accounts/use-cases/change-current-account-password.use-case.ts @@ -2,9 +2,13 @@ import type { ITransactionManager } from "@erp/core/api"; import type { UniqueID, UtcDateTime } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; +import { + AccountNotFoundError, + type IPasswordHasher, + type IRefreshTokenRepository, +} from "../../authentication"; import type { IAccountRepository } from "../contracts"; import { CurrentPasswordMismatchError } from "../services"; -import { AccountNotFoundError, type IPasswordHasher, type IRefreshTokenRepository } from "../../authentication"; export type ChangeCurrentAccountPasswordInput = { accountId: UniqueID; @@ -32,7 +36,10 @@ export class ChangeCurrentAccountPasswordUseCase { ): Promise> { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const accountResult = await this.deps.accountRepository.findById(input.accountId, transaction); + const accountResult = await this.deps.accountRepository.findById( + input.accountId, + transaction + ); if (accountResult.isFailure) { return Result.fail(accountResult.error); } diff --git a/modules/identity/src/api/application/accounts/use-cases/get-current-account.use-case.ts b/modules/identity/src/api/application/accounts/use-cases/get-current-account.use-case.ts index 2cded295..39ef591b 100644 --- a/modules/identity/src/api/application/accounts/use-cases/get-current-account.use-case.ts +++ b/modules/identity/src/api/application/accounts/use-cases/get-current-account.use-case.ts @@ -1,8 +1,8 @@ -import { Result } from "@repo/rdx-utils"; import type { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; -import type { IAccountRepository } from "../contracts"; import { AccountNotFoundError } from "../../authentication"; +import type { IAccountRepository } from "../contracts"; export type GetCurrentAccountInput = { accountId: UniqueID; @@ -24,7 +24,9 @@ export class GetCurrentAccountUseCase { } ) {} - public async execute(input: GetCurrentAccountInput): Promise> { + public async execute( + input: GetCurrentAccountInput + ): Promise> { try { const accountResult = await this.deps.accountRepository.findById(input.accountId); if (accountResult.isFailure) { diff --git a/modules/identity/src/api/application/accounts/use-cases/update-current-account.use-case.ts b/modules/identity/src/api/application/accounts/use-cases/update-current-account.use-case.ts index a2b06248..d100d9a4 100644 --- a/modules/identity/src/api/application/accounts/use-cases/update-current-account.use-case.ts +++ b/modules/identity/src/api/application/accounts/use-cases/update-current-account.use-case.ts @@ -1,10 +1,17 @@ import type { ITransactionManager } from "@erp/core/api"; -import { EmailAddress, LanguageCode, Name, URLAddress, type UniqueID, type UtcDateTime } from "@repo/rdx-ddd"; +import { + EmailAddress, + LanguageCode, + Name, + URLAddress, + type UniqueID, + type UtcDateTime, +} from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; +import { AccountNotFoundError } from "../../authentication"; import type { IAccountRepository } from "../contracts"; import { AccountEmailAlreadyExistsError } from "../services"; -import { AccountNotFoundError } from "../../authentication"; import type { CurrentAccountOutput } from "./get-current-account.use-case"; @@ -30,7 +37,10 @@ export class UpdateCurrentAccountUseCase { ): Promise> { return this.deps.transactionManager.complete(async (transaction: unknown) => { try { - const accountResult = await this.deps.accountRepository.findById(input.accountId, transaction); + const accountResult = await this.deps.accountRepository.findById( + input.accountId, + transaction + ); if (accountResult.isFailure) { return Result.fail(accountResult.error); } diff --git a/modules/identity/src/api/application/authentication/contracts/index.ts b/modules/identity/src/api/application/authentication/contracts/index.ts index 64f8c4d5..9f5792c8 100644 --- a/modules/identity/src/api/application/authentication/contracts/index.ts +++ b/modules/identity/src/api/application/authentication/contracts/index.ts @@ -2,5 +2,5 @@ export * from "./access-token-issuer.interface"; export * from "./access-token-verifier.interface"; export * from "./password-hasher.interface"; export * from "./refresh-token-generator.interface"; -export * from "./refresh-token-repository.interface"; export * from "./refresh-token-hasher.interface"; +export * from "./refresh-token-repository.interface"; diff --git a/modules/identity/src/api/application/authentication/use-cases/get-current-session.use-case.ts b/modules/identity/src/api/application/authentication/use-cases/get-current-session.use-case.ts index 2d2deb85..e125de3c 100644 --- a/modules/identity/src/api/application/authentication/use-cases/get-current-session.use-case.ts +++ b/modules/identity/src/api/application/authentication/use-cases/get-current-session.use-case.ts @@ -1,5 +1,5 @@ -import { Result } from "@repo/rdx-utils"; import type { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; import type { IAccountRepository } from "../../accounts"; import { AccountDisabledError, AccountNotFoundError } from "../services"; diff --git a/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.test.ts b/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.test.ts index f1b7f08e..6306658a 100644 --- a/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.test.ts +++ b/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.test.ts @@ -71,35 +71,46 @@ function setup( calls.push(p.transaction); if (concurrent && arrivals < 2) { arrivals++; - if (arrivals === 2) release(); + if (arrivals === 2) { + release(); + } await barrier; return Result.ok(Maybe.none()); } - if (failure === "find") return Result.fail(error); + if (failure === "find") { + return Result.fail(error); + } return Result.ok(state ? Maybe.some(state) : Maybe.none()); }, save: async (m, tx) => { calls.push(tx); saves++; - if (failure === "save") return Result.fail(error); - if (failure === "unpersisted") return Result.ok(); + if (failure === "save") { + return Result.fail(error); + } + if (failure === "unpersisted") { + return Result.ok(); + } if (failure === "duplicate") { state = winner; return Result.fail( new DuplicateEntityError("Membership", "accountId", accountId.toPrimitive()) ); } - if (state) + if (state) { return Result.fail( new DuplicateEntityError("Membership", "accountId", accountId.toPrimitive()) ); + } state = m; return Result.ok(); }, update: async (m, tx) => { calls.push(tx); updates++; - if (failure === "update") return Result.fail(error); + if (failure === "update") { + return Result.fail(error); + } state = m; return Result.ok(); }, @@ -141,8 +152,12 @@ function setup( resolve: async (p) => { resolutions++; calls.push(p.transaction); - if (failure === "resolve") return Result.fail(error); - if (failure === "permissions") return Result.ok(new Collection([])); + if (failure === "resolve") { + return Result.fail(error); + } + if (failure === "permissions") { + return Result.ok(new Collection([])); + } return resolver.resolve(p); }, }, @@ -186,7 +201,9 @@ test("crea membership activa, verifica permisos y es idempotente con estado real ); assert.ok(s.state?.id); assert.equal(s.state.createdAt.toPrimitive(), UtcDate.today().toPrimitive()); - for (const tx of s.calls) assert.equal(tx, transaction); + for (const tx of s.calls) { + assert.equal(tx, transaction); + } }); test("activa admin no escribe membership", async () => { const s = setup(member()); @@ -217,7 +234,9 @@ for (const failure of ["ensure", "find", "save", "update", "resolve"]) { test(`propaga error en ${failure}`, async () => { const s = setup(failure === "update" ? member([otherId]) : undefined, failure); assert.equal((await s.service.assign(params)).error, s.error); - if (failure === "ensure" || failure === "find") assert.equal(s.saves + s.updates, 0); + if (failure === "ensure" || failure === "find") { + assert.equal(s.saves + s.updates, 0); + } }); } test("permisos insuficientes impiden success", async () => { diff --git a/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.ts b/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.ts index f157ba09..df28676c 100644 --- a/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.ts +++ b/modules/identity/src/api/application/company-memberships/services/initial-company-admin-assigner.ts @@ -26,22 +26,30 @@ export class InitialCompanyAdminAssigner implements IInitialCompanyAdminAssigner ) {} public async assign(params: AssignInitialCompanyAdminParams): Promise> { - if (!params.transaction) + if (!params.transaction) { return Result.fail(new Error("Initial admin assignment requires an external transaction.")); + } const admin = await this.deps.adminRoles.ensure({ companyId: params.companyId, transaction: params.transaction, }); - if (admin.isFailure) return Result.fail(admin.error); + if (admin.isFailure) { + return Result.fail(admin.error); + } const assigned = await this.ensureMembership(admin.data.id, params); - if (assigned.isFailure) return assigned; + if (assigned.isFailure) { + return assigned; + } const persisted = await this.deps.memberships.findByAccountIdAndCompanyId({ ...params, currentRead: true, }); - if (persisted.isFailure) return Result.fail(persisted.error); - if (persisted.data.isNone()) + if (persisted.isFailure) { + return Result.fail(persisted.error); + } + if (persisted.data.isNone()) { return Result.fail(new Error("Initial admin membership was not persisted.")); + } const membership = persisted.data.unwrap(); if ( !(membership.status.isActive() && membership.roleIds.some((id) => id.equals(admin.data.id))) @@ -49,7 +57,9 @@ export class InitialCompanyAdminAssigner implements IInitialCompanyAdminAssigner return Result.fail(new Error("Initial admin membership postcondition failed.")); } const effective = await this.deps.permissions.resolve({ ...params, currentRead: true }); - if (effective.isFailure) return Result.fail(effective.error); + if (effective.isFailure) { + return Result.fail(effective.error); + } const permissions = new Set(effective.data.getAll()); if (admin.data.permissionCodes.some((code) => !permissions.has(code))) { return Result.fail(new Error("Initial admin effective permissions postcondition failed.")); @@ -62,8 +72,12 @@ export class InitialCompanyAdminAssigner implements IInitialCompanyAdminAssigner params: AssignInitialCompanyAdminParams ): Promise> { const found = await this.deps.memberships.findByAccountIdAndCompanyId(params); - if (found.isFailure) return Result.fail(found.error); - if (found.data.isSome()) return this.complete(found.data.unwrap(), adminId, params); + if (found.isFailure) { + return Result.fail(found.error); + } + if (found.data.isSome()) { + return this.complete(found.data.unwrap(), adminId, params); + } const now = UtcDate.today(); const created = CompanyMembership.create( { @@ -76,16 +90,26 @@ export class InitialCompanyAdminAssigner implements IInitialCompanyAdminAssigner }, UniqueID.generateNewID() ); - if (created.isFailure) return Result.fail(created.error); + if (created.isFailure) { + return Result.fail(created.error); + } const saved = await this.deps.memberships.save(created.data, params.transaction); - if (saved.isSuccess) return saved; - if (!isDuplicateEntityError(saved.error)) return Result.fail(saved.error); + if (saved.isSuccess) { + return saved; + } + if (!isDuplicateEntityError(saved.error)) { + return Result.fail(saved.error); + } const winner = await this.deps.memberships.findByAccountIdAndCompanyId({ ...params, currentRead: true, }); - if (winner.isFailure) return Result.fail(winner.error); - if (winner.data.isNone()) return Result.fail(saved.error); + if (winner.isFailure) { + return Result.fail(winner.error); + } + if (winner.data.isNone()) { + return Result.fail(saved.error); + } return this.complete(winner.data.unwrap(), adminId, params); } @@ -104,13 +128,20 @@ export class InitialCompanyAdminAssigner implements IInitialCompanyAdminAssigner } const now = UtcDate.today(); const roles = membership.roleIds; - if (!roles.some((id) => id.equals(adminId))) roles.push(adminId); + if (!roles.some((id) => id.equals(adminId))) { + roles.push(adminId); + } const replaced = membership.replaceRoles({ roleIds: roles, updatedAt: now }); - if (replaced.isFailure) return Result.fail(replaced.error); + if (replaced.isFailure) { + return Result.fail(replaced.error); + } const enabled = membership.enable(now); - if (enabled.isFailure) return Result.fail(enabled.error); - if (replaced.data || enabled.data) + if (enabled.isFailure) { + return Result.fail(enabled.error); + } + if (replaced.data || enabled.data) { return this.deps.memberships.update(membership, params.transaction); + } return Result.ok(); } } diff --git a/modules/identity/src/api/application/permissions/services/effective-permissions-resolver.ts b/modules/identity/src/api/application/permissions/services/effective-permissions-resolver.ts index 0f805a78..97986fd0 100644 --- a/modules/identity/src/api/application/permissions/services/effective-permissions-resolver.ts +++ b/modules/identity/src/api/application/permissions/services/effective-permissions-resolver.ts @@ -32,7 +32,9 @@ export class EffectivePermissionsResolver implements IEffectivePermissionsResolv params: ResolveEffectivePermissionsParams ): Promise, Error>> { const found = await this.deps.memberships.findByAccountIdAndCompanyId(params); - if (found.isFailure) return Result.fail(found.error); + if (found.isFailure) { + return Result.fail(found.error); + } if (found.data.isNone()) { return Result.fail( new CompanyMembershipCannotAccessCompanyError("Company membership is required.") @@ -48,7 +50,9 @@ export class EffectivePermissionsResolver implements IEffectivePermissionsResolv return Result.fail(new Error("Membership does not match the requested account and company.")); } const access = membership.canAccessCompany(); - if (access.isFailure) return Result.fail(access.error); + if (access.isFailure) { + return Result.fail(access.error); + } const roleIds = membership.roleIds; const foundRoles = await this.deps.roles.findByIdsInCompany({ @@ -57,7 +61,9 @@ export class EffectivePermissionsResolver implements IEffectivePermissionsResolv companyId: params.companyId, transaction: params.transaction, }); - if (foundRoles.isFailure) return Result.fail(foundRoles.error); + if (foundRoles.isFailure) { + return Result.fail(foundRoles.error); + } const roles = foundRoles.data.getAll(); const expected = new Set(roleIds.map((id) => id.toPrimitive())); const received = new Set(roles.map((role) => role.id.toPrimitive())); @@ -74,7 +80,9 @@ export class EffectivePermissionsResolver implements IEffectivePermissionsResolv } const permissions = new Set(); for (const role of roles) { - for (const permission of role.permissionCodes) permissions.add(permission); + for (const permission of role.permissionCodes) { + permissions.add(permission); + } } return Result.ok(new Collection([...permissions].sort())); } diff --git a/modules/identity/src/api/application/roles/contracts/index.ts b/modules/identity/src/api/application/roles/contracts/index.ts index 04e3a83f..d253a8fa 100644 --- a/modules/identity/src/api/application/roles/contracts/index.ts +++ b/modules/identity/src/api/application/roles/contracts/index.ts @@ -1,2 +1,2 @@ -export * from "./role-repository.interface"; export * from "./role-permission-validator.interface"; +export * from "./role-repository.interface"; diff --git a/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.test.ts b/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.test.ts index 1c77b6dd..a591d572 100644 --- a/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.test.ts +++ b/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.test.ts @@ -101,7 +101,9 @@ for (const valid of [true, false]) { const transaction = {}; const result = await subject.service.ensure({ companyId, transaction }); assert.equal(result.isSuccess, valid); - if (valid) assert.equal(result.data, winner); + if (valid) { + assert.equal(result.data, winner); + } assert.equal(subject.lookups.length, 2); for (const lookup of subject.lookups) { assert.equal(lookup.companyId, companyId); diff --git a/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.ts b/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.ts index 364179ba..5e04de26 100644 --- a/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.ts +++ b/modules/identity/src/api/application/roles/services/company-admin-role-ensurer.ts @@ -20,13 +20,21 @@ export class CompanyAdminRoleEnsurer implements ICompanyAdminRoleEnsurer { public async ensure(params: EnsureCompanyAdminRoleParams): Promise> { const code = RoleCode.create(COMPANY_ADMIN_ROLE_CODE); - if (code.isFailure) return Result.fail(code.error); + if (code.isFailure) { + return Result.fail(code.error); + } const lookup = { ...params, code: code.data }; const found = await this.roles.findByCodeInCompany(lookup); - if (found.isFailure) return Result.fail(found.error); - if (found.data.isSome()) return this.validate(found.data.unwrap(), params.companyId); + if (found.isFailure) { + return Result.fail(found.error); + } + if (found.data.isSome()) { + return this.validate(found.data.unwrap(), params.companyId); + } const name = Name.create("Administrator"); - if (name.isFailure) return Result.fail(name.error); + if (name.isFailure) { + return Result.fail(name.error); + } const now = UtcDate.today(); const created = Role.create( { @@ -40,13 +48,23 @@ export class CompanyAdminRoleEnsurer implements ICompanyAdminRoleEnsurer { }, UniqueID.generateNewID() ); - if (created.isFailure) return Result.fail(created.error); + if (created.isFailure) { + return Result.fail(created.error); + } const saved = await this.roles.save(created.data, params.transaction); - if (saved.isSuccess) return created; - if (!isDuplicateEntityError(saved.error)) return Result.fail(saved.error); + if (saved.isSuccess) { + return created; + } + if (!isDuplicateEntityError(saved.error)) { + return Result.fail(saved.error); + } const winner = await this.roles.findByCodeInCompany({ ...lookup, currentRead: true }); - if (winner.isFailure) return Result.fail(winner.error); - if (winner.data.isNone()) return Result.fail(saved.error); + if (winner.isFailure) { + return Result.fail(winner.error); + } + if (winner.data.isNone()) { + return Result.fail(saved.error); + } return this.validate(winner.data.unwrap(), params.companyId); } diff --git a/modules/identity/src/api/domain/accounts/account-status.vo.ts b/modules/identity/src/api/domain/accounts/account-status.vo.ts index 09d74c12..8c7b6fdd 100644 --- a/modules/identity/src/api/domain/accounts/account-status.vo.ts +++ b/modules/identity/src/api/domain/accounts/account-status.vo.ts @@ -19,9 +19,17 @@ export class AccountStatus extends ValueObject { ] as const; static create(value: string): Result { - if (!AccountStatus.ALLOWED_STATUSES.includes(value as (typeof AccountStatus.ALLOWED_STATUSES)[number])) { + if ( + !AccountStatus.ALLOWED_STATUSES.includes( + value as (typeof AccountStatus.ALLOWED_STATUSES)[number] + ) + ) { return Result.fail( - new DomainValidationError("INVALID_ACCOUNT_STATUS", "status", `Status value not valid: ${value}`) + new DomainValidationError( + "INVALID_ACCOUNT_STATUS", + "status", + `Status value not valid: ${value}` + ) ); } diff --git a/modules/identity/src/api/domain/companies/company-status.vo.ts b/modules/identity/src/api/domain/companies/company-status.vo.ts index f9cf90fc..5ea4cdce 100644 --- a/modules/identity/src/api/domain/companies/company-status.vo.ts +++ b/modules/identity/src/api/domain/companies/company-status.vo.ts @@ -11,12 +11,23 @@ export enum COMPANY_STATUS { } export class CompanyStatus extends ValueObject { - private static readonly ALLOWED_STATUSES = [COMPANY_STATUS.ACTIVE, COMPANY_STATUS.DISABLED] as const; + private static readonly ALLOWED_STATUSES = [ + COMPANY_STATUS.ACTIVE, + COMPANY_STATUS.DISABLED, + ] as const; static create(value: string): Result { - if (!CompanyStatus.ALLOWED_STATUSES.includes(value as (typeof CompanyStatus.ALLOWED_STATUSES)[number])) { + if ( + !CompanyStatus.ALLOWED_STATUSES.includes( + value as (typeof CompanyStatus.ALLOWED_STATUSES)[number] + ) + ) { return Result.fail( - new DomainValidationError("INVALID_COMPANY_STATUS", "status", `Status value not valid: ${value}`) + new DomainValidationError( + "INVALID_COMPANY_STATUS", + "status", + `Status value not valid: ${value}` + ) ); } diff --git a/modules/identity/src/api/infrastructure/express/identity-account-api-error-mapper.ts b/modules/identity/src/api/infrastructure/express/identity-account-api-error-mapper.ts index 7d7bbab4..70e92151 100644 --- a/modules/identity/src/api/infrastructure/express/identity-account-api-error-mapper.ts +++ b/modules/identity/src/api/infrastructure/express/identity-account-api-error-mapper.ts @@ -1,41 +1,52 @@ import { ApiErrorMapper, ConflictApiError, - NotFoundApiError, + type DuplicateEntityError, type ErrorToApiRule, + NotFoundApiError, ValidationApiError, + isDuplicateEntityError, } from "@erp/core/api"; -import { type DuplicateEntityError, isDuplicateEntityError } from "@erp/core/api"; -import { AccountCannotBeUpdatedError } from "../../domain"; -import { AccountEmailAlreadyExistsError, CurrentPasswordMismatchError } from "../../application/accounts"; +import { + AccountEmailAlreadyExistsError, + CurrentPasswordMismatchError, +} from "../../application/accounts"; import { AccountNotFoundError } from "../../application/authentication"; +import { AccountCannotBeUpdatedError } from "../../domain"; const accountNotFoundRule: ErrorToApiRule = { priority: 120, matches: (error) => error instanceof AccountNotFoundError, - build: (error) => new NotFoundApiError((error as AccountNotFoundError).message || "Account not found."), + build: (error) => + new NotFoundApiError((error as AccountNotFoundError).message || "Account not found."), }; const accountCannotBeUpdatedRule: ErrorToApiRule = { priority: 120, matches: (error) => error instanceof AccountCannotBeUpdatedError, build: (error) => - new ValidationApiError((error as AccountCannotBeUpdatedError).message || "Account cannot be updated."), + new ValidationApiError( + (error as AccountCannotBeUpdatedError).message || "Account cannot be updated." + ), }; const currentPasswordMismatchRule: ErrorToApiRule = { priority: 120, matches: (error) => error instanceof CurrentPasswordMismatchError, build: (error) => - new ValidationApiError((error as CurrentPasswordMismatchError).message || "Current password is incorrect."), + new ValidationApiError( + (error as CurrentPasswordMismatchError).message || "Current password is incorrect." + ), }; const accountEmailAlreadyExistsRule: ErrorToApiRule = { priority: 120, matches: (error) => error instanceof AccountEmailAlreadyExistsError, build: (error) => - new ConflictApiError((error as AccountEmailAlreadyExistsError).message || "Account email already exists."), + new ConflictApiError( + (error as AccountEmailAlreadyExistsError).message || "Account email already exists." + ), }; const genericDuplicateEntityRule: ErrorToApiRule = { diff --git a/modules/identity/src/api/infrastructure/express/identity-account.routes.ts b/modules/identity/src/api/infrastructure/express/identity-account.routes.ts index 268328ba..5c94310c 100644 --- a/modules/identity/src/api/infrastructure/express/identity-account.routes.ts +++ b/modules/identity/src/api/infrastructure/express/identity-account.routes.ts @@ -1,11 +1,12 @@ -import { type NextFunction, type Request, type Response, Router } from "express"; import { type StartParams, validateRequest } from "@erp/core/api"; +import { type NextFunction, type Request, type Response, Router } from "express"; import { ChangeAccountPasswordRequestSchema, UpdateAccountProfileRequestSchema, } from "../../../common"; import type { IdentityInternalDeps } from "../di"; + import { ChangeCurrentAccountPasswordController, GetCurrentAccountController, diff --git a/modules/identity/src/api/infrastructure/express/identity-auth-api-error-mapper.ts b/modules/identity/src/api/infrastructure/express/identity-auth-api-error-mapper.ts index 7ec64316..b87039ce 100644 --- a/modules/identity/src/api/infrastructure/express/identity-auth-api-error-mapper.ts +++ b/modules/identity/src/api/infrastructure/express/identity-auth-api-error-mapper.ts @@ -14,10 +14,7 @@ import { RefreshTokenAccountMismatchError, RefreshTokenNotFoundError, } from "../../application"; -import { - RefreshTokenExpiredError, - RefreshTokenRevokedError, -} from "../../domain"; +import { RefreshTokenExpiredError, RefreshTokenRevokedError } from "../../domain"; const invalidCredentialsRule: ErrorToApiRule = { priority: 120, diff --git a/modules/identity/src/api/infrastructure/express/index.ts b/modules/identity/src/api/infrastructure/express/index.ts index 7e75ca73..2cb0e522 100644 --- a/modules/identity/src/api/infrastructure/express/index.ts +++ b/modules/identity/src/api/infrastructure/express/index.ts @@ -1,6 +1,6 @@ -export * from "./identity-account-api-error-mapper"; -export * from "./identity-account.routes"; export * from "./controllers"; +export * from "./identity-account.routes"; +export * from "./identity-account-api-error-mapper"; export * from "./identity-auth.routes"; export * from "./identity-auth-api-error-mapper"; export * from "./middlewares"; diff --git a/modules/identity/src/api/infrastructure/express/middlewares/index.ts b/modules/identity/src/api/infrastructure/express/middlewares/index.ts index c9ed6cd8..c6793258 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/index.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/index.ts @@ -1,5 +1,5 @@ export * from "./authenticate-user.middleware"; +export * from "./identity-authenticate-request"; export * from "./require-authenticated.middleware"; export * from "./require-company-context.middleware"; export * from "./require-identity-middlewares"; -export * from "./identity-authenticate-request"; diff --git a/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts b/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts index 04ec8ba7..04e5a7aa 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts @@ -1,4 +1,4 @@ -import { ExpressController, UnauthorizedApiError, type RequestWithAuth } from "@erp/core/api"; +import { ExpressController, type RequestWithAuth, UnauthorizedApiError } from "@erp/core/api"; import type { NextFunction, Request, Response } from "express"; export function requireAuthenticated() { diff --git a/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts b/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts index 6cdd27a2..cf9b0600 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts @@ -110,6 +110,8 @@ async function findActiveCompany( companyId: UniqueID ): Promise> { // Algunos módulos solo validan membership para no introducir un ciclo con companies. - if (!companyFinder) return Result.ok(true); + if (!companyFinder) { + return Result.ok(true); + } return await companyFinder.existsActiveById({ companyId }); } diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-membership-domain.mapper.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-membership-domain.mapper.ts index 8af0385c..16f4bd07 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-membership-domain.mapper.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-company-membership-domain.mapper.ts @@ -17,21 +17,35 @@ export class SequelizeCompanyMembershipDomainMapper { ): Result { try { const id = UniqueID.create(source.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } const accountId = UniqueID.create(source.account_id); - if (accountId.isFailure) return Result.fail(accountId.error); + if (accountId.isFailure) { + return Result.fail(accountId.error); + } const companyId = UniqueID.create(source.company_id); - if (companyId.isFailure) return Result.fail(companyId.error); + if (companyId.isFailure) { + return Result.fail(companyId.error); + } const status = CompanyMembershipStatus.create(source.status); - if (status.isFailure) return Result.fail(status.error); + if (status.isFailure) { + return Result.fail(status.error); + } const createdAt = date(source.created_at); - if (createdAt.isFailure) return Result.fail(createdAt.error); + if (createdAt.isFailure) { + return Result.fail(createdAt.error); + } const updatedAt = date(source.updated_at); - if (updatedAt.isFailure) return Result.fail(updatedAt.error); + if (updatedAt.isFailure) { + return Result.fail(updatedAt.error); + } const roles: UniqueID[] = []; for (const value of roleIds) { const role = UniqueID.create(value); - if (role.isFailure) return Result.fail(role.error); + if (role.isFailure) { + return Result.fail(role.error); + } roles.push(role.data); } const membership = CompanyMembership.rehydrate( diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.test.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.test.ts index c3971080..e77b5640 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.test.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.test.ts @@ -42,7 +42,9 @@ test("modelo declara columnas, JSON y código único por compañía", () => { "updated_at", ]); assert.ok(attributes.permission_codes.type instanceof DataTypes.JSON); - for (const value of Object.values(attributes)) assert.equal(value.allowNull, false); + for (const value of Object.values(attributes)) { + assert.equal(value.allowNull, false); + } assert.equal(RoleModel.options.timestamps, false); const unique = RoleModel.options.indexes?.find((index) => index.unique); assert.deepEqual(unique?.fields, ["company_id", "code"]); diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.ts index c003465c..aa24e0b0 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/mappers/sequelize-role-domain.mapper.ts @@ -14,17 +14,29 @@ export class SequelizeRoleDomainMapper { public mapToDomain(source: RoleModel): Result { try { const id = UniqueID.create(source.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } const companyId = UniqueID.create(source.company_id); - if (companyId.isFailure) return Result.fail(companyId.error); + if (companyId.isFailure) { + return Result.fail(companyId.error); + } const code = RoleCode.create(source.code); - if (code.isFailure) return Result.fail(code.error); + if (code.isFailure) { + return Result.fail(code.error); + } const name = Name.create(source.name); - if (name.isFailure) return Result.fail(name.error); + if (name.isFailure) { + return Result.fail(name.error); + } const createdAt = toUtcDate(source.created_at); - if (createdAt.isFailure) return Result.fail(createdAt.error); + if (createdAt.isFailure) { + return Result.fail(createdAt.error); + } const updatedAt = toUtcDate(source.updated_at); - if (updatedAt.isFailure) return Result.fail(updatedAt.error); + if (updatedAt.isFailure) { + return Result.fail(updatedAt.error); + } // MariaDB puede entregar JSON como texto. Solo se comprueba su forma de persistencia. const permissions: unknown = typeof source.permission_codes === "string" diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-account.repository.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-account.repository.ts index dd586e33..1d2f7f18 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-account.repository.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-account.repository.ts @@ -1,7 +1,6 @@ import { SequelizeRepository, translateSequelizeError } from "@erp/core/api"; import { Maybe, Result } from "@repo/rdx-utils"; -import type { Transaction } from "sequelize"; -import type { Sequelize } from "sequelize"; +import type { Sequelize, Transaction } from "sequelize"; import type { IAccountRepository } from "../../../../application"; import type { Account } from "../../../../domain"; diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.test.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.test.ts index 50069553..698a0d03 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.test.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.test.ts @@ -141,8 +141,9 @@ test("save crea membership y roles con transacción en todas las operaciones", a mocks.find.mock.calls[0].arguments[0], mocks.create.mock.calls[0].arguments[1], mocks.add.mock.calls[0].arguments[1], - ]) + ]) { assert.equal(options?.transaction, transaction); + } }); test("save sincroniza A B a B C sin recrear B", async (t) => { const mocks = setup(t, [a, b]); @@ -245,8 +246,9 @@ test("membership deshabilitada puede retirar todos sus roles", async (t) => { mocks.links.mock.calls[0].arguments[0], mocks.update.mock.calls[0].arguments[1], mocks.remove.mock.calls[0].arguments[0], - ]) + ]) { assert.equal(options?.transaction, transaction); + } }); test("lectura actual bloquea membership y asignaciones en la misma transacción", async (t) => { diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.ts index 3bbf7016..48e131f7 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-company-membership.repository.ts @@ -29,11 +29,14 @@ export class SequelizeCompanyMembershipRepository implements ICompanyMembershipR transaction: unknown, updateOnly: boolean ): Promise> { - if (!transaction) + if (!transaction) { return Result.fail(new Error("Company membership writes require an external transaction.")); + } try { const mapped = this.mapper.mapToPersistence(source); - if (mapped.isFailure) return Result.fail(mapped.error); + if (mapped.isFailure) { + return Result.fail(mapped.error); + } const { membership, roleIds } = mapped.data; const options = { transaction: transaction as Transaction }; const roles = @@ -63,8 +66,9 @@ export class SequelizeCompanyMembershipRepository implements ICompanyMembershipR ) { return Result.fail(new Error("Membership identity cannot change.")); } - if (!current && updateOnly) + if (!current && updateOnly) { return Result.fail(new Error("Company membership not found for update.")); + } const links = current ? await CompanyMembershipRoleModel.findAll({ where: { membership_id: membership.id }, @@ -84,16 +88,18 @@ export class SequelizeCompanyMembershipRepository implements ICompanyMembershipR const wanted = new Set(roleIds); const removed = [...old].filter((id) => !wanted.has(id)); const added = roleIds.filter((id) => !old.has(id)); - if (removed.length > 0) + if (removed.length > 0) { await CompanyMembershipRoleModel.destroy({ where: { membership_id: membership.id, role_id: { [Op.in]: removed } }, ...options, }); - if (added.length > 0) + } + if (added.length > 0) { await CompanyMembershipRoleModel.bulkCreate( added.map((roleId) => ({ membership_id: membership.id, role_id: roleId })), options ); + } return Result.ok(); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -112,7 +118,9 @@ export class SequelizeCompanyMembershipRepository implements ICompanyMembershipR transaction: params.transaction as Transaction | undefined, ...(params.currentRead && params.transaction ? { lock: true } : {}), }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } return (await this.hydrate(row, params.transaction, params.currentRead)).map(Maybe.some); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -143,7 +151,9 @@ export class SequelizeCompanyMembershipRepository implements ICompanyMembershipR const memberships: CompanyMembership[] = []; for (const row of rows) { const result = await this.hydrate(row, transaction); - if (result.isFailure) return Result.fail(result.error); + if (result.isFailure) { + return Result.fail(result.error); + } memberships.push(result.data); } return Result.ok(new Collection(memberships)); diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-refresh-token.repository.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-refresh-token.repository.ts index edad3f55..ca6f198a 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-refresh-token.repository.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-refresh-token.repository.ts @@ -1,6 +1,6 @@ import { SequelizeRepository, translateSequelizeError } from "@erp/core/api"; import { Maybe, Result } from "@repo/rdx-utils"; -import { type Sequelize, type Transaction } from "sequelize"; +import type { Sequelize, Transaction } from "sequelize"; import type { IRefreshTokenRepository } from "../../../../application"; import type { RefreshToken } from "../../../../domain"; diff --git a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-role.repository.ts b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-role.repository.ts index dbeaafe7..493aedcc 100644 --- a/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-role.repository.ts +++ b/modules/identity/src/api/infrastructure/persistence/sequelize/repositories/sequelize-role.repository.ts @@ -21,7 +21,9 @@ export class SequelizeRoleRepository implements IRoleRepository { // Una lectura bloqueante ve el ganador incluso bajo REPEATABLE READ. ...(params.currentRead && params.transaction ? { lock: true } : {}), }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } return this.mapper.mapToDomain(row).map(Maybe.some); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -31,7 +33,9 @@ export class SequelizeRoleRepository implements IRoleRepository { public async save(role: Role, transaction?: unknown): Promise> { try { const mapped = this.mapper.mapToPersistence(role); - if (mapped.isFailure) return Result.fail(mapped.error); + if (mapped.isFailure) { + return Result.fail(mapped.error); + } const where = { id: role.id.toPrimitive(), company_id: role.companyId.toPrimitive() }; const options = { transaction: transaction as Transaction | undefined }; const existing = await RoleModel.findOne({ where, ...options }); @@ -49,7 +53,9 @@ export class SequelizeRoleRepository implements IRoleRepository { public async update(role: Role, transaction?: unknown): Promise> { try { const mapped = this.mapper.mapToPersistence(role); - if (mapped.isFailure) return Result.fail(mapped.error); + if (mapped.isFailure) { + return Result.fail(mapped.error); + } await RoleModel.update(mapped.data, { where: { id: role.id.toPrimitive(), company_id: role.companyId.toPrimitive() }, transaction: transaction as Transaction | undefined, @@ -80,7 +86,9 @@ export class SequelizeRoleRepository implements IRoleRepository { where: { id: params.roleId.toPrimitive(), company_id: params.companyId.toPrimitive() }, transaction: params.transaction as Transaction | undefined, }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } return this.mapper.mapToDomain(row).map(Maybe.some); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -90,7 +98,9 @@ export class SequelizeRoleRepository implements IRoleRepository { public findByIdsInCompany( params: Parameters[0] ): Promise, Error>> { - if (params.roleIds.length === 0) return Promise.resolve(Result.ok(new Collection([]))); + if (params.roleIds.length === 0) { + return Promise.resolve(Result.ok(new Collection([]))); + } return this.findMany( { id: { [Op.in]: params.roleIds.map((id) => id.toPrimitive()) }, @@ -121,7 +131,9 @@ export class SequelizeRoleRepository implements IRoleRepository { const roles: Role[] = []; for (const row of rows) { const mapped = this.mapper.mapToDomain(row); - if (mapped.isFailure) return Result.fail(mapped.error); + if (mapped.isFailure) { + return Result.fail(mapped.error); + } roles.push(mapped.data); } return Result.ok(new Collection(roles)); diff --git a/modules/identity/src/api/infrastructure/security/bcrypt-password-hasher.ts b/modules/identity/src/api/infrastructure/security/bcrypt-password-hasher.ts index 0d714e8e..7614e04a 100644 --- a/modules/identity/src/api/infrastructure/security/bcrypt-password-hasher.ts +++ b/modules/identity/src/api/infrastructure/security/bcrypt-password-hasher.ts @@ -1,5 +1,5 @@ -import bcrypt from "bcrypt"; import { Result } from "@repo/rdx-utils"; +import bcrypt from "bcrypt"; import type { IPasswordHasher } from "../../application"; import { PasswordHash } from "../../domain"; @@ -33,10 +33,7 @@ export class BcryptPasswordHasher implements IPasswordHasher { passwordHash: PasswordHash; }): Promise> { try { - const matches = await bcrypt.compare( - params.plainPassword, - params.passwordHash.toPrimitive() - ); + const matches = await bcrypt.compare(params.plainPassword, params.passwordHash.toPrimitive()); return Result.ok(matches); } catch (error: unknown) { return Result.fail( diff --git a/modules/identity/src/api/infrastructure/security/index.ts b/modules/identity/src/api/infrastructure/security/index.ts index 4a52da6b..367b8723 100644 --- a/modules/identity/src/api/infrastructure/security/index.ts +++ b/modules/identity/src/api/infrastructure/security/index.ts @@ -1,7 +1,7 @@ export * from "./bcrypt-password-hasher"; export * from "./crypto-refresh-token-generator"; export * from "./errors"; -export * from "./jwt-access-token-config"; export * from "./jsonwebtoken-access-token-issuer"; export * from "./jsonwebtoken-access-token-verifier"; +export * from "./jwt-access-token-config"; export * from "./sha256-refresh-token-hasher"; diff --git a/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-issuer.ts b/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-issuer.ts index 74bf07b1..3a3946b8 100644 --- a/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-issuer.ts +++ b/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-issuer.ts @@ -1,5 +1,5 @@ -import jwt from "jsonwebtoken"; import { Result } from "@repo/rdx-utils"; +import jwt from "jsonwebtoken"; import type { AccessTokenPayload, IAccessTokenIssuer } from "../../application"; diff --git a/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-verifier.ts b/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-verifier.ts index 7a9a0c0a..c89103cb 100644 --- a/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-verifier.ts +++ b/modules/identity/src/api/infrastructure/security/jsonwebtoken-access-token-verifier.ts @@ -1,5 +1,5 @@ -import jwt from "jsonwebtoken"; import { Result } from "@repo/rdx-utils"; +import jwt from "jsonwebtoken"; import { z } from "zod/v4"; import type { IAccessTokenVerifier, VerifiedAccessTokenPayload } from "../../application"; diff --git a/modules/identity/src/common/dto/account/response/change-account-password.response.dto.ts b/modules/identity/src/common/dto/account/response/change-account-password.response.dto.ts index 816e3c46..69fa6e66 100644 --- a/modules/identity/src/common/dto/account/response/change-account-password.response.dto.ts +++ b/modules/identity/src/common/dto/account/response/change-account-password.response.dto.ts @@ -4,6 +4,4 @@ export const ChangeAccountPasswordResponseSchema = z.object({ success: z.literal(true), }); -export type ChangeAccountPasswordResponseDTO = z.infer< - typeof ChangeAccountPasswordResponseSchema ->; +export type ChangeAccountPasswordResponseDTO = z.infer; diff --git a/modules/identity/src/common/dto/companies/response/update-current-company.response.dto.ts b/modules/identity/src/common/dto/companies/response/update-current-company.response.dto.ts index a718501d..af91f51c 100644 --- a/modules/identity/src/common/dto/companies/response/update-current-company.response.dto.ts +++ b/modules/identity/src/common/dto/companies/response/update-current-company.response.dto.ts @@ -6,6 +6,4 @@ export const UpdateCurrentCompanyResponseSchema = z.object({ company: CompanyDetailSchema, }); -export type UpdateCurrentCompanyResponseDTO = z.infer< - typeof UpdateCurrentCompanyResponseSchema ->; +export type UpdateCurrentCompanyResponseDTO = z.infer; diff --git a/modules/identity/src/common/dto/company-users/request/update-company-user-roles.request.dto.ts b/modules/identity/src/common/dto/company-users/request/update-company-user-roles.request.dto.ts index e71afb63..9943b2d0 100644 --- a/modules/identity/src/common/dto/company-users/request/update-company-user-roles.request.dto.ts +++ b/modules/identity/src/common/dto/company-users/request/update-company-user-roles.request.dto.ts @@ -8,7 +8,5 @@ export const UpdateCompanyUserRolesBodySchema = z.object({ role_ids: z.array(z.uuid()).min(1), }); -export type UpdateCompanyUserRolesParamsDTO = z.infer< - typeof UpdateCompanyUserRolesParamsSchema ->; +export type UpdateCompanyUserRolesParamsDTO = z.infer; export type UpdateCompanyUserRolesBodyDTO = z.infer; diff --git a/modules/identity/src/common/dto/roles/request/delete-company-role-by-id.request.dto.ts b/modules/identity/src/common/dto/roles/request/delete-company-role-by-id.request.dto.ts index 4b1a2823..41446d42 100644 --- a/modules/identity/src/common/dto/roles/request/delete-company-role-by-id.request.dto.ts +++ b/modules/identity/src/common/dto/roles/request/delete-company-role-by-id.request.dto.ts @@ -4,6 +4,4 @@ export const DeleteCompanyRoleByIdRequestSchema = z.object({ role_id: z.uuid(), }); -export type DeleteCompanyRoleByIdRequestDTO = z.infer< - typeof DeleteCompanyRoleByIdRequestSchema ->; +export type DeleteCompanyRoleByIdRequestDTO = z.infer; diff --git a/modules/identity/src/common/dto/roles/response/delete-company-role-by-id.response.dto.ts b/modules/identity/src/common/dto/roles/response/delete-company-role-by-id.response.dto.ts index f2eb7f96..c5be29d2 100644 --- a/modules/identity/src/common/dto/roles/response/delete-company-role-by-id.response.dto.ts +++ b/modules/identity/src/common/dto/roles/response/delete-company-role-by-id.response.dto.ts @@ -4,6 +4,4 @@ export const DeleteCompanyRoleByIdResponseSchema = z.object({ success: z.literal(true), }); -export type DeleteCompanyRoleByIdResponseDTO = z.infer< - typeof DeleteCompanyRoleByIdResponseSchema ->; +export type DeleteCompanyRoleByIdResponseDTO = z.infer; diff --git a/modules/identity/src/common/dto/roles/response/update-company-role-by-id.response.dto.ts b/modules/identity/src/common/dto/roles/response/update-company-role-by-id.response.dto.ts index 98ccbd3b..e6bfde6a 100644 --- a/modules/identity/src/common/dto/roles/response/update-company-role-by-id.response.dto.ts +++ b/modules/identity/src/common/dto/roles/response/update-company-role-by-id.response.dto.ts @@ -6,6 +6,4 @@ export const UpdateCompanyRoleByIdResponseSchema = z.object({ role: RoleDetailSchema, }); -export type UpdateCompanyRoleByIdResponseDTO = z.infer< - typeof UpdateCompanyRoleByIdResponseSchema ->; +export type UpdateCompanyRoleByIdResponseDTO = z.infer; diff --git a/modules/identity/src/web/account-routes.tsx b/modules/identity/src/web/account-routes.tsx index f510f4f0..8f12277b 100644 --- a/modules/identity/src/web/account-routes.tsx +++ b/modules/identity/src/web/account-routes.tsx @@ -1,6 +1,5 @@ -import { lazy } from "react"; - import type { ModuleClientParams, ModuleRouteObject } from "@erp/core/client"; +import { lazy } from "react"; const AccountPage = lazy(() => import("./account").then((module) => ({ diff --git a/modules/identity/src/web/account/controllers/use-account-page-controller.ts b/modules/identity/src/web/account/controllers/use-account-page-controller.ts index 9ec3a5cb..f3ec221b 100644 --- a/modules/identity/src/web/account/controllers/use-account-page-controller.ts +++ b/modules/identity/src/web/account/controllers/use-account-page-controller.ts @@ -11,7 +11,7 @@ import { showSuccessToast, showWarningToast, } from "@repo/rdx-ui/helpers"; -import { useCallback, useEffect, useId, useMemo, useState, type SubmitEvent } from "react"; +import { type SubmitEvent, useCallback, useEffect, useId, useMemo, useState } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../i18n"; diff --git a/modules/identity/src/web/account/shared/adapters/account-profile.adapter.ts b/modules/identity/src/web/account/shared/adapters/account-profile.adapter.ts index d2712712..131bd11b 100644 --- a/modules/identity/src/web/account/shared/adapters/account-profile.adapter.ts +++ b/modules/identity/src/web/account/shared/adapters/account-profile.adapter.ts @@ -6,9 +6,7 @@ import type { import type { AccountProfile } from "../entities"; export const AccountProfileAdapter = { - fromDto( - dto: GetAccountProfileResponseDTO | UpdateAccountProfileResponseDTO - ): AccountProfile { + fromDto(dto: GetAccountProfileResponseDTO | UpdateAccountProfileResponseDTO): AccountProfile { return { id: dto.id, email: dto.email, diff --git a/modules/identity/src/web/account/shared/hooks/use-change-current-account-password-mutation.ts b/modules/identity/src/web/account/shared/hooks/use-change-current-account-password-mutation.ts index bfc9c5f1..49f4d866 100644 --- a/modules/identity/src/web/account/shared/hooks/use-change-current-account-password-mutation.ts +++ b/modules/identity/src/web/account/shared/hooks/use-change-current-account-password-mutation.ts @@ -1,9 +1,9 @@ import { useDataSource } from "@erp/core/hooks"; import { - ChangeAccountPasswordRequestSchema, - ChangeAccountPasswordResponseSchema, type ChangeAccountPasswordRequestDTO, + ChangeAccountPasswordRequestSchema, type ChangeAccountPasswordResponseDTO, + ChangeAccountPasswordResponseSchema, } from "@erp/identity/common"; import { useMutation } from "@tanstack/react-query"; diff --git a/modules/identity/src/web/account/shared/hooks/use-current-account-query.ts b/modules/identity/src/web/account/shared/hooks/use-current-account-query.ts index 476846b0..66d54391 100644 --- a/modules/identity/src/web/account/shared/hooks/use-current-account-query.ts +++ b/modules/identity/src/web/account/shared/hooks/use-current-account-query.ts @@ -3,8 +3,8 @@ import { GetAccountProfileResponseSchema } from "@erp/identity/common"; import { type DefaultError, type UseQueryResult, useQuery } from "@tanstack/react-query"; import { AccountProfileAdapter } from "../adapters"; -import type { AccountProfile } from "../entities"; import { getCurrentAccount } from "../api"; +import type { AccountProfile } from "../entities"; import { CURRENT_ACCOUNT_QUERY_KEY } from "./keys"; diff --git a/modules/identity/src/web/account/shared/hooks/use-update-current-account-mutation.ts b/modules/identity/src/web/account/shared/hooks/use-update-current-account-mutation.ts index d16dad47..743de6ae 100644 --- a/modules/identity/src/web/account/shared/hooks/use-update-current-account-mutation.ts +++ b/modules/identity/src/web/account/shared/hooks/use-update-current-account-mutation.ts @@ -1,14 +1,14 @@ import { useDataSource } from "@erp/core/hooks"; import { + type UpdateAccountProfileRequestDTO, UpdateAccountProfileRequestSchema, UpdateAccountProfileResponseSchema, - type UpdateAccountProfileRequestDTO, } from "@erp/identity/common"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { AccountProfileAdapter } from "../adapters"; -import type { AccountProfile } from "../entities"; import { updateCurrentAccount } from "../api"; +import type { AccountProfile } from "../entities"; import { CURRENT_ACCOUNT_QUERY_KEY } from "./keys"; diff --git a/modules/identity/src/web/account/utils/build-account-profile-form.ts b/modules/identity/src/web/account/utils/build-account-profile-form.ts index 2a88c307..d1d930a8 100644 --- a/modules/identity/src/web/account/utils/build-account-profile-form.ts +++ b/modules/identity/src/web/account/utils/build-account-profile-form.ts @@ -1,5 +1,5 @@ -import type { AccountProfile } from "../shared"; import type { AccountProfileForm } from "../entities"; +import type { AccountProfile } from "../shared"; export const buildAccountProfileForm = (account: AccountProfile): AccountProfileForm => { return { diff --git a/modules/identity/src/web/context/identity-auth-session.context.tsx b/modules/identity/src/web/context/identity-auth-session.context.tsx index 29d65fe6..aa0a3f94 100644 --- a/modules/identity/src/web/context/identity-auth-session.context.tsx +++ b/modules/identity/src/web/context/identity-auth-session.context.tsx @@ -1,123 +1,130 @@ import { isApiClientError } from "@erp/core/client"; import { useDataSource } from "@erp/core/hooks"; import type { CurrentSessionResponseDTO } from "@erp/identity/common"; -import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; - -import type { LoginForm } from "../login/entities"; -import { - AvailableCompaniesResponseDTOSchema, - activeCompanyStorage, - authTokenStorage, - findAvailableCompanies, - mapAvailableCompany, - mapCurrentSessionToAuthSession, - resolveAuthenticatedRedirectPath, - resolveCompanySession, - useLoginMutation, - useLogoutMutation, - useRefreshSessionMutation, -} from "../shared"; -import { getCurrentSession } from "../shared/api"; +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; + +import type { LoginForm } from "../login/entities"; +import { + AvailableCompaniesResponseDTOSchema, + activeCompanyStorage, + authTokenStorage, + findAvailableCompanies, + mapAvailableCompany, + mapCurrentSessionToAuthSession, + resolveAuthenticatedRedirectPath, + resolveCompanySession, + useLoginMutation, + useLogoutMutation, + useRefreshSessionMutation, +} from "../shared"; +import { getCurrentSession } from "../shared/api"; import type { ActiveCompany, AuthSession, AuthTokens, CompanySession } from "../shared/entities"; - -import { - resetIdentityAuthSessionRuntimeHandlers, - setIdentityAuthSessionRuntimeHandlers, -} from "./identity-auth-session.runtime"; - -export interface IdentityAuthSessionContextValue { - session: AuthSession | null; - account: AuthSession["account"] | null; - companySession: CompanySession; - availableCompanies: ActiveCompany[]; - activeCompany: ActiveCompany | null; - isAuthenticated: boolean; - isLoading: boolean; - login: (params: LoginForm) => Promise; - logout: () => Promise; - restoreSession: () => Promise; - refreshSession: () => Promise; - refreshAccessToken: () => Promise; - selectActiveCompany: (companyId: string) => ActiveCompany | null; - changeActiveCompany: (companyId: string) => Promise; - updateActiveCompany: (company: { - id: string; - legalName: string; - tradeName: string | null; - }) => void; - updateAccount: (account: { - id: string; - email: string; - name: string; - avatarUrl: string | null; - languageCode: string; - }) => void; - getAuthenticatedRedirectPath: (params?: { - returnTo?: string | null; - fallbackPath?: string; - }) => string; - clearSession: () => void; -} - -const IdentityAuthSessionContext = createContext( - undefined -); - + +import { + resetIdentityAuthSessionRuntimeHandlers, + setIdentityAuthSessionRuntimeHandlers, +} from "./identity-auth-session.runtime"; + +export interface IdentityAuthSessionContextValue { + session: AuthSession | null; + account: AuthSession["account"] | null; + companySession: CompanySession; + availableCompanies: ActiveCompany[]; + activeCompany: ActiveCompany | null; + isAuthenticated: boolean; + isLoading: boolean; + login: (params: LoginForm) => Promise; + logout: () => Promise; + restoreSession: () => Promise; + refreshSession: () => Promise; + refreshAccessToken: () => Promise; + selectActiveCompany: (companyId: string) => ActiveCompany | null; + changeActiveCompany: (companyId: string) => Promise; + updateActiveCompany: (company: { + id: string; + legalName: string; + tradeName: string | null; + }) => void; + updateAccount: (account: { + id: string; + email: string; + name: string; + avatarUrl: string | null; + languageCode: string; + }) => void; + getAuthenticatedRedirectPath: (params?: { + returnTo?: string | null; + fallbackPath?: string; + }) => string; + clearSession: () => void; +} + +const IdentityAuthSessionContext = createContext( + undefined +); + const EMPTY_COMPANY_SESSION: CompanySession = { availableCompanies: [], activeCompany: null, }; -const isUnauthorizedError = (error: unknown) => - isApiClientError(error) && error.status === 401; - -export function IdentityAuthSessionProvider({ children }: { children: React.ReactNode }) { - const dataSource = useDataSource(); +const isUnauthorizedError = (error: unknown) => isApiClientError(error) && error.status === 401; + +export function IdentityAuthSessionProvider({ children }: { children: React.ReactNode }) { + const dataSource = useDataSource(); const loginMutation = useLoginMutation(); const logoutMutation = useLogoutMutation(); const refreshSessionMutation = useRefreshSessionMutation(); const refreshSessionMutationAsync = refreshSessionMutation.mutateAsync; - + const [session, setSession] = useState(null); const [companySession, setCompanySession] = useState(EMPTY_COMPANY_SESSION); const [isRestoringSession, setIsRestoringSession] = useState(authTokenStorage.hasAccessToken()); const restoreInFlightRef = useRef | null>(null); - - const clearSession = useCallback(() => { - authTokenStorage.clear(); - activeCompanyStorage.clear(); - setSession(null); - setCompanySession(EMPTY_COMPANY_SESSION); - }, []); - - const hydrateCompanySession = useCallback( - async (accessToken: string) => { - const response = await findAvailableCompanies(dataSource, accessToken); - const parsedResponse = AvailableCompaniesResponseDTOSchema.safeParse(response); - - if (!parsedResponse.success) { - throw new Error("Invalid available companies response"); - } - - const availableCompanies = parsedResponse.data.companies.map(mapAvailableCompany); - const nextCompanySession = resolveCompanySession({ - companies: availableCompanies, - persistedActiveCompanyId: activeCompanyStorage.getActiveCompanyId(), - }); - - if (nextCompanySession.activeCompany) { - activeCompanyStorage.setActiveCompanyId(nextCompanySession.activeCompany.id); - } else { - activeCompanyStorage.clear(); - } - - setCompanySession(nextCompanySession); - - return nextCompanySession; - }, - [dataSource] - ); - + + const clearSession = useCallback(() => { + authTokenStorage.clear(); + activeCompanyStorage.clear(); + setSession(null); + setCompanySession(EMPTY_COMPANY_SESSION); + }, []); + + const hydrateCompanySession = useCallback( + async (accessToken: string) => { + const response = await findAvailableCompanies(dataSource, accessToken); + const parsedResponse = AvailableCompaniesResponseDTOSchema.safeParse(response); + + if (!parsedResponse.success) { + throw new Error("Invalid available companies response"); + } + + const availableCompanies = parsedResponse.data.companies.map(mapAvailableCompany); + const nextCompanySession = resolveCompanySession({ + companies: availableCompanies, + persistedActiveCompanyId: activeCompanyStorage.getActiveCompanyId(), + }); + + if (nextCompanySession.activeCompany) { + activeCompanyStorage.setActiveCompanyId(nextCompanySession.activeCompany.id); + } else { + activeCompanyStorage.clear(); + } + + setCompanySession(nextCompanySession); + + return nextCompanySession; + }, + [dataSource] + ); + const restoreSession = useCallback(() => { if (restoreInFlightRef.current) { return restoreInFlightRef.current; @@ -144,7 +151,7 @@ export function IdentityAuthSessionProvider({ children }: { children: React.Reac try { response = await getCurrentSession(dataSource, activeTokens.accessToken); } catch (error) { - if (!isUnauthorizedError(error) || !activeTokens.refreshToken) { + if (!(isUnauthorizedError(error) && activeTokens.refreshToken)) { throw error; } @@ -179,265 +186,265 @@ export function IdentityAuthSessionProvider({ children }: { children: React.Reac return restorePromise; }, [clearSession, dataSource, hydrateCompanySession, refreshSessionMutationAsync]); - - const login = useCallback( - async (params: LoginForm) => { - const nextSession = await loginMutation.mutateAsync(params); - - authTokenStorage.setTokens({ - accessToken: nextSession.accessToken, - refreshToken: nextSession.refreshToken, - }); - - activeCompanyStorage.clear(); - await hydrateCompanySession(nextSession.accessToken); - setSession(nextSession); - - return nextSession; - }, - [hydrateCompanySession, loginMutation] - ); - - const logout = useCallback(async () => { - const tokens = authTokenStorage.getTokens(); - - try { - if (tokens.accessToken) { - await logoutMutation.mutateAsync({ - accessToken: tokens.accessToken, - refreshToken: tokens.refreshToken, - }); - } - } finally { - clearSession(); - } - }, [clearSession, logoutMutation]); - - const refreshSession = useCallback(async () => { - const tokens = authTokenStorage.getTokens(); - - if (!tokens.refreshToken) { - clearSession(); - return null; - } - - try { - const refreshedTokens = await refreshSessionMutation.mutateAsync({ - refreshToken: tokens.refreshToken, - }); - - authTokenStorage.setTokens(refreshedTokens); - await hydrateCompanySession(refreshedTokens.accessToken); - - if (session) { - const refreshedSession: AuthSession = { - ...session, - accessToken: refreshedTokens.accessToken, - refreshToken: refreshedTokens.refreshToken, - }; - - setSession(refreshedSession); - return refreshedSession; - } - - const currentSessionResponse = await getCurrentSession( - dataSource, - refreshedTokens.accessToken - ); - const refreshedSession = mapCurrentSessionToAuthSession(currentSessionResponse, { - accessToken: refreshedTokens.accessToken, - refreshToken: refreshedTokens.refreshToken, - }); - - setSession(refreshedSession); - - return refreshedSession; - } catch { - clearSession(); - return null; - } - }, [clearSession, dataSource, hydrateCompanySession, refreshSessionMutation, session]); - - const refreshAccessToken = useCallback(async () => { - const refreshedSession = await refreshSession(); - return refreshedSession?.accessToken ?? null; - }, [refreshSession]); - - const selectActiveCompany = useCallback( - (companyId: string) => { - if (companySession.activeCompany?.id === companyId) { - return companySession.activeCompany; - } - - const nextActiveCompany = - companySession.availableCompanies.find((company) => company.id === companyId) ?? null; - - if (!nextActiveCompany) { - return null; - } - - activeCompanyStorage.setActiveCompanyId(nextActiveCompany.id); - setCompanySession((currentSession) => ({ - ...currentSession, - activeCompany: nextActiveCompany, - })); - - return nextActiveCompany; - }, - [companySession.activeCompany, companySession.availableCompanies] - ); - - const changeActiveCompany = useCallback( - async (companyId: string) => { - return selectActiveCompany(companyId); - }, - [selectActiveCompany] - ); - - const updateActiveCompany = useCallback( - (company: { id: string; legalName: string; tradeName: string | null }) => { - setCompanySession((currentSession) => { - const availableCompanies = currentSession.availableCompanies.map((item) => - item.id === company.id - ? { - ...item, - legalName: company.legalName, - tradeName: company.tradeName, - } - : item - ); - - const activeCompany = - currentSession.activeCompany?.id === company.id - ? { - ...currentSession.activeCompany, - legalName: company.legalName, - tradeName: company.tradeName, - } - : currentSession.activeCompany; - - return { - availableCompanies, - activeCompany, - }; - }); - }, - [] - ); - - const updateAccount = useCallback( - (account: { - id: string; - email: string; - name: string; - avatarUrl: string | null; - languageCode: string; - }) => { - setSession((currentSession) => { - if (!currentSession || currentSession.account.id !== account.id) { - return currentSession; - } - - return { - ...currentSession, - account: { - ...currentSession.account, - email: account.email, - name: account.name, - avatarUrl: account.avatarUrl, - languageCode: account.languageCode, - }, - }; - }); - }, - [] - ); - - const getAuthenticatedRedirectPath = useCallback( - (params?: { returnTo?: string | null; fallbackPath?: string }) => { - return resolveAuthenticatedRedirectPath({ - companySession, - fallbackPath: params?.fallbackPath ?? "/proformas", - returnTo: params?.returnTo, - }); - }, - [companySession] - ); - - useEffect(() => { - restoreSession(); - }, [restoreSession]); - - useEffect(() => { - setIdentityAuthSessionRuntimeHandlers({ - clearSession, - refreshAccessToken, - }); - - return () => { - resetIdentityAuthSessionRuntimeHandlers(); - }; - }, [clearSession, refreshAccessToken]); - - const value = useMemo( - () => ({ - session, - account: session?.account ?? null, - companySession, - availableCompanies: companySession.availableCompanies, - activeCompany: companySession.activeCompany, - isAuthenticated: Boolean(session), - isLoading: - isRestoringSession || - loginMutation.isPending || - logoutMutation.isPending || - refreshSessionMutation.isPending, - login, - logout, - restoreSession, - refreshSession, - refreshAccessToken, - selectActiveCompany, - changeActiveCompany, - updateActiveCompany, - updateAccount, - getAuthenticatedRedirectPath, - clearSession, - }), - [ - changeActiveCompany, - clearSession, - companySession, - getAuthenticatedRedirectPath, - isRestoringSession, - login, - loginMutation.isPending, - logout, - logoutMutation.isPending, - refreshAccessToken, - refreshSession, - refreshSessionMutation.isPending, - restoreSession, - selectActiveCompany, - updateActiveCompany, - updateAccount, - session, - ] - ); - - return ( - - {children} - - ); -} - -export function useIdentityAuthSession() { - const context = useContext(IdentityAuthSessionContext); - - if (!context) { - throw new Error("useIdentityAuthSession must be used within a IdentityAuthSessionProvider"); - } - - return context; -} + + const login = useCallback( + async (params: LoginForm) => { + const nextSession = await loginMutation.mutateAsync(params); + + authTokenStorage.setTokens({ + accessToken: nextSession.accessToken, + refreshToken: nextSession.refreshToken, + }); + + activeCompanyStorage.clear(); + await hydrateCompanySession(nextSession.accessToken); + setSession(nextSession); + + return nextSession; + }, + [hydrateCompanySession, loginMutation] + ); + + const logout = useCallback(async () => { + const tokens = authTokenStorage.getTokens(); + + try { + if (tokens.accessToken) { + await logoutMutation.mutateAsync({ + accessToken: tokens.accessToken, + refreshToken: tokens.refreshToken, + }); + } + } finally { + clearSession(); + } + }, [clearSession, logoutMutation]); + + const refreshSession = useCallback(async () => { + const tokens = authTokenStorage.getTokens(); + + if (!tokens.refreshToken) { + clearSession(); + return null; + } + + try { + const refreshedTokens = await refreshSessionMutation.mutateAsync({ + refreshToken: tokens.refreshToken, + }); + + authTokenStorage.setTokens(refreshedTokens); + await hydrateCompanySession(refreshedTokens.accessToken); + + if (session) { + const refreshedSession: AuthSession = { + ...session, + accessToken: refreshedTokens.accessToken, + refreshToken: refreshedTokens.refreshToken, + }; + + setSession(refreshedSession); + return refreshedSession; + } + + const currentSessionResponse = await getCurrentSession( + dataSource, + refreshedTokens.accessToken + ); + const refreshedSession = mapCurrentSessionToAuthSession(currentSessionResponse, { + accessToken: refreshedTokens.accessToken, + refreshToken: refreshedTokens.refreshToken, + }); + + setSession(refreshedSession); + + return refreshedSession; + } catch { + clearSession(); + return null; + } + }, [clearSession, dataSource, hydrateCompanySession, refreshSessionMutation, session]); + + const refreshAccessToken = useCallback(async () => { + const refreshedSession = await refreshSession(); + return refreshedSession?.accessToken ?? null; + }, [refreshSession]); + + const selectActiveCompany = useCallback( + (companyId: string) => { + if (companySession.activeCompany?.id === companyId) { + return companySession.activeCompany; + } + + const nextActiveCompany = + companySession.availableCompanies.find((company) => company.id === companyId) ?? null; + + if (!nextActiveCompany) { + return null; + } + + activeCompanyStorage.setActiveCompanyId(nextActiveCompany.id); + setCompanySession((currentSession) => ({ + ...currentSession, + activeCompany: nextActiveCompany, + })); + + return nextActiveCompany; + }, + [companySession.activeCompany, companySession.availableCompanies] + ); + + const changeActiveCompany = useCallback( + async (companyId: string) => { + return selectActiveCompany(companyId); + }, + [selectActiveCompany] + ); + + const updateActiveCompany = useCallback( + (company: { id: string; legalName: string; tradeName: string | null }) => { + setCompanySession((currentSession) => { + const availableCompanies = currentSession.availableCompanies.map((item) => + item.id === company.id + ? { + ...item, + legalName: company.legalName, + tradeName: company.tradeName, + } + : item + ); + + const activeCompany = + currentSession.activeCompany?.id === company.id + ? { + ...currentSession.activeCompany, + legalName: company.legalName, + tradeName: company.tradeName, + } + : currentSession.activeCompany; + + return { + availableCompanies, + activeCompany, + }; + }); + }, + [] + ); + + const updateAccount = useCallback( + (account: { + id: string; + email: string; + name: string; + avatarUrl: string | null; + languageCode: string; + }) => { + setSession((currentSession) => { + if (!currentSession || currentSession.account.id !== account.id) { + return currentSession; + } + + return { + ...currentSession, + account: { + ...currentSession.account, + email: account.email, + name: account.name, + avatarUrl: account.avatarUrl, + languageCode: account.languageCode, + }, + }; + }); + }, + [] + ); + + const getAuthenticatedRedirectPath = useCallback( + (params?: { returnTo?: string | null; fallbackPath?: string }) => { + return resolveAuthenticatedRedirectPath({ + companySession, + fallbackPath: params?.fallbackPath ?? "/proformas", + returnTo: params?.returnTo, + }); + }, + [companySession] + ); + + useEffect(() => { + restoreSession(); + }, [restoreSession]); + + useEffect(() => { + setIdentityAuthSessionRuntimeHandlers({ + clearSession, + refreshAccessToken, + }); + + return () => { + resetIdentityAuthSessionRuntimeHandlers(); + }; + }, [clearSession, refreshAccessToken]); + + const value = useMemo( + () => ({ + session, + account: session?.account ?? null, + companySession, + availableCompanies: companySession.availableCompanies, + activeCompany: companySession.activeCompany, + isAuthenticated: Boolean(session), + isLoading: + isRestoringSession || + loginMutation.isPending || + logoutMutation.isPending || + refreshSessionMutation.isPending, + login, + logout, + restoreSession, + refreshSession, + refreshAccessToken, + selectActiveCompany, + changeActiveCompany, + updateActiveCompany, + updateAccount, + getAuthenticatedRedirectPath, + clearSession, + }), + [ + changeActiveCompany, + clearSession, + companySession, + getAuthenticatedRedirectPath, + isRestoringSession, + login, + loginMutation.isPending, + logout, + logoutMutation.isPending, + refreshAccessToken, + refreshSession, + refreshSessionMutation.isPending, + restoreSession, + selectActiveCompany, + updateActiveCompany, + updateAccount, + session, + ] + ); + + return ( + + {children} + + ); +} + +export function useIdentityAuthSession() { + const context = useContext(IdentityAuthSessionContext); + + if (!context) { + throw new Error("useIdentityAuthSession must be used within a IdentityAuthSessionProvider"); + } + + return context; +} diff --git a/modules/identity/src/web/login/controllers/index.ts b/modules/identity/src/web/login/controllers/index.ts index 4394cd2b..05c6cc3b 100644 --- a/modules/identity/src/web/login/controllers/index.ts +++ b/modules/identity/src/web/login/controllers/index.ts @@ -1,3 +1,2 @@ export * from "./use-login-controller"; export * from "./use-login-page-controller"; - diff --git a/modules/identity/src/web/login/entities/index.ts b/modules/identity/src/web/login/entities/index.ts index af2cce15..2ba2e37a 100644 --- a/modules/identity/src/web/login/entities/index.ts +++ b/modules/identity/src/web/login/entities/index.ts @@ -1,2 +1 @@ export * from "./login-form.entity"; - diff --git a/modules/identity/src/web/login/entities/login-form.entity.ts b/modules/identity/src/web/login/entities/login-form.entity.ts index 0d0b25a2..192797a0 100644 --- a/modules/identity/src/web/login/entities/login-form.entity.ts +++ b/modules/identity/src/web/login/entities/login-form.entity.ts @@ -7,4 +7,3 @@ export const LoginFormSchema = LoginWithPasswordRequestSchema.extend({ }); export type LoginForm = z.infer; - diff --git a/modules/identity/src/web/login/index.ts b/modules/identity/src/web/login/index.ts index 3bab01b3..9d1e3db6 100644 --- a/modules/identity/src/web/login/index.ts +++ b/modules/identity/src/web/login/index.ts @@ -1,4 +1,3 @@ export * from "./controllers"; export * from "./entities"; export * from "./ui"; - diff --git a/modules/identity/src/web/login/ui/index.ts b/modules/identity/src/web/login/ui/index.ts index 3f46e22e..e1cc1421 100644 --- a/modules/identity/src/web/login/ui/index.ts +++ b/modules/identity/src/web/login/ui/index.ts @@ -1,3 +1,2 @@ export * from "./login-card"; export * from "./login-page"; - diff --git a/modules/identity/src/web/shared/adapters/active-company.adapter.ts b/modules/identity/src/web/shared/adapters/active-company.adapter.ts index b18fb2cb..7d9103f1 100644 --- a/modules/identity/src/web/shared/adapters/active-company.adapter.ts +++ b/modules/identity/src/web/shared/adapters/active-company.adapter.ts @@ -42,7 +42,7 @@ export function resolveCompanySession(params: { persistedActiveCompanyId: string | null; }) { const persistedCompany = params.persistedActiveCompanyId - ? params.companies.find((company) => company.id === params.persistedActiveCompanyId) ?? null + ? (params.companies.find((company) => company.id === params.persistedActiveCompanyId) ?? null) : null; if (persistedCompany) { diff --git a/modules/identity/src/web/shared/adapters/auth-session.adapter.ts b/modules/identity/src/web/shared/adapters/auth-session.adapter.ts index aa8e8be8..7b130428 100644 --- a/modules/identity/src/web/shared/adapters/auth-session.adapter.ts +++ b/modules/identity/src/web/shared/adapters/auth-session.adapter.ts @@ -6,9 +6,7 @@ import type { import type { AuthSession, AuthTokens } from "../entities"; -export function mapAuthenticatedAccount( - dto: AuthenticatedAccountDTO -): AuthSession["account"] { +export function mapAuthenticatedAccount(dto: AuthenticatedAccountDTO): AuthSession["account"] { return { id: dto.id, email: dto.email, @@ -18,9 +16,7 @@ export function mapAuthenticatedAccount( }; } -export function mapLoginResponseToAuthSession( - dto: LoginWithPasswordResponseDTO -): AuthSession { +export function mapLoginResponseToAuthSession(dto: LoginWithPasswordResponseDTO): AuthSession { return { accessToken: dto.access_token, refreshToken: dto.refresh_token, @@ -38,4 +34,3 @@ export function mapCurrentSessionToAuthSession( account: mapAuthenticatedAccount(dto.account), }; } - diff --git a/modules/identity/src/web/shared/api/get-current-session.api.ts b/modules/identity/src/web/shared/api/get-current-session.api.ts index e506f9d4..2ef496db 100644 --- a/modules/identity/src/web/shared/api/get-current-session.api.ts +++ b/modules/identity/src/web/shared/api/get-current-session.api.ts @@ -14,4 +14,3 @@ export function getCurrentSession( data: {}, }); } - diff --git a/modules/identity/src/web/shared/api/login.api.ts b/modules/identity/src/web/shared/api/login.api.ts index d0d898be..261986f7 100644 --- a/modules/identity/src/web/shared/api/login.api.ts +++ b/modules/identity/src/web/shared/api/login.api.ts @@ -1,5 +1,8 @@ import type { IDataSource } from "@erp/core/client"; -import type { LoginWithPasswordRequestDTO, LoginWithPasswordResponseDTO } from "@erp/identity/common"; +import type { + LoginWithPasswordRequestDTO, + LoginWithPasswordResponseDTO, +} from "@erp/identity/common"; export function login( dataSource: IDataSource, @@ -11,4 +14,3 @@ export function login( data: params, }); } - diff --git a/modules/identity/src/web/shared/api/logout.api.ts b/modules/identity/src/web/shared/api/logout.api.ts index f32e33e4..55cd61ff 100644 --- a/modules/identity/src/web/shared/api/logout.api.ts +++ b/modules/identity/src/web/shared/api/logout.api.ts @@ -6,10 +6,7 @@ export interface LogoutParams { refreshToken: string | null; } -export function logout( - dataSource: IDataSource, - params: LogoutParams -): Promise { +export function logout(dataSource: IDataSource, params: LogoutParams): Promise { return dataSource.custom({ path: "identity/auth/logout", method: "post", @@ -21,4 +18,3 @@ export function logout( }, }); } - diff --git a/modules/identity/src/web/shared/api/refresh-session.api.ts b/modules/identity/src/web/shared/api/refresh-session.api.ts index f9d599e2..865aa02e 100644 --- a/modules/identity/src/web/shared/api/refresh-session.api.ts +++ b/modules/identity/src/web/shared/api/refresh-session.api.ts @@ -11,4 +11,3 @@ export function refreshSession( data: params, }); } - diff --git a/modules/identity/src/web/shared/entities/auth-session.entity.ts b/modules/identity/src/web/shared/entities/auth-session.entity.ts index e0ea3340..dfcdb019 100644 --- a/modules/identity/src/web/shared/entities/auth-session.entity.ts +++ b/modules/identity/src/web/shared/entities/auth-session.entity.ts @@ -14,4 +14,3 @@ export interface AuthTokens { accessToken: string; refreshToken: string; } - diff --git a/modules/identity/src/web/shared/entities/identity-auth-error.entity.ts b/modules/identity/src/web/shared/entities/identity-auth-error.entity.ts index ac0edec0..46bbf1d4 100644 --- a/modules/identity/src/web/shared/entities/identity-auth-error.entity.ts +++ b/modules/identity/src/web/shared/entities/identity-auth-error.entity.ts @@ -7,4 +7,3 @@ export class IdentityAuthError extends Error { this.status = status; } } - diff --git a/modules/identity/src/web/shared/hooks/index.ts b/modules/identity/src/web/shared/hooks/index.ts index 32eb05d3..9e443803 100644 --- a/modules/identity/src/web/shared/hooks/index.ts +++ b/modules/identity/src/web/shared/hooks/index.ts @@ -2,4 +2,3 @@ export * from "./use-current-session-query"; export * from "./use-login-mutation"; export * from "./use-logout-mutation"; export * from "./use-refresh-session-mutation"; - diff --git a/modules/identity/src/web/shared/hooks/use-login-mutation.ts b/modules/identity/src/web/shared/hooks/use-login-mutation.ts index efaed48c..5805734d 100644 --- a/modules/identity/src/web/shared/hooks/use-login-mutation.ts +++ b/modules/identity/src/web/shared/hooks/use-login-mutation.ts @@ -1,15 +1,14 @@ import { useDataSource } from "@erp/core/hooks"; import { + type LoginWithPasswordRequestDTO, LoginWithPasswordRequestSchema, LoginWithPasswordResponseSchema, - type LoginWithPasswordRequestDTO, } from "@erp/identity/common"; import { useMutation } from "@tanstack/react-query"; import { mapLoginResponseToAuthSession } from "../adapters"; -import { IdentityAuthError, type AuthSession } from "../entities"; - import { login } from "../api"; +import { type AuthSession, IdentityAuthError } from "../entities"; export function useLoginMutation() { const dataSource = useDataSource(); @@ -29,4 +28,3 @@ export function useLoginMutation() { }, }); } - diff --git a/modules/identity/src/web/shared/index.ts b/modules/identity/src/web/shared/index.ts index 813baa2f..0aa4cea0 100644 --- a/modules/identity/src/web/shared/index.ts +++ b/modules/identity/src/web/shared/index.ts @@ -3,4 +3,3 @@ export * from "./api"; export * from "./entities"; export * from "./hooks"; export * from "./storage"; - diff --git a/modules/identity/src/web/shared/storage/auth-token-storage.ts b/modules/identity/src/web/shared/storage/auth-token-storage.ts index cf370833..d8dcaf4d 100644 --- a/modules/identity/src/web/shared/storage/auth-token-storage.ts +++ b/modules/identity/src/web/shared/storage/auth-token-storage.ts @@ -48,8 +48,7 @@ export const authTokenStorage = { : typeof storedValue.token === "string" ? storedValue.token : null, - refreshToken: - typeof storedValue.refreshToken === "string" ? storedValue.refreshToken : null, + refreshToken: typeof storedValue.refreshToken === "string" ? storedValue.refreshToken : null, }; }, @@ -73,4 +72,3 @@ export const authTokenStorage = { return Boolean(this.getAccessToken()); }, }; - diff --git a/modules/onboarding/src/api/application/mappers/complete-initial-onboarding-input.mapper.ts b/modules/onboarding/src/api/application/mappers/complete-initial-onboarding-input.mapper.ts index 2e3b56bc..a91cdd2d 100644 --- a/modules/onboarding/src/api/application/mappers/complete-initial-onboarding-input.mapper.ts +++ b/modules/onboarding/src/api/application/mappers/complete-initial-onboarding-input.mapper.ts @@ -39,10 +39,14 @@ export class CompleteInitialOnboardingInputMapper implements ICompleteInitialOnb const countryCode = CountryCode.create(dto.address.country_code); const required = [accountId, legalName, tin, street, city, postalCode, province, countryCode]; const failed = required.find((value) => value.isFailure); - if (failed) return Result.fail(failed.error); + if (failed) { + return Result.fail(failed.error); + } const street2 = dto.address.street_2 ? Street.create(dto.address.street_2) : undefined; - if (street2?.isFailure) return Result.fail(street2.error); + if (street2?.isFailure) { + return Result.fail(street2.error); + } const address = PostalAddress.create({ street: Maybe.some(street.data), street2: street2 ? Maybe.some(street2.data) : Maybe.none(), @@ -51,16 +55,26 @@ export class CompleteInitialOnboardingInputMapper implements ICompleteInitialOnb province: Maybe.some(province.data), countryCode: Maybe.some(countryCode.data), }); - if (address.isFailure) return Result.fail(address.error); + if (address.isFailure) { + return Result.fail(address.error); + } const tradeName = dto.trade_name ? Name.create(dto.trade_name) : undefined; const email = dto.email ? EmailAddress.create(dto.email) : undefined; const phone = dto.phone ? PhoneNumber.create(dto.phone) : undefined; const website = dto.website ? URLAddress.create(dto.website) : undefined; - if (tradeName?.isFailure) return Result.fail(tradeName.error); - if (email?.isFailure) return Result.fail(email.error); - if (phone?.isFailure) return Result.fail(phone.error); - if (website?.isFailure) return Result.fail(website.error); + if (tradeName?.isFailure) { + return Result.fail(tradeName.error); + } + if (email?.isFailure) { + return Result.fail(email.error); + } + if (phone?.isFailure) { + return Result.fail(phone.error); + } + if (website?.isFailure) { + return Result.fail(website.error); + } return Result.ok({ accountId: accountId.data, diff --git a/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.test.ts b/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.test.ts index b44006b1..fdd74e84 100644 --- a/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.test.ts +++ b/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.test.ts @@ -1,9 +1,13 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { UniqueID } from "@repo/rdx-ddd"; import { Maybe, Result } from "@repo/rdx-utils"; + import { InitialOnboarding } from "../../domain"; + import { GetInitialOnboardingStatusUseCase } from "./get-initial-onboarding-status.use-case"; + const accountId = UniqueID.create("22222222-2222-4222-8222-222222222222").data; const companyId = UniqueID.create("33333333-3333-4333-8333-333333333333").data; const pending = InitialOnboarding.createPending(accountId).data; @@ -22,12 +26,13 @@ for (const [name, value, expected] of [ ["none", Maybe.none(), { status: "not_provisioned", companyId: null }], ["pending", Maybe.some(pending), { status: "pending", companyId: null }], ["completed", Maybe.some(completed), { status: "completed", companyId: companyId.toPrimitive() }], -] as const) +] as const) { test(name, async () => { const repo = { findByAccountId: async () => Result.ok(value) } as never; const result = await new GetInitialOnboardingStatusUseCase(repo).execute({ accountId }); assert.deepEqual(result.data, expected); }); +} test("repository error fails without writing", async () => { const error = new Error("read"); const repo = { findByAccountId: async () => Result.fail(error) } as never; diff --git a/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.ts b/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.ts index 1192f62a..a3f1a124 100644 --- a/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.ts +++ b/modules/onboarding/src/api/application/repositories/get-initial-onboarding-status.use-case.ts @@ -18,8 +18,12 @@ export class GetInitialOnboardingStatusUseCase { params: GetInitialOnboardingStatusParams ): Promise> { const found = await this.repository.findByAccountId(params.accountId); - if (found.isFailure) return Result.fail(found.error); - if (found.data.isNone()) return Result.ok({ status: "not_provisioned", companyId: null }); + if (found.isFailure) { + return Result.fail(found.error); + } + if (found.data.isNone()) { + return Result.ok({ status: "not_provisioned", companyId: null }); + } const onboarding = found.data.unwrap(); return Result.ok({ status: onboarding.status, diff --git a/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.test.ts b/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.test.ts index 48892627..193f61d2 100644 --- a/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.test.ts +++ b/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.test.ts @@ -28,14 +28,22 @@ class FakeRepository implements IInitialOnboardingRepository { public async findByAccountId(id: UniqueID): Promise, Error>> { assert.ok(id.equals(accountId)); this.reads++; - if (this.readError) return Result.fail(this.readError); - if (this.reads > 1 && this.rereadError) return Result.fail(this.rereadError); + if (this.readError) { + return Result.fail(this.readError); + } + if (this.reads > 1 && this.rereadError) { + return Result.fail(this.rereadError); + } return Result.ok(Maybe.fromNullable(this.stored)); } public async save(value: InitialOnboarding): Promise> { this.writes++; - if (this.winner) this.stored = this.winner; - if (this.writeError) return Result.fail(this.writeError); + if (this.winner) { + this.stored = this.winner; + } + if (this.writeError) { + return Result.fail(this.writeError); + } this.stored = value; return Result.ok(); } @@ -169,7 +177,9 @@ test("dos provision simultáneos con barrera devuelven un único ganador", async assert.ok(id.equals(accountId)); reads++; if (reads <= 2) { - if (reads === 2) release(); + if (reads === 2) { + release(); + } await barrier; return Result.ok(Maybe.none()); } @@ -177,7 +187,9 @@ test("dos provision simultáneos con barrera devuelven un único ganador", async }, async save(value) { writes++; - if (stored) return Result.fail(duplicate()); + if (stored) { + return Result.fail(duplicate()); + } stored = value; return Result.ok(); }, diff --git a/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.ts b/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.ts index da1524b7..b4e1b84b 100644 --- a/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.ts +++ b/modules/onboarding/src/api/application/services/initial-onboarding-provisioner.ts @@ -11,20 +11,34 @@ export class InitialOnboardingProvisioner { /** Asegura que existe onboarding; conserva los estados pending y completed existentes. */ public async provision(accountId: UniqueID): Promise> { const current = await this.repository.findByAccountId(accountId); - if (current.isFailure) return Result.fail(current.error); - if (current.data.isSome()) return Result.ok(current.data.unwrap()); + if (current.isFailure) { + return Result.fail(current.error); + } + if (current.data.isSome()) { + return Result.ok(current.data.unwrap()); + } const created = InitialOnboarding.createPending(accountId); - if (created.isFailure) return Result.fail(created.error); + if (created.isFailure) { + return Result.fail(created.error); + } const saved = await this.repository.save(created.data); - if (saved.isSuccess) return Result.ok(created.data); - if (!isDuplicateEntityError(saved.error)) return Result.fail(saved.error); + if (saved.isSuccess) { + return Result.ok(created.data); + } + if (!isDuplicateEntityError(saved.error)) { + return Result.fail(saved.error); + } // UNIQUE es la garantía entre procesos. Solo recuperamos el ganador confirmado por cuenta. const winner = await this.repository.findByAccountId(accountId); - if (winner.isFailure) return Result.fail(winner.error); - if (winner.data.isSome()) return Result.ok(winner.data.unwrap()); + if (winner.isFailure) { + return Result.fail(winner.error); + } + if (winner.data.isSome()) { + return Result.ok(winner.data.unwrap()); + } return Result.fail( new Error("Initial onboarding duplicate without a persisted onboarding for the account.", { cause: saved.error, diff --git a/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.test.ts b/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.test.ts index d252366f..1d89488f 100644 --- a/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.test.ts +++ b/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.test.ts @@ -35,12 +35,16 @@ function setup(failure?: string, absent = false) { const manager: ITransactionManager = { start: async () => { events.push("begin"); - if (failure === "begin") throw error; + if (failure === "begin") { + throw error; + } }, getTransaction: () => transaction, commit: async () => { events.push("commit"); - if (failure === "commit") throw error; + if (failure === "commit") { + throw error; + } }, rollback: async () => { events.push("rollback"); @@ -49,7 +53,9 @@ function setup(failure?: string, absent = false) { await manager.start(); try { const value = await work(transaction); - if (value instanceof Result && value.isFailure) throw value.error; + if (value instanceof Result && value.isFailure) { + throw value.error; + } await manager.commit(); return value; } catch (caught) { @@ -68,7 +74,9 @@ function setup(failure?: string, absent = false) { events.push("lock"); assert.equal(tx, transaction); assert.equal(id.toPrimitive(), accountId.toPrimitive()); - if (failure === "read") return Result.fail(error); + if (failure === "read") { + return Result.fail(error); + } return Result.ok(stored ? Maybe.some(stored) : Maybe.none()); }, save: async (value, tx) => { @@ -77,7 +85,9 @@ function setup(failure?: string, absent = false) { assert.equal(value.status, "completed"); assert.equal(value.companyId.unwrap().toPrimitive(), companyId.toPrimitive()); assert.equal(value.completedAt.unwrap().toPrimitive(), value.updatedAt.toPrimitive()); - if (failure === "save") return Result.fail(error); + if (failure === "save") { + return Result.fail(error); + } stored = value; return Result.ok(); }, @@ -86,8 +96,12 @@ function setup(failure?: string, absent = false) { create: async (p) => { events.push("company"); assert.equal(p.transaction, transaction); - if (failure === "throw") throw error; - if (failure === "company") return Result.fail(error); + if (failure === "throw") { + throw error; + } + if (failure === "company") { + return Result.fail(error); + } return Result.ok({ companyId: companyId.toPrimitive(), slug: "internal" }); }, }, @@ -148,9 +162,14 @@ for (const failure of ["read", "company", "admin", "save", "throw", "commit", "b test(`fallo ${failure} retorna Result.fail y gestiona transacción`, async () => { const s = setup(failure); assert.equal((await s.useCase.execute(input)).error, s.error); - if (failure === "begin") assert.deepEqual(s.events, ["begin"]); - else assert.equal(s.events.at(-1), "rollback"); - if (failure !== "commit") assert.equal(s.events.includes("commit"), false); + if (failure === "begin") { + assert.deepEqual(s.events, ["begin"]); + } else { + assert.equal(s.events.at(-1), "rollback"); + } + if (failure !== "commit") { + assert.equal(s.events.includes("commit"), false); + } }); } test("fallo del dominio no persiste ni confirma", async (t) => { diff --git a/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.ts b/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.ts index b98e341f..dc388411 100644 --- a/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.ts +++ b/modules/onboarding/src/api/application/use-cases/complete-initial-onboarding.use-case.ts @@ -37,7 +37,9 @@ export class CompleteInitialOnboardingUseCase { input: CompleteInitialOnboardingUseCaseInput ): Promise> { const mappedResult = this.deps.dtoMapper.map(input.dto, { accountId: input.accountId }); - if (mappedResult.isFailure) return Result.fail(mappedResult.error); + if (mappedResult.isFailure) { + return Result.fail(mappedResult.error); + } const params = mappedResult.data; try { return await this.deps.transactionManager.complete(async (transaction: unknown) => { @@ -45,14 +47,20 @@ export class CompleteInitialOnboardingUseCase { params.accountId, transaction ); - if (found.isFailure) return Result.fail(found.error); - if (found.data.isNone()) return Result.fail(new InitialOnboardingNotProvisionedError()); + if (found.isFailure) { + return Result.fail(found.error); + } + if (found.data.isNone()) { + return Result.fail(new InitialOnboardingNotProvisionedError()); + } const onboarding = found.data.unwrap(); - if (!onboarding.accountId.equals(params.accountId)) + if (!onboarding.accountId.equals(params.accountId)) { return Result.fail(new Error("Initial onboarding account mismatch.")); + } if (onboarding.status === "completed") { - if (onboarding.companyId.isNone() || onboarding.completedAt.isNone()) + if (onboarding.companyId.isNone() || onboarding.completedAt.isNone()) { return Result.fail(new Error("Invalid completed onboarding.")); + } return Result.ok({ companyId: onboarding.companyId.unwrap().toPrimitive() }); } if ( @@ -78,24 +86,34 @@ export class CompleteInitialOnboardingUseCase { transaction: unknown ): Promise> { const company = await this.deps.companyCreator.create({ ...params.company, transaction }); - if (company.isFailure) return Result.fail(company.error); + if (company.isFailure) { + return Result.fail(company.error); + } const companyId = UniqueID.create(company.data.companyId); - if (companyId.isFailure) return Result.fail(companyId.error); + if (companyId.isFailure) { + return Result.fail(companyId.error); + } const admin = await this.deps.adminAssigner.assign({ accountId: params.accountId, companyId: companyId.data, transaction, }); - if (admin.isFailure) return Result.fail(admin.error); + if (admin.isFailure) { + return Result.fail(admin.error); + } const now = UtcDateTime.now(); const completed = onboarding.complete({ companyId: companyId.data, completedAt: now, updatedAt: now, }); - if (completed.isFailure) return Result.fail(completed.error); + if (completed.isFailure) { + return Result.fail(completed.error); + } const saved = await this.deps.repository.save(onboarding, transaction); - if (saved.isFailure) return Result.fail(saved.error); + if (saved.isFailure) { + return Result.fail(saved.error); + } return Result.ok({ companyId: companyId.data.toPrimitive() }); } } diff --git a/modules/onboarding/src/api/application/use-cases/index.ts b/modules/onboarding/src/api/application/use-cases/index.ts index aa23f856..415afe2c 100644 --- a/modules/onboarding/src/api/application/use-cases/index.ts +++ b/modules/onboarding/src/api/application/use-cases/index.ts @@ -1 +1 @@ -export * from './complete-initial-onboarding.use-case'; +export * from "./complete-initial-onboarding.use-case"; diff --git a/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.test.ts b/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.test.ts index 60ee9375..3714382b 100644 --- a/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.test.ts +++ b/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.test.ts @@ -6,7 +6,9 @@ import { Maybe } from "@repo/rdx-utils"; import { InitialOnboarding } from "./initial-onboarding.aggregate"; const check = (value: unknown) => { - if (!value) throw new Error("Assertion failed"); + if (!value) { + throw new Error("Assertion failed"); + } }; const date = () => UtcDateTime.now(); diff --git a/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.ts b/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.ts index 0303dd0a..06b83594 100644 --- a/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.ts +++ b/modules/onboarding/src/api/domain/initial-onboarding/initial-onboarding.aggregate.ts @@ -46,8 +46,9 @@ export class InitialOnboarding extends AggregateRoot { completedAt: UtcDateTime; updatedAt: UtcDateTime; }): Result { - if (this.props.status === "completed") + if (this.props.status === "completed") { return Result.fail(new Error("Initial onboarding already completed.")); + } const props = { ...this.props, diff --git a/modules/onboarding/src/api/infrastructure/di/onboarding.di.ts b/modules/onboarding/src/api/infrastructure/di/onboarding.di.ts index 53d4ef3e..4b2b48c3 100644 --- a/modules/onboarding/src/api/infrastructure/di/onboarding.di.ts +++ b/modules/onboarding/src/api/infrastructure/di/onboarding.di.ts @@ -1,10 +1,11 @@ -import type { IInitialCompanyAdminAssigner } from "@erp/identity/api"; import type { IInitialCompanyCreator } from "@erp/companies/api"; import { type ModuleParams, buildTransactionManager } from "@erp/core/api"; +import type { IInitialCompanyAdminAssigner } from "@erp/identity/api"; +import type { Sequelize } from "sequelize"; + import { buildOnboardingApplicationServices } from "./onboarding-application-services.di"; import { buildOnboardingPersistence } from "./onboarding-persistence.di"; import { buildOnboardingUseCases } from "./onboarding-use-cases.di"; -import type { Sequelize } from "sequelize"; export type OnboardingInternalDeps = { applicationServices: ReturnType; @@ -13,11 +14,18 @@ export type OnboardingInternalDeps = { export const buildOnboardingDependencies = (params: ModuleParams): OnboardingInternalDeps => { const persistence = buildOnboardingPersistence(); - const companies = params.getService<{ initialCompanyCreator: IInitialCompanyCreator }>("companies:general"); - const identity = params.getService<{ initialCompanyAdminAssigner: IInitialCompanyAdminAssigner }>("identity:general"); + const companies = params.getService<{ initialCompanyCreator: IInitialCompanyCreator }>( + "companies:general" + ); + const identity = params.getService<{ initialCompanyAdminAssigner: IInitialCompanyAdminAssigner }>( + "identity:general" + ); const useCases = buildOnboardingUseCases({ ...persistence, - transactionManager: buildTransactionManager(params.database as Sequelize, params.transactionContext), + transactionManager: buildTransactionManager( + params.database as Sequelize, + params.transactionContext + ), initialCompanyCreator: companies.initialCompanyCreator, initialCompanyAdminAssigner: identity.initialCompanyAdminAssigner, }); diff --git a/modules/onboarding/src/api/infrastructure/express/complete-initial-onboarding.controller.ts b/modules/onboarding/src/api/infrastructure/express/complete-initial-onboarding.controller.ts index bf7a4790..a4f048a3 100644 --- a/modules/onboarding/src/api/infrastructure/express/complete-initial-onboarding.controller.ts +++ b/modules/onboarding/src/api/infrastructure/express/complete-initial-onboarding.controller.ts @@ -14,7 +14,9 @@ export class CompleteInitialOnboardingController extends ExpressController { protected async executeImpl() { const user = this.getUser(); - if (!user?.userId) return this.unauthorizedError("Unauthorized"); + if (!user?.userId) { + return this.unauthorizedError("Unauthorized"); + } const dto = this.req.body as CompleteInitialOnboardingRequestDTO; const result = await this.useCase.execute({ dto, accountId: user.userId.toPrimitive() }); return result.match( diff --git a/modules/onboarding/src/api/infrastructure/express/onboarding.routes.test.ts b/modules/onboarding/src/api/infrastructure/express/onboarding.routes.test.ts index 99587124..f5203354 100644 --- a/modules/onboarding/src/api/infrastructure/express/onboarding.routes.test.ts +++ b/modules/onboarding/src/api/infrastructure/express/onboarding.routes.test.ts @@ -82,7 +82,9 @@ async function runtime(t: TestContext, error?: Error) { } as T; }, getService: (name: string) => { - if (name === "companies:general") return { finder: {} } as T; + if (name === "companies:general") { + return { finder: {} } as T; + } assert.equal(name, "identity:general"); return { auth: { diff --git a/modules/onboarding/src/api/infrastructure/express/provision-initial-onboarding.controller.ts b/modules/onboarding/src/api/infrastructure/express/provision-initial-onboarding.controller.ts index d6063ec4..735dbaad 100644 --- a/modules/onboarding/src/api/infrastructure/express/provision-initial-onboarding.controller.ts +++ b/modules/onboarding/src/api/infrastructure/express/provision-initial-onboarding.controller.ts @@ -1,4 +1,5 @@ import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api"; + import type { InitialOnboardingProvisioner } from "../../application/services/initial-onboarding-provisioner"; export class ProvisionInitialOnboardingController extends ExpressController { @@ -8,7 +9,9 @@ export class ProvisionInitialOnboardingController extends ExpressController { } protected async executeImpl() { const user = this.getUser(); - if (!user?.userId) return this.unauthorizedError("Unauthorized"); + if (!user?.userId) { + return this.unauthorizedError("Unauthorized"); + } const result = await this.provisioner.provision(user.userId); return result.match( (onboarding) => diff --git a/modules/onboarding/src/api/infrastructure/persistence/sequelize/mappers/sequelize-initial-onboarding-domain.mapper.ts b/modules/onboarding/src/api/infrastructure/persistence/sequelize/mappers/sequelize-initial-onboarding-domain.mapper.ts index 3cc6e191..9d0c5b13 100644 --- a/modules/onboarding/src/api/infrastructure/persistence/sequelize/mappers/sequelize-initial-onboarding-domain.mapper.ts +++ b/modules/onboarding/src/api/infrastructure/persistence/sequelize/mappers/sequelize-initial-onboarding-domain.mapper.ts @@ -27,19 +27,31 @@ export class SequelizeInitialOnboardingDomainMapper { } const id = UniqueID.create(source.id); - if (id.isFailure) return Result.fail(id.error); + if (id.isFailure) { + return Result.fail(id.error); + } const accountId = UniqueID.create(source.account_id); - if (accountId.isFailure) return Result.fail(accountId.error); + if (accountId.isFailure) { + return Result.fail(accountId.error); + } const companyId = maybeFromNullableResult(source.company_id, (value) => UniqueID.create(value)); - if (companyId.isFailure) return Result.fail(companyId.error); + if (companyId.isFailure) { + return Result.fail(companyId.error); + } const createdAt = toUtcDateTime(source.created_at); - if (createdAt.isFailure) return Result.fail(createdAt.error); + if (createdAt.isFailure) { + return Result.fail(createdAt.error); + } const updatedAt = toUtcDateTime(source.updated_at); - if (updatedAt.isFailure) return Result.fail(updatedAt.error); + if (updatedAt.isFailure) { + return Result.fail(updatedAt.error); + } const completedAt = maybeFromNullableResult(source.completed_at, (value) => toUtcDateTime(value) ); - if (completedAt.isFailure) return Result.fail(completedAt.error); + if (completedAt.isFailure) { + return Result.fail(completedAt.error); + } return InitialOnboarding.rehydrate( { diff --git a/modules/onboarding/src/api/infrastructure/persistence/sequelize/repositories/sequelize-initial-onboarding.repository.ts b/modules/onboarding/src/api/infrastructure/persistence/sequelize/repositories/sequelize-initial-onboarding.repository.ts index e08744fc..1bd57ed2 100644 --- a/modules/onboarding/src/api/infrastructure/persistence/sequelize/repositories/sequelize-initial-onboarding.repository.ts +++ b/modules/onboarding/src/api/infrastructure/persistence/sequelize/repositories/sequelize-initial-onboarding.repository.ts @@ -17,14 +17,18 @@ export class SequelizeInitialOnboardingRepository implements IInitialOnboardingR accountId: UniqueID, transaction: unknown ): Promise, Error>> { - if (!transaction) return Result.fail(new Error("Onboarding lock requires a transaction.")); + if (!transaction) { + return Result.fail(new Error("Onboarding lock requires a transaction.")); + } try { const row = await InitialOnboardingModel.findOne({ where: { account_id: accountId.toPrimitive() }, transaction: transaction as Transaction, lock: true, }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } return this.domainMapper.mapToDomain(row).map(Maybe.some); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -40,7 +44,9 @@ export class SequelizeInitialOnboardingRepository implements IInitialOnboardingR where: { account_id: accountId.toPrimitive() }, transaction: transaction as Transaction | undefined, }); - if (!row) return Result.ok(Maybe.none()); + if (!row) { + return Result.ok(Maybe.none()); + } return this.domainMapper.mapToDomain(row).map((value) => Maybe.some(value)); } catch (error: unknown) { return Result.fail(translateSequelizeError(error)); @@ -53,7 +59,9 @@ export class SequelizeInitialOnboardingRepository implements IInitialOnboardingR ): Promise> { try { const persistence = this.domainMapper.mapToPersistence(initialOnboarding); - if (persistence.isFailure) return Result.fail(persistence.error); + if (persistence.isFailure) { + return Result.fail(persistence.error); + } const sqlTransaction = transaction as Transaction | undefined; const { id, ...values } = persistence.data; diff --git a/modules/onboarding/src/api/module.composition.test.ts b/modules/onboarding/src/api/module.composition.test.ts index 3d1b97cb..be531a38 100644 --- a/modules/onboarding/src/api/module.composition.test.ts +++ b/modules/onboarding/src/api/module.composition.test.ts @@ -5,7 +5,7 @@ import initialOnboardingModelInit from "./infrastructure/persistence/sequelize/m import { onboardingModule } from "./module"; test("onboarding composition builds public capabilities and registers its model", async () => { - let provisionCalls = 0; + const provisionCalls = 0; const initialCompanyCreator = { create: async () => { throw new Error("not invoked"); @@ -20,8 +20,12 @@ test("onboarding composition builds public capabilities and registers its model" database: {} as never, transactionContext: {} as never, getService: (name: string) => { - if (name === "companies:general") return { initialCompanyCreator } as T; - if (name === "identity:general") return { initialCompanyAdminAssigner } as T; + if (name === "companies:general") { + return { initialCompanyCreator } as T; + } + if (name === "identity:general") { + return { initialCompanyAdminAssigner } as T; + } throw new Error(`Unexpected service ${name}`); }, } as never); diff --git a/modules/supplier-invoices/package.json b/modules/supplier-invoices/package.json index c508a46e..20165bd1 100644 --- a/modules/supplier-invoices/package.json +++ b/modules/supplier-invoices/package.json @@ -33,4 +33,4 @@ "sequelize": "^6.37.8", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-item-domain.mapper.ts b/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-item-domain.mapper.ts index 2ff84101..b1d0f38d 100644 --- a/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-item-domain.mapper.ts +++ b/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-item-domain.mapper.ts @@ -36,8 +36,6 @@ export class SequelizeSupplierInvoiceItemDomainMapper extends SequelizeDomainMap CustomerInvoiceItemCreationAttributes, SupplierInvoiceItem > { - private readonly taxCatalog!: JsonTaxCatalogProvider; - constructor(params: MapperParamsType) { super(); const { taxCatalog } = params as { diff --git a/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-taxes-domain.mapper.ts b/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-taxes-domain.mapper.ts index 80d86359..9ec088c3 100644 --- a/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-taxes-domain.mapper.ts +++ b/modules/supplier-invoices/src/api/infrastucture/persistence/sequelize/mappers/domain/sequelize-supplier-invoice-taxes-domain.mapper.ts @@ -46,8 +46,6 @@ export class SequelizeSupplierInvoiceTaxesDomainMapper extends SequelizeDomainMa CustomerInvoiceTaxCreationAttributes, SupplierInvoiceTax > { - private taxCatalog!: JsonTaxCatalogProvider; - constructor(params: MapperParamsType) { super(); const { taxCatalog } = params as { diff --git a/modules/suppliers/package.json b/modules/suppliers/package.json index d4831a4e..6cf46e1d 100644 --- a/modules/suppliers/package.json +++ b/modules/suppliers/package.json @@ -57,4 +57,4 @@ "use-debounce": "^10.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.test.ts b/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.test.ts index d79dc1bb..c998ce11 100644 --- a/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.test.ts +++ b/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.test.ts @@ -21,10 +21,7 @@ test("mobile_secondary se mapea sin modificar mobilePrimary", () => { test("is_company false se conserva como boolean", () => { const mapper = new UpdateSupplierInputMapper(); - const result = mapper.map( - { isCompany: false }, - { companyId: UniqueID.generateNewID() } - ); + const result = mapper.map({ isCompany: false }, { companyId: UniqueID.generateNewID() }); assert.equal(result.isSuccess, true); assert.equal(result.data.isCompany, false); @@ -33,10 +30,7 @@ test("is_company false se conserva como boolean", () => { test("is_company true se conserva como boolean", () => { const mapper = new UpdateSupplierInputMapper(); - const result = mapper.map( - { isCompany: true }, - { companyId: UniqueID.generateNewID() } - ); + const result = mapper.map({ isCompany: true }, { companyId: UniqueID.generateNewID() }); assert.equal(result.isSuccess, true); assert.equal(result.data.isCompany, true); diff --git a/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.ts b/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.ts index dad33a53..68f3b3fb 100644 --- a/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.ts +++ b/modules/suppliers/src/api/application/mappers/update-supplier-input.mapper.ts @@ -37,10 +37,7 @@ import type { UpdateSupplierPatchInput } from "../use-cases/update-supplier.comm */ export interface IUpdateSupplierInputMapper { - map( - dto: UpdateSupplierPatchInput, - params: { companyId: UniqueID } - ): Result; + map(dto: UpdateSupplierPatchInput, params: { companyId: UniqueID }): Result; } export class UpdateSupplierInputMapper implements IUpdateSupplierInputMapper { diff --git a/modules/suppliers/src/api/application/repositories/index.ts b/modules/suppliers/src/api/application/repositories/index.ts index 8b61f9e1..1173daa2 100644 --- a/modules/suppliers/src/api/application/repositories/index.ts +++ b/modules/suppliers/src/api/application/repositories/index.ts @@ -1 +1 @@ -export * from './supplier-repository.interface'; +export * from "./supplier-repository.interface"; diff --git a/modules/suppliers/src/api/application/repositories/supplier-repository.interface.ts b/modules/suppliers/src/api/application/repositories/supplier-repository.interface.ts index 37ca3e0a..56d205c5 100644 --- a/modules/suppliers/src/api/application/repositories/supplier-repository.interface.ts +++ b/modules/suppliers/src/api/application/repositories/supplier-repository.interface.ts @@ -68,5 +68,4 @@ export interface ISupplierRepository { criteria: Criteria, transaction: unknown ): Promise, Error>>; - } diff --git a/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.test.ts b/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.test.ts index e3e0e424..f0fc6735 100644 --- a/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.test.ts +++ b/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.test.ts @@ -4,6 +4,7 @@ import test from "node:test"; import { Maybe } from "@repo/rdx-utils"; import { GetSupplierByIdResponseSchema } from "../../../../common"; + import { SupplierFullSnapshotBuilder } from "./supplier-snapshot-builder"; test("el snapshot de detalle satisface el contrato de respuesta", () => { diff --git a/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.ts b/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.ts index 4b6543db..5c518dee 100644 --- a/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.ts +++ b/modules/suppliers/src/api/application/snapshot-builders/domain/supplier-snapshot-builder.ts @@ -1,8 +1,8 @@ import type { ISnapshotBuilder } from "@erp/core/api"; import { maybeToEmptyString } from "@repo/rdx-ddd"; -import type { Supplier } from "../../../domain"; import type { SupplierDetailDTO } from "../../../../common"; +import type { Supplier } from "../../../domain"; export interface ISupplierFullSnapshotBuilder extends ISnapshotBuilder {} diff --git a/modules/suppliers/src/api/application/snapshot-builders/summary/supplier-summary-snapshot-builder.ts b/modules/suppliers/src/api/application/snapshot-builders/summary/supplier-summary-snapshot-builder.ts index 376873c9..f36677c6 100644 --- a/modules/suppliers/src/api/application/snapshot-builders/summary/supplier-summary-snapshot-builder.ts +++ b/modules/suppliers/src/api/application/snapshot-builders/summary/supplier-summary-snapshot-builder.ts @@ -1,8 +1,8 @@ import type { ISnapshotBuilder } from "@erp/core/api"; import { maybeToEmptyString } from "@repo/rdx-ddd"; -import type { SupplierSummary } from "../../models"; import type { SupplierSummaryDTO } from "../../../../common"; +import type { SupplierSummary } from "../../models"; export interface ISupplierSummarySnapshotBuilder extends ISnapshotBuilder {} diff --git a/modules/suppliers/src/api/application/use-cases/create-supplier.use-case.ts b/modules/suppliers/src/api/application/use-cases/create-supplier.use-case.ts index 83f26f52..c7791e73 100644 --- a/modules/suppliers/src/api/application/use-cases/create-supplier.use-case.ts +++ b/modules/suppliers/src/api/application/use-cases/create-supplier.use-case.ts @@ -5,6 +5,7 @@ import { Result } from "@repo/rdx-utils"; import type { ICreateSupplierInputMapper } from "../mappers"; import type { ISupplierCreator } from "../services"; import type { ISupplierFullSnapshotBuilder } from "../snapshot-builders"; + import type { CreateSupplierCommand } from "./create-supplier.command"; type CreateSupplierUseCaseInput = { diff --git a/modules/suppliers/src/api/application/use-cases/update-supplier.use-case.ts b/modules/suppliers/src/api/application/use-cases/update-supplier.use-case.ts index 8fe7816e..6d15328b 100644 --- a/modules/suppliers/src/api/application/use-cases/update-supplier.use-case.ts +++ b/modules/suppliers/src/api/application/use-cases/update-supplier.use-case.ts @@ -6,6 +6,7 @@ import type { SupplierPatchProps } from "../../domain"; import type { IUpdateSupplierInputMapper } from "../mappers"; import type { ISupplierFinder, ISupplierUpdater } from "../services"; import type { ISupplierFullSnapshotBuilder } from "../snapshot-builders"; + import type { UpdateSupplierCommand } from "./update-supplier.command"; type UpdateSupplierUseCaseInput = { diff --git a/modules/suppliers/src/api/domain/aggregates/index.ts b/modules/suppliers/src/api/domain/aggregates/index.ts index de720f10..27924412 100644 --- a/modules/suppliers/src/api/domain/aggregates/index.ts +++ b/modules/suppliers/src/api/domain/aggregates/index.ts @@ -1 +1 @@ -export * from './supplier.aggregate'; +export * from "./supplier.aggregate"; diff --git a/modules/suppliers/src/api/domain/collections/index.ts b/modules/suppliers/src/api/domain/collections/index.ts index 8b464466..f301f717 100644 --- a/modules/suppliers/src/api/domain/collections/index.ts +++ b/modules/suppliers/src/api/domain/collections/index.ts @@ -1 +1 @@ -export * from './supplier-taxes.collection'; +export * from "./supplier-taxes.collection"; diff --git a/modules/suppliers/src/api/domain/index.ts b/modules/suppliers/src/api/domain/index.ts index 9dc38f73..363314a6 100644 --- a/modules/suppliers/src/api/domain/index.ts +++ b/modules/suppliers/src/api/domain/index.ts @@ -1,4 +1,4 @@ -export * from './aggregates'; -export * from './collections'; -export * from './errors'; -export * from './value-objects'; +export * from "./aggregates"; +export * from "./collections"; +export * from "./errors"; +export * from "./value-objects"; diff --git a/modules/suppliers/src/api/domain/value-objects/index.ts b/modules/suppliers/src/api/domain/value-objects/index.ts index 07c48e7a..1813334f 100644 --- a/modules/suppliers/src/api/domain/value-objects/index.ts +++ b/modules/suppliers/src/api/domain/value-objects/index.ts @@ -1 +1 @@ -export * from './supplier-status.value-object'; +export * from "./supplier-status.value-object"; diff --git a/modules/suppliers/src/api/infrastructure/persistence/index.ts b/modules/suppliers/src/api/infrastructure/persistence/index.ts index 9c2c79b5..62f8ac11 100644 --- a/modules/suppliers/src/api/infrastructure/persistence/index.ts +++ b/modules/suppliers/src/api/infrastructure/persistence/index.ts @@ -1 +1 @@ -export * from './sequelize'; +export * from "./sequelize"; diff --git a/modules/suppliers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-supplier-summary.mapper.ts b/modules/suppliers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-supplier-summary.mapper.ts index c161ccc6..6f8d3920 100644 --- a/modules/suppliers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-supplier-summary.mapper.ts +++ b/modules/suppliers/src/api/infrastructure/persistence/sequelize/mappers/summary/sequelize-supplier-summary.mapper.ts @@ -189,7 +189,7 @@ export class SequelizeSupplierSummaryMapper extends SequelizeQueryMapper< return Result.ok({ id: supplierId!, companyId: companyId!, - isActive: status!.isActive(), + isActive: status?.isActive(), reference: reference!, isCompany: isCompany, diff --git a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/index.ts b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/index.ts index ce8c3b81..c6fe3c0c 100644 --- a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/index.ts +++ b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/index.ts @@ -1 +1 @@ -export * from './sequelize-supplier.repository'; +export * from "./sequelize-supplier.repository"; diff --git a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.test.ts b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.test.ts index 16e6a1ba..b41e3a4b 100644 --- a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.test.ts +++ b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.test.ts @@ -5,6 +5,7 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { SupplierModel } from "../models"; + import { SupplierRepository } from "./sequelize-supplier.repository"; const supplierId = "11111111-1111-4111-8111-111111111111"; @@ -61,10 +62,13 @@ test("getByIdInCompany mantiene el scope de empresa", { concurrency: false }, as assert.equal(found.isSuccess, true); assert.equal(notFound.isFailure, true); - assert.deepEqual(calls.map((call) => call.where), [ - { id: supplierId, company_id: companyId }, - { id: supplierId, company_id: otherCompanyId }, - ]); + assert.deepEqual( + calls.map((call) => call.where), + [ + { id: supplierId, company_id: companyId }, + { id: supplierId, company_id: otherCompanyId }, + ] + ); } finally { (SupplierModel as any).findOne = originalFindOne; } @@ -94,7 +98,9 @@ test("create propaga la transaction al modelo", { concurrency: false }, async () } }); -test("update filtra por empresa, preserva identidad y propaga transaction", { concurrency: false }, async () => { +test("update filtra por empresa, preserva identidad y propaga transaction", { + concurrency: false, +}, async () => { const repository = createRepository(); const transaction = { id: "tx-1" }; const originalUpdate = SupplierModel.update; @@ -126,7 +132,9 @@ test("update filtra por empresa, preserva identidad y propaga transaction", { co } }); -test("update devuelve Result.fail si el mapper de persistencia falla", { concurrency: false }, async () => { +test("update devuelve Result.fail si el mapper de persistencia falla", { + concurrency: false, +}, async () => { const expectedError = new Error("persistence mapper failed"); const repository = createRepository({ mapToPersistence: () => Result.fail(expectedError), diff --git a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.ts b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.ts index 7e34860a..d42cef26 100644 --- a/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.ts +++ b/modules/suppliers/src/api/infrastructure/persistence/sequelize/repositories/sequelize-supplier.repository.ts @@ -305,5 +305,4 @@ export class SupplierRepository return Result.fail(translateSequelizeError(err)); } } - } diff --git a/modules/suppliers/src/common/dto/response/list-suppliers.response.dto.ts b/modules/suppliers/src/common/dto/response/list-suppliers.response.dto.ts index b71ded1e..7619582c 100644 --- a/modules/suppliers/src/common/dto/response/list-suppliers.response.dto.ts +++ b/modules/suppliers/src/common/dto/response/list-suppliers.response.dto.ts @@ -9,38 +9,38 @@ import { import { z } from "zod/v4"; export const SupplierSummarySchema = z.object({ - id: z.uuid(), - company_id: z.uuid(), - status: z.string(), - reference: z.string(), + id: z.uuid(), + company_id: z.uuid(), + status: z.string(), + reference: z.string(), - is_company: z.boolean(), - name: z.string(), - trade_name: z.string(), - tin: z.string(), + is_company: z.boolean(), + name: z.string(), + trade_name: z.string(), + tin: z.string(), - street: z.string(), - street2: z.string(), + street: z.string(), + street2: z.string(), - city: z.string(), - province: z.string(), - postal_code: PostalCodeSchema, - country: CountryCodeSchema, + city: z.string(), + province: z.string(), + postal_code: PostalCodeSchema, + country: CountryCodeSchema, - email_primary: z.string(), - email_secondary: z.string(), - phone_primary: z.string(), - phone_secondary: z.string(), - mobile_primary: z.string(), - mobile_secondary: z.string(), - fax: z.string(), - website: z.string(), + email_primary: z.string(), + email_secondary: z.string(), + phone_primary: z.string(), + phone_secondary: z.string(), + mobile_primary: z.string(), + mobile_secondary: z.string(), + fax: z.string(), + website: z.string(), - language_code: LanguageCodeSchema, - currency_code: CurrencyCodeSchema, + language_code: LanguageCodeSchema, + currency_code: CurrencyCodeSchema, - metadata: MetadataSchema.optional(), - }); + metadata: MetadataSchema.optional(), +}); export type SupplierSummaryDTO = z.infer; diff --git a/modules/suppliers/src/common/locales/en.json b/modules/suppliers/src/common/locales/en.json index 6278e60b..9cb6fa15 100644 --- a/modules/suppliers/src/common/locales/en.json +++ b/modules/suppliers/src/common/locales/en.json @@ -61,10 +61,16 @@ "modified": "Modified", "keyboard_shortcuts": "View keyboard shortcuts", "sections": { - "identity": { "title": "Identification", "description": "Information used to identify the supplier." }, + "identity": { + "title": "Identification", + "description": "Information used to identify the supplier." + }, "address": { "title": "Address", "description": "Supplier address and location." }, "contact": { "title": "Contact", "description": "Primary and secondary contact channels." }, - "preferences": { "title": "Preferences", "description": "Language and currency associated with the supplier." } + "preferences": { + "title": "Preferences", + "description": "Language and currency associated with the supplier." + } } }, "fields": { @@ -94,8 +100,31 @@ }, "status": { "active": "Active", "inactive": "Inactive" }, "options": { - "country": { "es": "Spain", "fr": "France", "de": "Germany", "it": "Italy", "pt": "Portugal", "us": "United States", "mx": "Mexico", "ar": "Argentina" }, - "language": { "es": "Spanish", "en": "English", "fr": "French", "de": "German", "it": "Italian", "pt": "Portuguese" }, - "currency": { "EUR": "Euro", "USD": "US dollar", "GBP": "Pound sterling", "ARS": "Argentine peso", "MXN": "Mexican peso", "JPY": "Japanese yen" } + "country": { + "es": "Spain", + "fr": "France", + "de": "Germany", + "it": "Italy", + "pt": "Portugal", + "us": "United States", + "mx": "Mexico", + "ar": "Argentina" + }, + "language": { + "es": "Spanish", + "en": "English", + "fr": "French", + "de": "German", + "it": "Italian", + "pt": "Portuguese" + }, + "currency": { + "EUR": "Euro", + "USD": "US dollar", + "GBP": "Pound sterling", + "ARS": "Argentine peso", + "MXN": "Mexican peso", + "JPY": "Japanese yen" + } } } diff --git a/modules/suppliers/src/common/locales/es.json b/modules/suppliers/src/common/locales/es.json index cf3f7851..d4cfa867 100644 --- a/modules/suppliers/src/common/locales/es.json +++ b/modules/suppliers/src/common/locales/es.json @@ -109,8 +109,31 @@ "inactive": "Inactivo" }, "options": { - "country": { "es": "España", "fr": "Francia", "de": "Alemania", "it": "Italia", "pt": "Portugal", "us": "Estados Unidos", "mx": "México", "ar": "Argentina" }, - "language": { "es": "Español", "en": "Inglés", "fr": "Francés", "de": "Alemán", "it": "Italiano", "pt": "Portugués" }, - "currency": { "EUR": "Euro", "USD": "Dólar estadounidense", "GBP": "Libra esterlina", "ARS": "Peso argentino", "MXN": "Peso mexicano", "JPY": "Yen japonés" } + "country": { + "es": "España", + "fr": "Francia", + "de": "Alemania", + "it": "Italia", + "pt": "Portugal", + "us": "Estados Unidos", + "mx": "México", + "ar": "Argentina" + }, + "language": { + "es": "Español", + "en": "Inglés", + "fr": "Francés", + "de": "Alemán", + "it": "Italiano", + "pt": "Portugués" + }, + "currency": { + "EUR": "Euro", + "USD": "Dólar estadounidense", + "GBP": "Libra esterlina", + "ARS": "Peso argentino", + "MXN": "Peso mexicano", + "JPY": "Yen japonés" + } } } diff --git a/modules/suppliers/src/web/list/controllers/use-list-suppliers.controller.ts b/modules/suppliers/src/web/list/controllers/use-list-suppliers.controller.ts index 58d1af16..033536b4 100644 --- a/modules/suppliers/src/web/list/controllers/use-list-suppliers.controller.ts +++ b/modules/suppliers/src/web/list/controllers/use-list-suppliers.controller.ts @@ -84,7 +84,9 @@ export const useListSuppliersController = () => { const updateSearchParams = (updates: Record) => { setSearchParams((current) => { const next = new URLSearchParams(current); - for (const [key, value] of Object.entries(updates)) next.set(key, value); + for (const [key, value] of Object.entries(updates)) { + next.set(key, value); + } return next; }); }; @@ -111,7 +113,9 @@ export const useListSuppliersController = () => { }, sort, setSort: (nextSort: DataTableSort) => { - if (!isSupplierSortField(nextSort.field)) return; + if (!isSupplierSortField(nextSort.field)) { + return; + } tablePreferences.setSort(nextSort); updateSearchParams({ page: "1", diff --git a/modules/suppliers/src/web/shared/api/create-supplier.api.ts b/modules/suppliers/src/web/shared/api/create-supplier.api.ts index 1a99e96d..dc8917b3 100644 --- a/modules/suppliers/src/web/shared/api/create-supplier.api.ts +++ b/modules/suppliers/src/web/shared/api/create-supplier.api.ts @@ -21,9 +21,13 @@ export type CreateSupplierResult = CreateSupplierResponseDTO; export function createSupplier(dataSource: IDataSource, params: CreateSupplierParams) { const { id, data } = params; - if (!id) throw new Error("supplierId is required"); + if (!id) { + throw new Error("supplierId is required"); + } - if (id !== data.id) throw new Error("supplierId in params must match id in data"); + if (id !== data.id) { + throw new Error("supplierId in params must match id in data"); + } return dataSource.createOne( "suppliers", diff --git a/modules/suppliers/src/web/shared/api/get-supplier-by-id.api.ts b/modules/suppliers/src/web/shared/api/get-supplier-by-id.api.ts index 5c5246b3..51aacc60 100644 --- a/modules/suppliers/src/web/shared/api/get-supplier-by-id.api.ts +++ b/modules/suppliers/src/web/shared/api/get-supplier-by-id.api.ts @@ -23,6 +23,8 @@ export function getSupplierById( params: GetSupplierByIdParams ): Promise { const { id, signal } = params; - if (!id) throw new Error("supplierId is required"); + if (!id) { + throw new Error("supplierId is required"); + } return dataSource.getOne("suppliers", id, { signal }); } diff --git a/modules/suppliers/src/web/shared/api/update-supplier-by-id.api.ts b/modules/suppliers/src/web/shared/api/update-supplier-by-id.api.ts index 226f3fd0..77f839aa 100644 --- a/modules/suppliers/src/web/shared/api/update-supplier-by-id.api.ts +++ b/modules/suppliers/src/web/shared/api/update-supplier-by-id.api.ts @@ -24,7 +24,9 @@ export function updateSupplierById( ): Promise { const { id, data } = params; - if (!id) throw new Error("supplierId is required"); + if (!id) { + throw new Error("supplierId is required"); + } return dataSource.custom({ path: `suppliers/${id}`, method: "patch", diff --git a/modules/suppliers/src/web/shared/forms/supplier-form.test.ts b/modules/suppliers/src/web/shared/forms/supplier-form.test.ts index 699186ce..75e8784c 100644 --- a/modules/suppliers/src/web/shared/forms/supplier-form.test.ts +++ b/modules/suppliers/src/web/shared/forms/supplier-form.test.ts @@ -2,10 +2,10 @@ import assert from "node:assert/strict"; import test from "node:test"; import { + SupplierFormSchema, buildCreateSupplierRequest, buildSupplierFormDefaults, buildUpdateSupplierRequest, - SupplierFormSchema, } from "./supplier-form"; test("los valores iniciales cumplen el schema del formulario", () => { diff --git a/modules/suppliers/src/web/shared/forms/supplier-form.ts b/modules/suppliers/src/web/shared/forms/supplier-form.ts index b6279e13..bf85412e 100644 --- a/modules/suppliers/src/web/shared/forms/supplier-form.ts +++ b/modules/suppliers/src/web/shared/forms/supplier-form.ts @@ -135,10 +135,14 @@ export const buildUpdateSupplierRequest = ( ): UpdateSupplierByIdRequestDTO => { const entries = Object.entries(UPDATE_FIELD_MAPPING).flatMap(([formField, requestField]) => { const field = formField as keyof SupplierFormValues; - if (!dirtyFields[field]) return []; + if (!dirtyFields[field]) { + return []; + } const value = values[field]; - if (typeof value === "string" && value.trim() === "") return []; + if (typeof value === "string" && value.trim() === "") { + return []; + } return [[requestField, typeof value === "string" ? value.trim() : value]]; }); diff --git a/modules/suppliers/src/web/shared/hooks/supplier-cache-strategy.ts b/modules/suppliers/src/web/shared/hooks/supplier-cache-strategy.ts index 92798901..05e5c056 100644 --- a/modules/suppliers/src/web/shared/hooks/supplier-cache-strategy.ts +++ b/modules/suppliers/src/web/shared/hooks/supplier-cache-strategy.ts @@ -48,10 +48,14 @@ export function upsertSupplierInListCaches( for (const key of keys) { const page = queryClient.getQueryData(key); - if (!page) continue; + if (!page) { + continue; + } const index = page.items.findIndex((row) => row.id === supplier.id); - if (index === -1) continue; + if (index === -1) { + continue; + } const nextItems = page.items.slice(); nextItems[index] = { diff --git a/modules/suppliers/src/web/shared/hooks/use-supplier-get-query.ts b/modules/suppliers/src/web/shared/hooks/use-supplier-get-query.ts index dbace30b..2734d0b9 100644 --- a/modules/suppliers/src/web/shared/hooks/use-supplier-get-query.ts +++ b/modules/suppliers/src/web/shared/hooks/use-supplier-get-query.ts @@ -22,7 +22,9 @@ export const useSupplierGetQuery = ( return useQuery({ queryKey: SUPPLIER_QUERY_KEY(id), queryFn: async ({ signal }) => { - if (!id) throw new Error("supplierId is required"); + if (!id) { + throw new Error("supplierId is required"); + } const dto: GetSupplierByIdResult = await getSupplierById(dataSource, { id, diff --git a/modules/suppliers/src/web/update/controllers/use-supplier-update.controller.ts b/modules/suppliers/src/web/update/controllers/use-supplier-update.controller.ts index eeb81fa8..eca5198d 100644 --- a/modules/suppliers/src/web/update/controllers/use-supplier-update.controller.ts +++ b/modules/suppliers/src/web/update/controllers/use-supplier-update.controller.ts @@ -37,13 +37,17 @@ export const useSupplierUpdateController = ( }); useEffect(() => { - if (!query.data) return; + if (!query.data) { + return; + } form.reset(mapSupplierToFormValues(query.data), { keepDirty: false }); }, [form, query.data]); const submit = form.handleSubmit( async (values) => { - if (!supplierId) return; + if (!supplierId) { + return; + } const data = buildUpdateSupplierRequest(values, form.formState.dirtyFields); if (Object.keys(data).length === 0) { diff --git a/modules/suppliers/src/web/update/supplier-update-page.tsx b/modules/suppliers/src/web/update/supplier-update-page.tsx index ca8e2a0d..14554298 100644 --- a/modules/suppliers/src/web/update/supplier-update-page.tsx +++ b/modules/suppliers/src/web/update/supplier-update-page.tsx @@ -24,7 +24,9 @@ export const SupplierUpdatePage = () => { }), }); - if (controller.query.isLoading) return ; + if (controller.query.isLoading) { + return ; + } if (controller.query.isError || !controller.query.data) { return ( diff --git a/modules/suppliers/src/web/view/supplier-detail-page.tsx b/modules/suppliers/src/web/view/supplier-detail-page.tsx index 7d2922c9..ff7eeb1d 100644 --- a/modules/suppliers/src/web/view/supplier-detail-page.tsx +++ b/modules/suppliers/src/web/view/supplier-detail-page.tsx @@ -32,7 +32,9 @@ export const SupplierDetailPage = () => { const { navigateBack } = useReturnToNavigation({ fallbackPath: returnTo }); const query = useSupplierGetQuery({ id, enabled: Boolean(id) }); - if (query.isLoading) return ; + if (query.isLoading) { + return ; + } if (query.isError || !query.data) { return ( diff --git a/package.json b/package.json index fd736d67..9ad83b3e 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,7 @@ "name": "factuges-erp-2025", "private": true, "version": "0.10.8", - "workspaces": [ - "apps/*", - "modules/*", - "packages/*" - ], + "workspaces": ["apps/*", "modules/*", "packages/*"], "scripts": { "build": "turbo run build", "build:templates": "bash scripts/build-templates.sh", @@ -15,7 +11,7 @@ "dev:server": "turbo dev --filter=server", "dev:client": "turbo dev --filter=client", "format-and-lint": "biome check .", - "format-and-lint:fix": "biome check . --write", + "format-and-lint:fix": "biome check --write --unsafe .", "create:package": "ts-node scripts/create-package.ts", "volta:install": "curl https://get.volta.sh | bash", "clean": "turbo run clean && rimraf ./node_modules && rimraf ./package-lock.json && rimraf ./out", @@ -23,7 +19,7 @@ "format": "biome format --write .", "format:check": "biome format .", "check": "biome check .", - "check:write": "biome check --write .", + "check:write": "biome check --write --unsafe .", "test:frontend": "node --experimental-test-module-mocks --import tsx --test", "test:unit": "node --import tsx --test", "typecheck": "tsc -p tsconfig.json --noEmit" @@ -49,4 +45,4 @@ "node": ">=24" }, "packageManager": "pnpm@11.25.0" -} \ No newline at end of file +} diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 5e370664..133490f7 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,27 +1,27 @@ { - "name": "@repo/i18next", - "version": "0.10.8", - "private": true, - "type": "module", - "scripts": { - "typecheck": "tsc -p tsconfig.json --noEmit", - "check": "biome check .", - "lint": "biome lint .", - "clean": "rimraf .turbo node_modules dist" - }, - "exports": { - ".": "./src/i18n.ts" - }, - "devDependencies": { - "@repo/typescript-config": "workspace:*", - "@types/node": "^25.6.0", - "rimraf": "^6.1.3", - "typescript": "^6.0.2" - }, - "dependencies": { - "i18next": "26.0.4", - "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^3.0.4", - "react-i18next": "^17.0.2" - } -} \ No newline at end of file + "name": "@repo/i18next", + "version": "0.10.8", + "private": true, + "type": "module", + "scripts": { + "typecheck": "tsc -p tsconfig.json --noEmit", + "check": "biome check .", + "lint": "biome lint .", + "clean": "rimraf .turbo node_modules dist" + }, + "exports": { + ".": "./src/i18n.ts" + }, + "devDependencies": { + "@repo/typescript-config": "workspace:*", + "@types/node": "^25.6.0", + "rimraf": "^6.1.3", + "typescript": "^6.0.2" + }, + "dependencies": { + "i18next": "26.0.4", + "i18next-browser-languagedetector": "^8.2.1", + "i18next-http-backend": "^3.0.4", + "react-i18next": "^17.0.2" + } +} diff --git a/packages/rdx-audit/package.json b/packages/rdx-audit/package.json index 9c49de20..b8ca2c1f 100644 --- a/packages/rdx-audit/package.json +++ b/packages/rdx-audit/package.json @@ -24,4 +24,4 @@ "dependencies": { "@repo/rdx-utils": "workspace:*" } -} \ No newline at end of file +} diff --git a/packages/rdx-audit/src/audit.test.ts b/packages/rdx-audit/src/audit.test.ts index 8041001e..eb34c00b 100644 --- a/packages/rdx-audit/src/audit.test.ts +++ b/packages/rdx-audit/src/audit.test.ts @@ -42,13 +42,18 @@ class InMemoryAuditStore implements IAuditStore { } async append(entry: AuditEntry) { - if (this.error) return Result.fail(this.error); + if (this.error) { + return Result.fail(this.error); + } this.entries.push(entry); return Result.ok(); } } -function buildAudit(store = new InMemoryAuditStore(), provider: IAuditContextProvider = new StubContextProvider(context)) { +function buildAudit( + store = new InMemoryAuditStore(), + provider: IAuditContextProvider = new StubContextProvider(context) +) { return { audit: new Audit({ source: { type: "application", id: "test-app" }, @@ -165,9 +170,15 @@ test("rejects invalid actions, targets and mutation states", async () => { const actionResult = await audit.recordEvent({ ...eventInput, action: "Document.Downloaded" }); assert.ok(actionResult.error instanceof InvalidAuditInputError); - const targetResult = await audit.recordMutation({ ...mutationInput, target: { ...mutationInput.target, id: "" } }); + const targetResult = await audit.recordMutation({ + ...mutationInput, + target: { ...mutationInput.target, id: "" }, + }); assert.ok(targetResult.error instanceof InvalidAuditInputError); - const stateResult = await audit.recordMutation({ ...mutationInput, before: { bad: new Date() as never } }); + const stateResult = await audit.recordMutation({ + ...mutationInput, + before: { bad: new Date() as never }, + }); assert.ok(stateResult.error instanceof InvalidAuditStateError); }); diff --git a/packages/rdx-audit/src/audit.ts b/packages/rdx-audit/src/audit.ts index fddabada..1d36dc55 100644 --- a/packages/rdx-audit/src/audit.ts +++ b/packages/rdx-audit/src/audit.ts @@ -60,7 +60,9 @@ export class Audit implements IAudit { validateAuditState(input.after, "after"); const atomicPaths = validateAtomicPaths(input.atomicPaths); const changes = diffAuditState(input.before, input.after, atomicPaths); - if (changes.length === 0) return Result.ok(); + if (changes.length === 0) { + return Result.ok(); + } const context = this.getContext(); const entry: AuditMutationEntry = { @@ -101,7 +103,9 @@ export class Audit implements IAudit { private getContext(): AuditContext { const context = this.deps.contextProvider.get(); - if (!context) throw new AuditContextUnavailableError(); + if (!context) { + throw new AuditContextUnavailableError(); + } validateContext(context); return context; } @@ -127,7 +131,9 @@ export class Audit implements IAudit { } private toAuditError(error: unknown): AuditError { - if (error instanceof AuditError) return error; + if (error instanceof AuditError) { + return error; + } return new AuditStoreError(error instanceof Error ? error.message : "Audit store failed."); } } diff --git a/packages/rdx-audit/src/internal/audit-diff.test.ts b/packages/rdx-audit/src/internal/audit-diff.test.ts index c77211bb..52ceee5d 100644 --- a/packages/rdx-audit/src/internal/audit-diff.test.ts +++ b/packages/rdx-audit/src/internal/audit-diff.test.ts @@ -3,6 +3,7 @@ import test from "node:test"; import type { AuditState } from "../audit-state.types"; import { InvalidAuditInputError } from "../errors"; + import { diffAuditState } from "./audit-diff"; import { validateAtomicPaths } from "./audit-input-validator"; @@ -60,15 +61,27 @@ test("treats arrays as atomic and distinguishes null from missing", () => { }); test("records incompatible structural changes at their current path", () => { - assert.deepEqual(diff({ address: { city: "Madrid" }, value: "text" }, { address: "Madrid", value: { text: "x" } }), [ - { path: "address", kind: "change", before: { city: "Madrid" }, after: "Madrid" }, - { path: "value", kind: "change", before: "text", after: { text: "x" } }, - ]); + assert.deepEqual( + diff( + { address: { city: "Madrid" }, value: "text" }, + { address: "Madrid", value: { text: "x" } } + ), + [ + { path: "address", kind: "change", before: { city: "Madrid" }, after: "Madrid" }, + { path: "value", kind: "change", before: "text", after: { text: "x" } }, + ] + ); }); test("honours atomic paths including nested paths", () => { - const before = { paymentTerm: { id: "PT1", label: "30 days" }, address: { city: "Madrid", zip: "1" } }; - const after = { paymentTerm: { id: "PT2", label: "60 days" }, address: { city: "Barcelona", zip: "1" } }; + const before = { + paymentTerm: { id: "PT1", label: "30 days" }, + address: { city: "Madrid", zip: "1" }, + }; + const after = { + paymentTerm: { id: "PT2", label: "60 days" }, + address: { city: "Barcelona", zip: "1" }, + }; assert.equal(diff(before, after).length, 3); assert.deepEqual(diff(before, after, ["paymentTerm", "address.city"]), [ diff --git a/packages/rdx-audit/src/internal/audit-diff.ts b/packages/rdx-audit/src/internal/audit-diff.ts index 1af1a0af..02e66083 100644 --- a/packages/rdx-audit/src/internal/audit-diff.ts +++ b/packages/rdx-audit/src/internal/audit-diff.ts @@ -44,7 +44,9 @@ function compareValues( changes.push({ path, kind: "unset", before, after: null }); return; } - if (deepEqual(before, after)) return; + if (deepEqual(before, after)) { + return; + } if (atomicPaths.has(path) || !isObject(before) || !isObject(after)) { changes.push({ path, kind: "change", before, after }); return; @@ -54,22 +56,30 @@ function compareValues( } function deepEqual(left: AuditValue, right: AuditValue): boolean { - if (left === right) return true; + if (left === right) { + return true; + } if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) { return false; } if (Array.isArray(left) || Array.isArray(right)) { - if (!(Array.isArray(left) && Array.isArray(right)) || left.length !== right.length) return false; + if (!(Array.isArray(left) && Array.isArray(right)) || left.length !== right.length) { + return false; + } return left.every((value, index) => { const rightValue = right[index]; return rightValue !== undefined && deepEqual(value, rightValue); }); } - if (!(isObject(left) && isObject(right))) return false; + if (!(isObject(left) && isObject(right))) { + return false; + } const leftKeys = Object.keys(left).sort(); const rightKeys = Object.keys(right).sort(); - if (leftKeys.length !== rightKeys.length) return false; + if (leftKeys.length !== rightKeys.length) { + return false; + } return leftKeys.every((key, index) => { const rightKey = rightKeys[index]; const leftValue = left[key]; @@ -85,7 +95,9 @@ function deepEqual(left: AuditValue, right: AuditValue): boolean { } function getOwnValue(object: AuditObject, key: string): AuditValue | typeof MISSING { - if (!Object.hasOwn(object, key)) return MISSING; + if (!Object.hasOwn(object, key)) { + return MISSING; + } const value = object[key]; return value === undefined ? MISSING : value; } diff --git a/packages/rdx-audit/src/internal/audit-input-validator.ts b/packages/rdx-audit/src/internal/audit-input-validator.ts index d82c8737..05c06f8e 100644 --- a/packages/rdx-audit/src/internal/audit-input-validator.ts +++ b/packages/rdx-audit/src/internal/audit-input-validator.ts @@ -46,14 +46,20 @@ export function validateAtomicPaths(paths: readonly string[] | undefined): Reado if (typeof path !== "string" || !path || path.split(".").some((segment) => !segment)) { throw new InvalidAuditInputError("atomicPaths must contain non-empty dot-separated paths."); } - if (normalized.has(path)) throw new InvalidAuditInputError(`Duplicate atomic path "${path}".`); + if (normalized.has(path)) { + throw new InvalidAuditInputError(`Duplicate atomic path "${path}".`); + } normalized.add(path); } return normalized; } function validateAction(action: string): void { - if (typeof action !== "string" || action.length > MAX_ACTION_LENGTH || !ACTION_PATTERN.test(action)) { + if ( + typeof action !== "string" || + action.length > MAX_ACTION_LENGTH || + !ACTION_PATTERN.test(action) + ) { throw new InvalidAuditInputError("action must use lowercase dot-separated segments."); } } @@ -71,15 +77,21 @@ function validateMutationTarget(target: AuditMutationTarget): void { } function validateOptionalIdentifier(value: string | null, name: string): void { - if (value !== null) validateIdentifier(value, name); + if (value !== null) { + validateIdentifier(value, name); + } } function validateOptionalLabel(value: string | null, name: string): void { - if (value !== null) validateIdentifier(value, name); + if (value !== null) { + validateIdentifier(value, name); + } } function validateIdentifier(value: string, name: string): void { if (typeof value !== "string" || !value.trim() || value.length > MAX_IDENTIFIER_LENGTH) { - throw new InvalidAuditInputError(`${name} must be a non-empty string up to ${MAX_IDENTIFIER_LENGTH} characters.`); + throw new InvalidAuditInputError( + `${name} must be a non-empty string up to ${MAX_IDENTIFIER_LENGTH} characters.` + ); } } diff --git a/packages/rdx-audit/src/internal/audit-state-validator.test.ts b/packages/rdx-audit/src/internal/audit-state-validator.test.ts index f546d7d7..30f43fbf 100644 --- a/packages/rdx-audit/src/internal/audit-state-validator.test.ts +++ b/packages/rdx-audit/src/internal/audit-state-validator.test.ts @@ -1,8 +1,9 @@ -import { Buffer } from "node:buffer"; import assert from "node:assert/strict"; +import { Buffer } from "node:buffer"; import test from "node:test"; import { InvalidAuditStateError } from "../errors"; + import { validateAuditState } from "./audit-state-validator"; test("accepts supported audit state values", () => { @@ -38,7 +39,10 @@ for (const [name, value] of [ ["Set", new Set()], ] as const) { test(`rejects ${name}`, () => { - assert.throws(() => validateAuditState({ value: value as never }, "state"), InvalidAuditStateError); + assert.throws( + () => validateAuditState({ value: value as never }, "state"), + InvalidAuditStateError + ); }); } diff --git a/packages/rdx-audit/src/internal/audit-state-validator.ts b/packages/rdx-audit/src/internal/audit-state-validator.ts index 17abeeaa..6a7416bb 100644 --- a/packages/rdx-audit/src/internal/audit-state-validator.ts +++ b/packages/rdx-audit/src/internal/audit-state-validator.ts @@ -1,20 +1,30 @@ -import { InvalidAuditStateError } from "../errors"; import type { AuditState, AuditValue } from "../audit-state.types"; +import { InvalidAuditStateError } from "../errors"; export function validateAuditState(state: AuditState, name: string): void { validateObject(state, name, new WeakSet()); } -function validateValue(value: unknown, path: string, ancestors: WeakSet): asserts value is AuditValue { - if (value === null || typeof value === "string" || typeof value === "boolean") return; +function validateValue( + value: unknown, + path: string, + ancestors: WeakSet +): asserts value is AuditValue { + if (value === null || typeof value === "string" || typeof value === "boolean") { + return; + } if (typeof value === "number") { - if (Number.isFinite(value)) return; + if (Number.isFinite(value)) { + return; + } throw new InvalidAuditStateError(`${path} must contain a finite number.`); } if (Array.isArray(value)) { - if (ancestors.has(value)) throw new InvalidAuditStateError(`${path} contains a circular reference.`); + if (ancestors.has(value)) { + throw new InvalidAuditStateError(`${path} contains a circular reference.`); + } ancestors.add(value); value.forEach((item, index) => { validateValue(item, `${path}[${index}]`, ancestors); @@ -32,23 +42,36 @@ function validateValue(value: unknown, path: string, ancestors: WeakSet) } function validateObject(value: object, path: string, ancestors: WeakSet): void { - if (!isPlainObject(value)) throw new InvalidAuditStateError(`${path} must be a plain object.`); - if (ancestors.has(value)) throw new InvalidAuditStateError(`${path} contains a circular reference.`); + if (!isPlainObject(value)) { + throw new InvalidAuditStateError(`${path} must be a plain object.`); + } + if (ancestors.has(value)) { + throw new InvalidAuditStateError(`${path} contains a circular reference.`); + } if (Object.getOwnPropertySymbols(value).length > 0) { throw new InvalidAuditStateError(`${path} must not contain symbol keys.`); } ancestors.add(value); for (const [key, nestedValue] of Object.entries(value)) { - if (!key) throw new InvalidAuditStateError(`${path} must not contain an empty key.`); - if (key.includes(".")) throw new InvalidAuditStateError(`${path}.${key} must not contain '.'.`); + if (!key) { + throw new InvalidAuditStateError(`${path} must not contain an empty key.`); + } + if (key.includes(".")) { + throw new InvalidAuditStateError(`${path}.${key} must not contain '.'.`); + } validateValue(nestedValue, `${path}.${key}`, ancestors); } ancestors.delete(value); } export function isPlainObject(value: unknown): value is Readonly> { - if (value === null || typeof value !== "object" || Array.isArray(value) || Buffer.isBuffer(value)) { + if ( + value === null || + typeof value !== "object" || + Array.isArray(value) || + Buffer.isBuffer(value) + ) { return false; } diff --git a/packages/rdx-criteria/package.json b/packages/rdx-criteria/package.json index c85313fd..af65c06d 100644 --- a/packages/rdx-criteria/package.json +++ b/packages/rdx-criteria/package.json @@ -20,4 +20,4 @@ "dependencies": { "sequelize": "^6.37.8" } -} \ No newline at end of file +} diff --git a/packages/rdx-criteria/src/build-boolean-full-text-query.ts b/packages/rdx-criteria/src/build-boolean-full-text-query.ts index 921f9ffc..aca648e1 100644 --- a/packages/rdx-criteria/src/build-boolean-full-text-query.ts +++ b/packages/rdx-criteria/src/build-boolean-full-text-query.ts @@ -1,7 +1,9 @@ const FULL_TEXT_TOKEN_PATTERN = /[\p{L}\p{N}]+/gu; export function buildBooleanFullTextQuery(search: string): string { - if (!search) return ""; + if (!search) { + return ""; + } const terms = search.normalize("NFKC").match(FULL_TEXT_TOKEN_PATTERN) ?? []; diff --git a/packages/rdx-criteria/src/codelytv/Order.ts b/packages/rdx-criteria/src/codelytv/Order.ts index 2b20184a..10e72d1e 100644 --- a/packages/rdx-criteria/src/codelytv/Order.ts +++ b/packages/rdx-criteria/src/codelytv/Order.ts @@ -12,9 +12,9 @@ export class Order { } static fromPrimitives(orderBy: string | null, orderType: string | null): Order { - return orderBy !== null - ? new Order(new OrderBy(orderBy), new OrderType(orderType as OrderTypes)) - : Order.none(); + return orderBy === null + ? Order.none() + : new Order(new OrderBy(orderBy), new OrderType(orderType as OrderTypes)); } isNone(): boolean { diff --git a/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts b/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts index a9ba075b..13701713 100644 --- a/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts +++ b/packages/rdx-criteria/src/criteria-to-sequelize-converter.ts @@ -36,7 +36,9 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { } public applyFilters(options: FindOptions, criteria: Criteria, mappings: CriteriaMappings): void { - if (!criteria.hasFilters()) return; + if (!criteria.hasFilters()) { + return; + } const filters: WhereOptions[] = []; @@ -58,17 +60,23 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { const { searchableFields = [], database, enableFullText = false, fullTextTableAlias } = params; const term = typeof criteria.quickSearch === "string" ? criteria.quickSearch.trim() : ""; - if (!term || searchableFields.length === 0 || !enableFullText) return; + if (!term || searchableFields.length === 0 || !enableFullText) { + return; + } if (!database) { const msg = "[CriteriaToSequelizeConverter] enableFullText=true pero falta 'database'."; - if (params.strictMode) throw new Error(msg); + if (params.strictMode) { + throw new Error(msg); + } console.warn(msg); return; } const booleanTerm = buildBooleanFullTextQuery(term); - if (!booleanTerm) return; + if (!booleanTerm) { + return; + } const qualifiedFields = searchableFields.map((field) => this.qualifyRootColumn(field, fullTextTableAlias) @@ -97,7 +105,9 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { mappings: CriteriaMappings, params: ConvertParams ): void { - if (!criteria.hasOrder()) return; + if (!criteria.hasOrder()) { + return; + } const logicalField = criteria.order.orderBy.value; const direction = criteria.order.orderType.value.toUpperCase(); @@ -111,7 +121,9 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { } public applyPagination(options: FindOptions, criteria: Criteria): void { - if (criteria.pageSize == null) return; + if (criteria.pageSize == null) { + return; + } options.limit = criteria.pageSize; @@ -133,11 +145,15 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { const sortableFields = params.sortableFields ?? ["id", "created_at"]; const strict = params.strictMode ?? false; - if (sortableFields.includes(logicalField)) return; + if (sortableFields.includes(logicalField)) { + return; + } const msg = `[CriteriaToSequelizeConverter] Ignored ORDER BY '${logicalField}' because it is not sortable.`; - if (strict) throw new Error(msg); + if (strict) { + throw new Error(msg); + } console.warn(msg); } @@ -225,16 +241,26 @@ export class CriteriaToSequelizeConverter implements ICriteriaToOrmConverter { } private transformValue(operator: symbol, value: unknown): unknown { - if (operator === Op.is || operator === Op.not) return null; - if (operator === Op.like || operator === Op.notLike) return `%${value}%`; - if (value === "true") return true; - if (value === "false") return false; + if (operator === Op.is || operator === Op.not) { + return null; + } + if (operator === Op.like || operator === Op.notLike) { + return `%${value}%`; + } + if (value === "true") { + return true; + } + if (value === "false") { + return false; + } return value; } private qualifyRootColumn(column: string, tableAlias?: string): string { - if (!tableAlias) return this.quoteIdentifier(column); + if (!tableAlias) { + return this.quoteIdentifier(column); + } return `${this.quoteIdentifier(tableAlias)}.${this.quoteIdentifier(column)}`; } diff --git a/packages/rdx-criteria/src/utils.ts b/packages/rdx-criteria/src/utils.ts index 3ff91b66..ce4b1695 100644 --- a/packages/rdx-criteria/src/utils.ts +++ b/packages/rdx-criteria/src/utils.ts @@ -1,12 +1,16 @@ import type { FindOptions, Includeable, OrderItem } from "sequelize"; export function normalizeOrder(order: FindOptions["order"]): OrderItem[] { - if (!order) return []; + if (!order) { + return []; + } return Array.isArray(order) ? (order as OrderItem[]) : [order as OrderItem]; } export function normalizeInclude(include: FindOptions["include"]): Includeable[] { - if (!include) return []; + if (!include) { + return []; + } return Array.isArray(include) ? include : [include]; } @@ -26,7 +30,9 @@ export function appendOrder(options: FindOptions, orderItem: OrderItem): void { */ export function getCountIncludes(include: Includeable[]): Includeable[] { return include.filter((item) => { - if (!item || typeof item !== "object") return true; + if (!item || typeof item !== "object") { + return true; + } return !("separate" in item && item.separate === true); }); diff --git a/packages/rdx-ddd/package.json b/packages/rdx-ddd/package.json index c9a0423a..4e3844a9 100644 --- a/packages/rdx-ddd/package.json +++ b/packages/rdx-ddd/package.json @@ -27,4 +27,4 @@ "shallow-equal-object": "^1.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/packages/rdx-ddd/src/domain-entity.ts b/packages/rdx-ddd/src/domain-entity.ts index ce32d5f4..889a1877 100644 --- a/packages/rdx-ddd/src/domain-entity.ts +++ b/packages/rdx-ddd/src/domain-entity.ts @@ -26,7 +26,9 @@ export abstract class DomainEntity { } equals(other?: DomainEntity): boolean { - if (!other) return false; + if (!other) { + return false; + } return other instanceof DomainEntity && this.id.equals(other.id); } diff --git a/packages/rdx-ddd/src/events/domain-event-handle.ts b/packages/rdx-ddd/src/events/domain-event-handle.ts index 4301cdc6..3a00ce90 100644 --- a/packages/rdx-ddd/src/events/domain-event-handle.ts +++ b/packages/rdx-ddd/src/events/domain-event-handle.ts @@ -1,3 +1,3 @@ -export interface IHandle { +export interface IHandle<_IDomainEvent> { setupSubscriptions(): void; } diff --git a/packages/rdx-ddd/src/events/domain-event.ts b/packages/rdx-ddd/src/events/domain-event.ts index da960780..d7aa9508 100644 --- a/packages/rdx-ddd/src/events/domain-event.ts +++ b/packages/rdx-ddd/src/events/domain-event.ts @@ -97,7 +97,7 @@ export class DomainEvents { if (!Object.hasOwn(DomainEvents.handlersMap, eventClassName)) { DomainEvents.handlersMap[eventClassName] = []; } - DomainEvents.handlersMap[eventClassName]!.push(callback); + DomainEvents.handlersMap[eventClassName]?.push(callback); } /** diff --git a/packages/rdx-ddd/src/helpers/normalizers.ts b/packages/rdx-ddd/src/helpers/normalizers.ts index 204b9cdb..391a15c8 100644 --- a/packages/rdx-ddd/src/helpers/normalizers.ts +++ b/packages/rdx-ddd/src/helpers/normalizers.ts @@ -307,7 +307,9 @@ export function maybeToEmptyQuantityObjectString( * @returns Maybe del string normalizado con `trim()`. */ export function maybeFromNullableOrEmptyString(input?: string | null): Maybe { - if (input == null) return Maybe.none(); + if (input == null) { + return Maybe.none(); + } const trimmed = input.trim(); return trimmed ? Maybe.some(trimmed) : Maybe.none(); diff --git a/packages/rdx-ddd/src/specification.ts b/packages/rdx-ddd/src/specification.ts index a44ab31a..3641b70b 100644 --- a/packages/rdx-ddd/src/specification.ts +++ b/packages/rdx-ddd/src/specification.ts @@ -44,7 +44,9 @@ class AndSpecification extends CompositeSpecification { public async isSatisfiedBy(candidate: T): Promise { const leftResult = await this.left.isSatisfiedBy(candidate); - if (!leftResult) return false; + if (!leftResult) { + return false; + } const rightResult = await this.right.isSatisfiedBy(candidate); return rightResult; @@ -75,7 +77,9 @@ class OrSpecification extends CompositeSpecification { public async isSatisfiedBy(candidate: T): Promise { const leftResult = await this.left.isSatisfiedBy(candidate); - if (leftResult) return true; + if (leftResult) { + return true; + } const rightResult = await this.right.isSatisfiedBy(candidate); return rightResult; diff --git a/packages/rdx-ddd/src/value-objects/__tests__/phone-number.test.ts b/packages/rdx-ddd/src/value-objects/__tests__/phone-number.test.ts index 7c1714ba..dd2e8671 100644 --- a/packages/rdx-ddd/src/value-objects/__tests__/phone-number.test.ts +++ b/packages/rdx-ddd/src/value-objects/__tests__/phone-number.test.ts @@ -1,10 +1,11 @@ import { parsePhoneNumberWithError } from "libphonenumber-js"; + import { PhoneNumber } from "../phone-number"; describe("PhoneNumber", () => { const validPhone = "+14155552671"; // Número válido en formato internacional const invalidPhone = "12345"; // Número inválido - const nullablePhone = ""; + const _nullablePhone = ""; test("debe crear un número de teléfono válido", () => { const result = PhoneNumber.create(validPhone); diff --git a/packages/rdx-ddd/src/value-objects/city.ts b/packages/rdx-ddd/src/value-objects/city.ts index 5457de4a..aefe20e6 100644 --- a/packages/rdx-ddd/src/value-objects/city.ts +++ b/packages/rdx-ddd/src/value-objects/city.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface CityProps { diff --git a/packages/rdx-ddd/src/value-objects/phone-number.ts b/packages/rdx-ddd/src/value-objects/phone-number.ts index 80a16f63..8f7c0dd6 100644 --- a/packages/rdx-ddd/src/value-objects/phone-number.ts +++ b/packages/rdx-ddd/src/value-objects/phone-number.ts @@ -1,7 +1,9 @@ import { Result } from "@repo/rdx-utils"; import { isPossiblePhoneNumber, parsePhoneNumberWithError } from "libphonenumber-js"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface PhoneNumberProps { diff --git a/packages/rdx-ddd/src/value-objects/postal-address.ts b/packages/rdx-ddd/src/value-objects/postal-address.ts index 2ef05fb4..995c6614 100644 --- a/packages/rdx-ddd/src/value-objects/postal-address.ts +++ b/packages/rdx-ddd/src/value-objects/postal-address.ts @@ -27,7 +27,9 @@ export class PostalAddress extends ValueObject { static create(values: PostalAddressProps): Result { const valueIsValid = PostalAddress.validate(values); - if (valueIsValid.isFailure) return Result.fail(valueIsValid.error); + if (valueIsValid.isFailure) { + return Result.fail(valueIsValid.error); + } return Result.ok(new PostalAddress(values)); } diff --git a/packages/rdx-ddd/src/value-objects/postal-code.ts b/packages/rdx-ddd/src/value-objects/postal-code.ts index 854b945f..9e985a2d 100644 --- a/packages/rdx-ddd/src/value-objects/postal-code.ts +++ b/packages/rdx-ddd/src/value-objects/postal-code.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface PostalCodeProps { diff --git a/packages/rdx-ddd/src/value-objects/province.ts b/packages/rdx-ddd/src/value-objects/province.ts index 4f51046b..07bba1ac 100644 --- a/packages/rdx-ddd/src/value-objects/province.ts +++ b/packages/rdx-ddd/src/value-objects/province.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface ProvinceProps { diff --git a/packages/rdx-ddd/src/value-objects/slug.ts b/packages/rdx-ddd/src/value-objects/slug.ts index d15f5e09..722253c5 100644 --- a/packages/rdx-ddd/src/value-objects/slug.ts +++ b/packages/rdx-ddd/src/value-objects/slug.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface SlugProps { diff --git a/packages/rdx-ddd/src/value-objects/street.ts b/packages/rdx-ddd/src/value-objects/street.ts index eee8c391..e1a43217 100644 --- a/packages/rdx-ddd/src/value-objects/street.ts +++ b/packages/rdx-ddd/src/value-objects/street.ts @@ -1,6 +1,8 @@ import { Result } from "@repo/rdx-utils"; import { z } from "zod/v4"; + import { translateZodValidationError } from "../helpers"; + import { ValueObject } from "./value-object"; interface StreetProps { diff --git a/packages/rdx-logger/package.json b/packages/rdx-logger/package.json index 45217919..69715400 100644 --- a/packages/rdx-logger/package.json +++ b/packages/rdx-logger/package.json @@ -22,4 +22,4 @@ "winston": "^3.19.0", "winston-daily-rotate-file": "^5.0.0" } -} \ No newline at end of file +} diff --git a/packages/rdx-logger/src/strategies/console-logger.ts b/packages/rdx-logger/src/strategies/console-logger.ts index 59284515..cdadbcd1 100644 --- a/packages/rdx-logger/src/strategies/console-logger.ts +++ b/packages/rdx-logger/src/strategies/console-logger.ts @@ -20,7 +20,9 @@ export class ConsoleLogger implements ILogger { } private merge(meta: unknown): unknown { - if (!this.processMetadata) return meta ?? ""; + if (!this.processMetadata) { + return meta ?? ""; + } return typeof meta === "object" && meta !== null ? { ...this.processMetadata, ...meta } : { ...this.processMetadata, detail: meta ?? null }; diff --git a/packages/rdx-ui/package.json b/packages/rdx-ui/package.json index 70f94236..88a21b26 100644 --- a/packages/rdx-ui/package.json +++ b/packages/rdx-ui/package.json @@ -19,9 +19,7 @@ "./components": "./src/components/index.ts", "./components/*": "./src/components/*.tsx", "./locales/*": "./src/locales/*", - "./hooks": [ - "./src/hooks/index.ts" - ] + "./hooks": ["./src/hooks/index.ts"] }, "devDependencies": { "@biomejs/biome": "^2.4.11", @@ -69,4 +67,4 @@ "sonner": "^2.0.7", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/packages/rdx-ui/src/components/buttons/button-group.tsx b/packages/rdx-ui/src/components/buttons/button-group.tsx index 62769033..b99790a0 100644 --- a/packages/rdx-ui/src/components/buttons/button-group.tsx +++ b/packages/rdx-ui/src/components/buttons/button-group.tsx @@ -3,7 +3,7 @@ import React from "react"; export const ButtonGroup = React.forwardRef>( ({ className, ...props }, ref) => ( -
+
) ); ButtonGroup.displayName = "ButtonGroup"; diff --git a/packages/rdx-ui/src/components/context-panel/rdx-context-panel.test.tsx b/packages/rdx-ui/src/components/context-panel/rdx-context-panel.test.tsx index 73f43b32..360d39a3 100644 --- a/packages/rdx-ui/src/components/context-panel/rdx-context-panel.test.tsx +++ b/packages/rdx-ui/src/components/context-panel/rdx-context-panel.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; diff --git a/packages/rdx-ui/src/components/context-panel/rdx-context-panel.tsx b/packages/rdx-ui/src/components/context-panel/rdx-context-panel.tsx index 610ff38e..fde4073e 100644 --- a/packages/rdx-ui/src/components/context-panel/rdx-context-panel.tsx +++ b/packages/rdx-ui/src/components/context-panel/rdx-context-panel.tsx @@ -14,17 +14,14 @@ type RdxContextPanelClosableProps = closeLabel?: undefined; }; -export type RdxContextPanelProps = Omit< - ComponentPropsWithoutRef<"div">, - "children" | "title" -> & +export type RdxContextPanelProps = Omit, "children" | "title"> & RdxContextPanelClosableProps & { - title: ReactNode; - description?: ReactNode; - actions?: ReactNode; - children: ReactNode; - footer?: ReactNode; -}; + title: ReactNode; + description?: ReactNode; + actions?: ReactNode; + children: ReactNode; + footer?: ReactNode; + }; export const RdxContextPanel = ({ title, diff --git a/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination-button.tsx b/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination-button.tsx index d7945958..3d7e5d67 100644 --- a/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination-button.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination-button.tsx @@ -27,7 +27,9 @@ export const DataTablePaginationButton = ({ className )} onClick={() => { - if (!disabled) onClick(); + if (!disabled) { + onClick(); + } }} size="sm" tabIndex={disabled ? -1 : 0} diff --git a/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination.tsx b/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination.tsx index 96ae8858..2168633e 100644 --- a/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table-pagination/data-table-pagination.tsx @@ -25,7 +25,9 @@ export function DataTablePagination({ table, className }: DataTablePagina }; const handlePageSizeChange = (size: string | null) => { - if (!size) return; + if (!size) { + return; + } table.setPageSize(Number(size)); table.setPageIndex(0); diff --git a/packages/rdx-ui/src/components/datatable/data-table-toolbar.tsx b/packages/rdx-ui/src/components/datatable/data-table-toolbar.tsx index 539aff79..cb6e6456 100644 --- a/packages/rdx-ui/src/components/datatable/data-table-toolbar.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table-toolbar.tsx @@ -49,23 +49,33 @@ export function DataTableToolbar({ const selectedIndexes = React.useMemo(() => selectedRows.map((r) => r.index), [selectedRows]); const handleAdd = React.useCallback(() => { - if (!readOnly) meta?.tableOps?.onAdd?.(table); + if (!readOnly) { + meta?.tableOps?.onAdd?.(table); + } }, [meta, table, readOnly]); const handleDuplicateSelected = React.useCallback(() => { - if (!readOnly) meta?.bulkOps?.duplicateSelected?.(selectedIndexes, table); + if (!readOnly) { + meta?.bulkOps?.duplicateSelected?.(selectedIndexes, table); + } }, [meta, selectedIndexes, table, readOnly]); const handleMoveSelectedUp = React.useCallback(() => { - if (!readOnly) meta?.bulkOps?.moveSelectedUp?.(selectedIndexes, table); + if (!readOnly) { + meta?.bulkOps?.moveSelectedUp?.(selectedIndexes, table); + } }, [meta, selectedIndexes, table, readOnly]); const handleMoveSelectedDown = React.useCallback(() => { - if (!readOnly) meta?.bulkOps?.moveSelectedDown?.(selectedIndexes, table); + if (!readOnly) { + meta?.bulkOps?.moveSelectedDown?.(selectedIndexes, table); + } }, [meta, selectedIndexes, table, readOnly]); const handleRemoveSelected = React.useCallback(() => { - if (!readOnly) meta?.bulkOps?.removeSelected?.(selectedIndexes, table); + if (!readOnly) { + meta?.bulkOps?.removeSelected?.(selectedIndexes, table); + } }, [meta, selectedIndexes, table, readOnly]); const handleClearSelection = React.useCallback(() => { diff --git a/packages/rdx-ui/src/components/datatable/data-table-view-options.tsx b/packages/rdx-ui/src/components/datatable/data-table-view-options.tsx index a0091c40..9880335d 100644 --- a/packages/rdx-ui/src/components/datatable/data-table-view-options.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table-view-options.tsx @@ -65,7 +65,9 @@ const getColumnLabel = (column: Column): string => const h = column.columnDef.header; // header como string literal - if (typeof h === "string") label = meta?.title ?? h; + if (typeof h === "string") { + label = meta?.title ?? h; + } // Para cabeceras dinámicas, usamos un título explícito para no exponer ids técnicos. if (typeof h === "function") { diff --git a/packages/rdx-ui/src/components/datatable/data-table.tsx b/packages/rdx-ui/src/components/datatable/data-table.tsx index 59096fd7..3d2b3af6 100644 --- a/packages/rdx-ui/src/components/datatable/data-table.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table.tsx @@ -310,16 +310,16 @@ export function DataTable({ ) : null}
- - {/* CABECERA */} - - {table.getHeaderGroups().map((hg) => ( - + {/* CABECERA */} + + {table.getHeaderGroups().map((hg) => ( + - {hg.headers.map((h) => { - /* + > + {hg.headers.map((h) => { + /* const w = h.getSize(); const minW = h.column.columnDef.minSize; const maxW = h.column.columnDef.maxSize; @@ -333,49 +333,49 @@ export function DataTable({ */ - const headerClassName = h.column.columnDef.meta?.headerClassName; + const headerClassName = h.column.columnDef.meta?.headerClassName; - return ( - - {h.isPlaceholder - ? null - : flexRender(h.column.columnDef.header, h.getContext())} - - ); - })} - - ))} - + > + {h.isPlaceholder + ? null + : flexRender(h.column.columnDef.header, h.getContext())} + + ); + })} + + ))} + - {/* CUERPO */} - - {table.getRowModel().rows.length ? ( - table.getRowModel().rows.map((row, rowIndex) => ( - onRowClick?.(row.original, rowIndex, e)} - onKeyDown={(event) => { - if (event.key !== "Enter" && event.key !== " ") { - return; - } + {/* CUERPO */} + + {table.getRowModel().rows.length ? ( + table.getRowModel().rows.map((row, rowIndex) => ( + onRowClick?.(row.original, rowIndex, e)} + onKeyDown={(event) => { + if (event.key !== "Enter" && event.key !== " ") { + return; + } - event.preventDefault(); - onRowClick?.(row.original, rowIndex, event); - }} - role={onRowClick ? "button" : undefined} - tabIndex={onRowClick ? 0 : undefined} - > - {row.getVisibleCells().map((cell) => { - /* + event.preventDefault(); + onRowClick?.(row.original, rowIndex, event); + }} + role={onRowClick ? "button" : undefined} + tabIndex={onRowClick ? 0 : undefined} + > + {row.getVisibleCells().map((cell) => { + /* const w = cell.column.getSize(); const minW = cell.column.columnDef.minSize; const maxW = cell.column.columnDef.maxSize; @@ -387,45 +387,45 @@ export function DataTable({ */ - const cellClassName = cell.column.columnDef.meta?.cellClassName; + const cellClassName = cell.column.columnDef.meta?.cellClassName; - return ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ); - })} + return ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ); + })} + + )) + ) : ( + + + {t("components.datatable.empty")} + - )) - ) : ( - - - {t("components.datatable.empty")} - - - )} - + )} + - {/* Paginación */} - {enablePagination && ( + {/* Paginación */} + {enablePagination && ( - - - + + + - - )} - + + )} +
diff --git a/packages/rdx-ui/src/components/form/form-actions/form-secondary-actions-menu.tsx b/packages/rdx-ui/src/components/form/form-actions/form-secondary-actions-menu.tsx index a117e98b..8b3664bd 100644 --- a/packages/rdx-ui/src/components/form/form-actions/form-secondary-actions-menu.tsx +++ b/packages/rdx-ui/src/components/form/form-actions/form-secondary-actions-menu.tsx @@ -39,7 +39,9 @@ export const FormSecondaryActionsMenu = ({ }: FormSecondaryActionsMenuProps) => { const visibleActions = actions; - if (visibleActions.length === 0) return null; + if (visibleActions.length === 0) { + return null; + } return ( diff --git a/packages/rdx-ui/src/components/form/index.ts b/packages/rdx-ui/src/components/form/index.ts index 99df1d61..ea5f7a01 100644 --- a/packages/rdx-ui/src/components/form/index.ts +++ b/packages/rdx-ui/src/components/form/index.ts @@ -4,11 +4,11 @@ export * from "./date-picker-input-field/index.ts"; export * from "./decimal-field/index.ts"; export * from "./form-actions/index.ts"; export * from "./form-field-label.tsx"; -export * from "./rdx-form-grid.tsx"; export * from "./form-section-card.tsx"; export * from "./form-section-grid.tsx"; export * from "./multi-select-field.tsx"; export * from "./radio-group-field.tsx"; +export * from "./rdx-form-grid.tsx"; export * from "./select-field.tsx"; export * from "./semantic-fields/index.ts"; export * from "./switch-field.tsx"; diff --git a/packages/rdx-ui/src/components/form/multi-select-field.tsx b/packages/rdx-ui/src/components/form/multi-select-field.tsx index ab11d971..3e89ea9c 100644 --- a/packages/rdx-ui/src/components/form/multi-select-field.tsx +++ b/packages/rdx-ui/src/components/form/multi-select-field.tsx @@ -112,7 +112,9 @@ export const MultiSelectField = ({ const grouped = options.reduce>((acc, item) => { const key = item.group || ""; - if (!acc[key]) acc[key] = []; + if (!acc[key]) { + acc[key] = []; + } acc[key].push(item); return acc; }, {}); @@ -255,7 +257,9 @@ export const MultiSelectField = ({ disabled={optionDisabled} key={option.value} onSelect={() => { - if (optionDisabled) return; + if (optionDisabled) { + return; + } toggleOption(option.value); }} > diff --git a/packages/rdx-ui/src/components/form/semantic-fields/line-description-field.tsx b/packages/rdx-ui/src/components/form/semantic-fields/line-description-field.tsx index 88b5b071..8c7a672c 100644 --- a/packages/rdx-ui/src/components/form/semantic-fields/line-description-field.tsx +++ b/packages/rdx-ui/src/components/form/semantic-fields/line-description-field.tsx @@ -62,7 +62,9 @@ export const LineDescriptionField = ({ const resizeTextarea = React.useCallback(() => { const textarea = textareaRef.current; - if (!textarea) return; + if (!textarea) { + return; + } const minHeight = minRows * DEFAULT_LINE_HEIGHT; const maxHeight = maxRows * DEFAULT_LINE_HEIGHT; @@ -77,7 +79,7 @@ export const LineDescriptionField = ({ React.useEffect(() => { resizeTextarea(); - }, []); + }, [resizeTextarea]); return ( diff --git a/packages/rdx-ui/src/components/form/text-area-field.tsx b/packages/rdx-ui/src/components/form/text-area-field.tsx index aacd1bce..fa1356f8 100644 --- a/packages/rdx-ui/src/components/form/text-area-field.tsx +++ b/packages/rdx-ui/src/components/form/text-area-field.tsx @@ -45,7 +45,9 @@ export function TextAreaField({ const handleKeyDown = (e: React.KeyboardEvent) => { inputRest.onKeyDown?.(e); - if (e.defaultPrevented) return; + if (e.defaultPrevented) { + return; + } // Cmd/Ctrl + Enter para submit if ((e.metaKey || e.ctrlKey) && e.key === "Enter" && !e.nativeEvent.isComposing) { diff --git a/packages/rdx-ui/src/components/form/text-field-presets.tsx b/packages/rdx-ui/src/components/form/text-field-presets.tsx index 3a2faf53..3637e32e 100644 --- a/packages/rdx-ui/src/components/form/text-field-presets.tsx +++ b/packages/rdx-ui/src/components/form/text-field-presets.tsx @@ -69,8 +69,6 @@ export const getInputPresetProps = (preset: TextFieldTypePreset = "text"): Resol spellCheck: false, enterKeyHint: "next", }; - - case "text": default: return { type: "text", diff --git a/packages/rdx-ui/src/components/full-screen-modal.tsx b/packages/rdx-ui/src/components/full-screen-modal.tsx index 502ae77f..847b85a7 100644 --- a/packages/rdx-ui/src/components/full-screen-modal.tsx +++ b/packages/rdx-ui/src/components/full-screen-modal.tsx @@ -1,8 +1,7 @@ -import type React from "react"; - import { Button, Dialog, DialogContent } from "@repo/shadcn-ui/components"; import { cn } from "@repo/shadcn-ui/lib/utils"; import { X } from "lucide-react"; +import type React from "react"; interface FullscreenModalProps { isOpen: boolean; @@ -20,7 +19,7 @@ export const FullscreenModal = ({ className, }: FullscreenModalProps) => { return ( - + {/* Header fijo */} -
-

{title}

-
{/* Contenido scrolleable */} -
{children}
+
{children}
); diff --git a/packages/rdx-ui/src/components/initials-avatar.tsx b/packages/rdx-ui/src/components/initials-avatar.tsx index 2845df20..ce35a7ca 100644 --- a/packages/rdx-ui/src/components/initials-avatar.tsx +++ b/packages/rdx-ui/src/components/initials-avatar.tsx @@ -9,7 +9,9 @@ type InitialsProps = { }; export const getInitials = ({ name, maxParts = 2, fallback = "?" }: InitialsProps): string => { - if (!name?.trim()) return fallback; + if (!name?.trim()) { + return fallback; + } return ( name diff --git a/packages/rdx-ui/src/components/loading-overlay/loading-spin-icon.tsx b/packages/rdx-ui/src/components/loading-overlay/loading-spin-icon.tsx index e621043b..875a52b5 100644 --- a/packages/rdx-ui/src/components/loading-overlay/loading-spin-icon.tsx +++ b/packages/rdx-ui/src/components/loading-overlay/loading-spin-icon.tsx @@ -11,17 +11,17 @@ export const LoadingSpinIcon = ({ }): JSX.Element => ( Loading spinner - + ); diff --git a/packages/rdx-ui/src/components/lookup-dialog/lookup-dialog.tsx b/packages/rdx-ui/src/components/lookup-dialog/lookup-dialog.tsx index 3dd49fa1..6d5aedbe 100644 --- a/packages/rdx-ui/src/components/lookup-dialog/lookup-dialog.tsx +++ b/packages/rdx-ui/src/components/lookup-dialog/lookup-dialog.tsx @@ -1,5 +1,3 @@ -import { useTranslation } from "../../locales/i18n.ts"; - import { Button, Dialog, @@ -13,6 +11,8 @@ import { } from "@repo/shadcn-ui/components"; import { PlusIcon, RefreshCwIcon } from "lucide-react"; +import { useTranslation } from "../../locales/i18n.ts"; + type LookupDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; @@ -71,10 +71,10 @@ export const LookupDialog = ({ "3xl": "custom-dialog-3xl", }[maxWidth]; - const showPagination = totalItems !== undefined && onPageChange !== undefined; + const _showPagination = totalItems !== undefined && onPageChange !== undefined; return ( - + {title || t("components.entity_selector.select_entity")} @@ -83,58 +83,58 @@ export const LookupDialog = ({ -
+
onSearchChange(e.target.value)} placeholder={searchPlaceholder || t("components.entity_selector.search_entity")} value={search} - onChange={(e) => onSearchChange(e.target.value)} - className='flex-1' />
{isLoading && ( -

+

{t("components.entity_selector.loading")}

)} {isError && ( -
+

Error al cargar los datos.

{refetch && ( - )}
)} - {!(isLoading || isError ) && ( + {!(isLoading || isError) && ( <> {items.length === 0 ? ( emptyStateComponent || ( -

+

{t("components.entity_selector.no_entities_found")}

) ) : ( -
{renderContainer(items)}
+
{renderContainer(items)}
)} )} -
+
- - diff --git a/packages/rdx-ui/src/components/multi-select.tsx b/packages/rdx-ui/src/components/multi-select.tsx index fd8d61ce..50450cc8 100644 --- a/packages/rdx-ui/src/components/multi-select.tsx +++ b/packages/rdx-ui/src/components/multi-select.tsx @@ -168,7 +168,9 @@ export const MultiSelect = React.forwardRef const { t } = useTranslation(); const applySelectedFilter = React.useCallback(() => { - if (!filterSelected) return; + if (!filterSelected) { + return; + } const filtered = filterSelected(selectedValues); if (filtered.length !== selectedValues.length) { setSelectedValues(filtered); @@ -178,11 +180,15 @@ export const MultiSelect = React.forwardRef // Filtro automático cuando cambia selectedValues React.useEffect(() => { - if (autoFilter) applySelectedFilter(); - }, [autoFilter, selectedValues, applySelectedFilter]); + if (autoFilter) { + applySelectedFilter(); + } + }, [autoFilter, applySelectedFilter]); const grouped = options.reduce>((acc, item) => { - if (!acc[item.group || ""]) acc[item.group || ""] = []; + if (!acc[item.group || ""]) { + acc[item.group || ""] = []; + } acc[item.group ?? ""]?.push(item); return acc; }, {}); diff --git a/packages/rdx-ui/src/components/right-panel/right-panel.tsx b/packages/rdx-ui/src/components/right-panel/right-panel.tsx index ba3bb2af..0855c1fe 100644 --- a/packages/rdx-ui/src/components/right-panel/right-panel.tsx +++ b/packages/rdx-ui/src/components/right-panel/right-panel.tsx @@ -3,12 +3,7 @@ import { cn } from "@repo/shadcn-ui/lib/utils"; import type { RightPanelProps } from "./right-panel-types.ts"; -export const RightPanel = ({ - open, - className, - children, - ...props -}: RightPanelProps) => { +export const RightPanel = ({ open, className, children, ...props }: RightPanelProps) => { if (!open) { return null; } diff --git a/packages/rdx-ui/src/components/row-actions/rdx-row-actions.test.tsx b/packages/rdx-ui/src/components/row-actions/rdx-row-actions.test.tsx index 7715a772..9604c743 100644 --- a/packages/rdx-ui/src/components/row-actions/rdx-row-actions.test.tsx +++ b/packages/rdx-ui/src/components/row-actions/rdx-row-actions.test.tsx @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; + import { isValidElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; @@ -27,7 +28,9 @@ const resolveElement = (node: unknown): unknown => { }; }; -const collectElements = (node: unknown): Array<{ type: unknown; props: Record }> => { +const collectElements = ( + node: unknown +): Array<{ type: unknown; props: Record }> => { if (Array.isArray(node)) { return node.flatMap(collectElements); } @@ -46,9 +49,7 @@ const findElement = ( predicate: (element: { type: unknown; props: Record }) => boolean ) => collectElements(node).find(predicate) ?? null; -const invokeClick = async ( - element: { type: unknown; props: Record } | null -) => { +const invokeClick = async (element: { type: unknown; props: Record } | null) => { const onClick = element?.props.onClick as | ((event: { preventDefault: () => void; stopPropagation: () => void }) => unknown) | undefined; diff --git a/packages/rdx-ui/src/components/row-actions/rdx-row-actions.tsx b/packages/rdx-ui/src/components/row-actions/rdx-row-actions.tsx index 63b8b0af..9aefcc06 100644 --- a/packages/rdx-ui/src/components/row-actions/rdx-row-actions.tsx +++ b/packages/rdx-ui/src/components/row-actions/rdx-row-actions.tsx @@ -27,8 +27,7 @@ export interface RdxRowAction { destructive?: boolean; } -export interface RdxRowActionsProps - extends Omit, "children"> { +export interface RdxRowActionsProps extends Omit, "children"> { primaryActions?: RdxRowAction[]; overflowActions?: RdxRowAction[]; overflowLabel: string; @@ -84,9 +83,7 @@ export const RdxRowActions = ({ return ( {button} : button - } + render={action.disabled ? {button} : button} /> {getTooltipLabel(action)} diff --git a/packages/rdx-ui/src/components/tailwind-indicator.tsx b/packages/rdx-ui/src/components/tailwind-indicator.tsx index 0f38c8bf..583e028e 100644 --- a/packages/rdx-ui/src/components/tailwind-indicator.tsx +++ b/packages/rdx-ui/src/components/tailwind-indicator.tsx @@ -1,5 +1,7 @@ export function TailwindIndicator() { - if (process.env.NODE_ENV === "production") return null; + if (process.env.NODE_ENV === "production") { + return null; + } return (
diff --git a/packages/rdx-ui/src/helpers/forms/get-first-form-error-message.ts b/packages/rdx-ui/src/helpers/forms/get-first-form-error-message.ts index c05118e9..d861cb7b 100644 --- a/packages/rdx-ui/src/helpers/forms/get-first-form-error-message.ts +++ b/packages/rdx-ui/src/helpers/forms/get-first-form-error-message.ts @@ -2,7 +2,7 @@ import type { FieldErrors, FieldValues, UseFormReturn } from "react-hook-form"; export const getFirstFormErrorMessage = ( errors: FieldErrors, - form: UseFormReturn + _form: UseFormReturn ) => { const firstKey = Object.keys(errors)[0] as keyof T | undefined; const firstError = errors[firstKey] as diff --git a/packages/rdx-ui/src/helpers/forms/prevent-enter-key-submit-form.ts b/packages/rdx-ui/src/helpers/forms/prevent-enter-key-submit-form.ts index db91d742..5dbc5063 100644 --- a/packages/rdx-ui/src/helpers/forms/prevent-enter-key-submit-form.ts +++ b/packages/rdx-ui/src/helpers/forms/prevent-enter-key-submit-form.ts @@ -1,15 +1,21 @@ export const preventEnterKeySubmitForm = (event: React.KeyboardEvent) => { - if (event.key !== "Enter") return; + if (event.key !== "Enter") { + return; + } const target = event.target; - if (!(target instanceof HTMLElement)) return; + if (!(target instanceof HTMLElement)) { + return; + } const isTextArea = target.tagName === "TEXTAREA"; const isSubmitButton = target.tagName === "BUTTON" && (target as HTMLButtonElement).type === "submit"; - if (isTextArea || isSubmitButton) return; + if (isTextArea || isSubmitButton) { + return; + } event.preventDefault(); }; diff --git a/packages/rdx-ui/src/hooks/use-device-info.ts b/packages/rdx-ui/src/hooks/use-device-info.ts index 383b8b4e..a6a7aa42 100644 --- a/packages/rdx-ui/src/hooks/use-device-info.ts +++ b/packages/rdx-ui/src/hooks/use-device-info.ts @@ -35,7 +35,9 @@ function useMediaQuery(query: string, fallback = false) { const [matches, setMatches] = useState(fallback); useEffect(() => { - if (typeof window === "undefined") return; // SSR + if (typeof window === "undefined") { + return; // SSR + } const mql = window.matchMedia(query); const onChange = (e: MediaQueryListEvent) => setMatches(e.matches); setMatches(mql.matches); @@ -54,8 +56,12 @@ function classifyDevice( hoverNone: boolean ): DeviceType { // Heurística práctica: - if (pointerCoarse && (widthNarrow || hoverNone)) return "phone"; - if (pointerCoarse && widthTablet) return "tablet"; + if (pointerCoarse && (widthNarrow || hoverNone)) { + return "phone"; + } + if (pointerCoarse && widthTablet) { + return "tablet"; + } return "desktop"; } @@ -96,7 +102,9 @@ export function useDeviceInfo(opts: Options = {}): DeviceInfo { ); useEffect(() => { - if (typeof window === "undefined") return; + if (typeof window === "undefined") { + return; + } // maxTouchPoints es útil para detectar iPadOS que se disfraza de Mac setMaxTouchPoints(navigator.maxTouchPoints || 0); @@ -137,7 +145,9 @@ export function useDeviceInfo(opts: Options = {}): DeviceInfo { // Si teníamos estado inicial en SSR, al primer render en cliente lo reemplazamos por el real useEffect(() => { - if (initial) setInitial(null); + if (initial) { + setInitial(null); + } }, [initial]); return { diff --git a/packages/rdx-ui/src/hooks/use-row-selection.ts b/packages/rdx-ui/src/hooks/use-row-selection.ts index c9517ecb..0b157135 100644 --- a/packages/rdx-ui/src/hooks/use-row-selection.ts +++ b/packages/rdx-ui/src/hooks/use-row-selection.ts @@ -16,8 +16,12 @@ export function useRowSelection(totalRows: number) { // Estado visual del checkbox maestro const selectAllState: CheckedState = React.useMemo(() => { - if (totalRows === 0 || selectedRows.size === 0) return false; - if (selectedRows.size === totalRows) return true; + if (totalRows === 0 || selectedRows.size === 0) { + return false; + } + if (selectedRows.size === totalRows) { + return true; + } return "indeterminate"; }, [selectedRows, totalRows]); diff --git a/packages/rdx-ui/src/locales/es.json b/packages/rdx-ui/src/locales/es.json index 3fb2bc3f..44c82d73 100644 --- a/packages/rdx-ui/src/locales/es.json +++ b/packages/rdx-ui/src/locales/es.json @@ -68,4 +68,4 @@ "select_date": "Seleccionar fecha" } } -} \ No newline at end of file +} diff --git a/packages/rdx-utils/package.json b/packages/rdx-utils/package.json index 1049a9ed..94ba0136 100644 --- a/packages/rdx-utils/package.json +++ b/packages/rdx-utils/package.json @@ -23,4 +23,4 @@ "joi": "^18.1.2", "uuid": "^13.0.0" } -} \ No newline at end of file +} diff --git a/packages/rdx-utils/src/helpers/date-helper.ts b/packages/rdx-utils/src/helpers/date-helper.ts index c311c3e8..9982a48e 100644 --- a/packages/rdx-utils/src/helpers/date-helper.ts +++ b/packages/rdx-utils/src/helpers/date-helper.ts @@ -14,7 +14,9 @@ function formatDate( options?: Intl.DateTimeFormatOptions ): string { const date = normalizeToDate(input); - if (!date || Number.isNaN(date.getTime())) return ""; + if (!date || Number.isNaN(date.getTime())) { + return ""; + } // Por defecto, formato corto y consistente. const fmt = new Intl.DateTimeFormat(locale, { @@ -29,12 +31,18 @@ function formatDate( /** Normaliza distintos inputs a Date. Maneja "YYYY-MM-DD" de forma segura. */ function normalizeToDate(input: DateInput): Date | null { - if (input instanceof Date) return input; - if (typeof input === "number") return new Date(input); + if (input instanceof Date) { + return input; + } + if (typeof input === "number") { + return new Date(input); + } if (typeof input === "string") { const trimmed = input.trim(); - if (!trimmed) return null; + if (!trimmed) { + return null; + } // Patrón "date-only" ISO (sin hora) const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed); @@ -46,7 +54,9 @@ function normalizeToDate(input: DateInput): Date | null { // Cualquier otra cadena se delega al parser nativo const t = Date.parse(trimmed); - if (Number.isNaN(t)) return null; + if (Number.isNaN(t)) { + return null; + } return new Date(t); } diff --git a/packages/rdx-utils/src/helpers/number-helper.ts b/packages/rdx-utils/src/helpers/number-helper.ts index d3b286e0..1eff86a3 100644 --- a/packages/rdx-utils/src/helpers/number-helper.ts +++ b/packages/rdx-utils/src/helpers/number-helper.ts @@ -23,10 +23,14 @@ const formatNumber = (value: string | number | null | undefined, scale: number): // Heurística robusta: determina decimal por última ocurrencia de , o . const parseLocaleNumber = (raw: string, locale = "es-ES"): number | null => { - if (!raw) return null; + if (!raw) { + return null; + } const s = MoneyHelper.stripCurrencySymbols(raw); - if (!s) return null; + if (!s) { + return null; + } // Obtener formato local const example = Intl.NumberFormat(locale).format(1000.1); diff --git a/packages/rdx-utils/src/helpers/patch-field.ts b/packages/rdx-utils/src/helpers/patch-field.ts index 1afa3d6d..f9fdb65d 100644 --- a/packages/rdx-utils/src/helpers/patch-field.ts +++ b/packages/rdx-utils/src/helpers/patch-field.ts @@ -1,4 +1,3 @@ - /** * Representa un campo PATCH con semántica tri-estado: * @@ -52,7 +51,9 @@ export class PatchField { * @returns Instancia en estado set */ static set(value: T | null): PatchField { - if (value === null) return new PatchField(true, true); + if (value === null) { + return new PatchField(true, true); + } return new PatchField(true, false, value); } @@ -102,7 +103,9 @@ export class PatchField { * @returns this (permite chaining) */ ifSet(fn: (v: T) => void): PatchField { - if (this._isSet && !this._isNull) fn(this._value!); + if (this._isSet && !this._isNull) { + fn(this._value!); + } return this; } @@ -117,7 +120,9 @@ export class PatchField { * @returns this (permite chaining) */ ifNull(fn: () => void): PatchField { - if (this._isSet && this._isNull) fn(); + if (this._isSet && this._isNull) { + fn(); + } return this; } @@ -131,7 +136,9 @@ export class PatchField { * @returns this (permite chaining) */ ifSetOrNull(fn: (v: T | null) => void): PatchField { - if (this._isSet) fn(this._isNull ? null : this._value!); + if (this._isSet) { + fn(this._isNull ? null : this._value!); + } return this; } } @@ -159,7 +166,11 @@ export class PatchField { * ``` */ export function toPatchField(value: T | null | undefined): PatchField { - if (value === undefined) return PatchField.unset(); - if (value === null) return PatchField.set(null); + if (value === undefined) { + return PatchField.unset(); + } + if (value === null) { + return PatchField.set(null); + } return PatchField.set(value as T); } diff --git a/packages/rdx-utils/src/helpers/result.ts b/packages/rdx-utils/src/helpers/result.ts index 78dc34f7..024095e2 100644 --- a/packages/rdx-utils/src/helpers/result.ts +++ b/packages/rdx-utils/src/helpers/result.ts @@ -8,7 +8,7 @@ export class Result { if (isSuccess && error) { throw new Error("InvalidOperation: A result cannot be successful and contain an error"); } - if (!isSuccess && !error) { + if (!(isSuccess || error)) { throw new Error("InvalidOperation: A failing result needs to contain an error message"); } diff --git a/packages/shadcn-ui/components.json b/packages/shadcn-ui/components.json index ef4a0d9b..ec8311bc 100644 --- a/packages/shadcn-ui/components.json +++ b/packages/shadcn-ui/components.json @@ -21,4 +21,4 @@ "rtl": false, "menuColor": "default", "menuAccent": "subtle" -} \ No newline at end of file +} diff --git a/packages/shadcn-ui/package.json b/packages/shadcn-ui/package.json index 3c4952b2..29f69df3 100644 --- a/packages/shadcn-ui/package.json +++ b/packages/shadcn-ui/package.json @@ -10,10 +10,7 @@ "./components": "./src/components/index.tsx", "./components/*": "./src/components/*.tsx", "./lib/*": "./src/lib/*.ts", - "./hooks/*": [ - "./src/hooks/*.ts", - "./src/hooks/*/index.ts" - ] + "./hooks/*": ["./src/hooks/*.ts", "./src/hooks/*/index.ts"] }, "scripts": { "ui:add": "pnpm dlx shadcn@latest add" @@ -67,4 +64,4 @@ "vaul": "^1.1.2", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/packages/shadcn-ui/src/components/accordion.tsx b/packages/shadcn-ui/src/components/accordion.tsx index 306a7e85..34295a88 100644 --- a/packages/shadcn-ui/src/components/accordion.tsx +++ b/packages/shadcn-ui/src/components/accordion.tsx @@ -1,60 +1,57 @@ -import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion" - -import { cn } from "@repo/shadcn-ui/lib/utils" -import { ChevronDownIcon, ChevronUpIcon } from "lucide-react" +import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"; function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { return ( - ) + ); } function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { return ( - ) + ); } -function AccordionTrigger({ - className, - children, - ...props -}: AccordionPrimitive.Trigger.Props) { +function AccordionTrigger({ className, children, ...props }: AccordionPrimitive.Trigger.Props) { return ( {children} - - + + - ) + ); } -function AccordionContent({ - className, - children, - ...props -}: AccordionPrimitive.Panel.Props) { +function AccordionContent({ className, children, ...props }: AccordionPrimitive.Panel.Props) { return (
- ) + ); } -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }; diff --git a/packages/shadcn-ui/src/components/alert-dialog.tsx b/packages/shadcn-ui/src/components/alert-dialog.tsx index 363ce996..f101f835 100644 --- a/packages/shadcn-ui/src/components/alert-dialog.tsx +++ b/packages/shadcn-ui/src/components/alert-dialog.tsx @@ -1,41 +1,33 @@ -"use client" +"use client"; -import type * as React from "react" -import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" - -import { cn } from "@repo/shadcn-ui/lib/utils" -import { Button } from "@repo/shadcn-ui/components/button" +import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"; +import { Button } from "@repo/shadcn-ui/components/button"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import type * as React from "react"; function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { - return + return ; } function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { - return ( - - ) + return ; } function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { - return ( - - ) + return ; } -function AlertDialogOverlay({ - className, - ...props -}: AlertDialogPrimitive.Backdrop.Props) { +function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) { return ( - ) + ); } function AlertDialogContent({ @@ -43,70 +35,61 @@ function AlertDialogContent({ size = "default", ...props }: AlertDialogPrimitive.Popup.Props & { - size?: "default" | "sm" + size?: "default" | "sm"; }) { return ( - ) + ); } -function AlertDialogHeader({ - className, - ...props -}: React.ComponentProps<"div">) { +function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) { return (
- ) + ); } -function AlertDialogFooter({ - className, - ...props -}: React.ComponentProps<"div">) { +function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) { return (
- ) + ); } -function AlertDialogMedia({ - className, - ...props -}: React.ComponentProps<"div">) { +function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">) { return (
- ) + ); } function AlertDialogTitle({ @@ -115,14 +98,14 @@ function AlertDialogTitle({ }: React.ComponentProps) { return ( - ) + ); } function AlertDialogDescription({ @@ -131,27 +114,18 @@ function AlertDialogDescription({ }: React.ComponentProps) { return ( - ) + ); } -function AlertDialogAction({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) + ); } function CarouselNext({ @@ -207,13 +201,10 @@ function CarouselNext({ size = "icon-sm", ...props }: React.ComponentProps) { - const { orientation, scrollNext, canScrollNext } = useCarousel() + const { orientation, scrollNext, canScrollNext } = useCarousel(); return ( - ) + ); } export { - type CarouselApi, Carousel, + type CarouselApi, CarouselContent, CarouselItem, - CarouselPrevious, CarouselNext, + CarouselPrevious, useCarousel, -} +}; diff --git a/packages/shadcn-ui/src/components/chart.tsx b/packages/shadcn-ui/src/components/chart.tsx index b2dd186a..cfbd10b7 100644 --- a/packages/shadcn-ui/src/components/chart.tsx +++ b/packages/shadcn-ui/src/components/chart.tsx @@ -1,40 +1,39 @@ -import * as React from "react" -import * as RechartsPrimitive from "recharts" -import type { TooltipValueType } from "recharts" - -import { cn } from "@repo/shadcn-ui/lib/utils" +import { cn } from "@repo/shadcn-ui/lib/utils"; +import * as React from "react"; +import type { TooltipValueType } from "recharts"; +import * as RechartsPrimitive from "recharts"; // Format: { THEME_NAME: CSS_SELECTOR } -const THEMES = { light: "", dark: ".dark" } as const +const THEMES = { light: "", dark: ".dark" } as const; -const INITIAL_DIMENSION = { width: 320, height: 200 } as const -type TooltipNameType = number | string +const INITIAL_DIMENSION = { width: 320, height: 200 } as const; +type TooltipNameType = number | string; export type ChartConfig = Record< string, { - label?: React.ReactNode - icon?: React.ComponentType + label?: React.ReactNode; + icon?: React.ComponentType; } & ( | { color?: string; theme?: never } | { color?: never; theme: Record } ) -> +>; type ChartContextProps = { - config: ChartConfig -} + config: ChartConfig; +}; -const ChartContext = React.createContext(null) +const ChartContext = React.createContext(null); function useChart() { - const context = React.useContext(ChartContext) + const context = React.useContext(ChartContext); if (!context) { - throw new Error("useChart must be used within a ") + throw new Error("useChart must be used within a "); } - return context + return context; } function ChartContainer({ @@ -45,47 +44,41 @@ function ChartContainer({ initialDimension = INITIAL_DIMENSION, ...props }: React.ComponentProps<"div"> & { - config: ChartConfig - children: React.ComponentProps< - typeof RechartsPrimitive.ResponsiveContainer - >["children"] + config: ChartConfig; + children: React.ComponentProps["children"]; initialDimension?: { - width: number - height: number - } + width: number; + height: number; + }; }) { - const uniqueId = React.useId() - const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}` + const uniqueId = React.useId(); + const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`; return (
- - + + {children}
- ) + ); } const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { - const colorConfig = Object.entries(config).filter( - ([, config]) => config.theme ?? config.color - ) + const colorConfig = Object.entries(config).filter(([, config]) => config.theme ?? config.color); if (!colorConfig.length) { - return null + return null; } return ( @@ -97,10 +90,8 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { ${prefix} [data-chart=${id}] { ${colorConfig .map(([key, itemConfig]) => { - const color = - itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ?? - itemConfig.color - return color ? ` --color-${key}: ${color};` : null + const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ?? itemConfig.color; + return color ? ` --color-${key}: ${color};` : null; }) .join("\n")} } @@ -109,10 +100,10 @@ ${colorConfig .join("\n"), }} /> - ) -} + ); +}; -const ChartTooltip = RechartsPrimitive.Tooltip +const ChartTooltip = RechartsPrimitive.Tooltip; function ChartTooltipContent({ active, @@ -130,61 +121,46 @@ function ChartTooltipContent({ labelKey, }: React.ComponentProps & React.ComponentProps<"div"> & { - hideLabel?: boolean - hideIndicator?: boolean - indicator?: "line" | "dot" | "dashed" - nameKey?: string - labelKey?: string + hideLabel?: boolean; + hideIndicator?: boolean; + indicator?: "line" | "dot" | "dashed"; + nameKey?: string; + labelKey?: string; } & Omit< - RechartsPrimitive.DefaultTooltipContentProps< - TooltipValueType, - TooltipNameType - >, + RechartsPrimitive.DefaultTooltipContentProps, "accessibilityLayer" >) { - const { config } = useChart() + const { config } = useChart(); const tooltipLabel = React.useMemo(() => { if (hideLabel || !payload?.length) { - return null + return null; } - const [item] = payload - const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) + const [item] = payload; + const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); const value = - !labelKey && typeof label === "string" - ? (config[label]?.label ?? label) - : itemConfig?.label + !labelKey && typeof label === "string" ? (config[label]?.label ?? label) : itemConfig?.label; if (labelFormatter) { return ( -
- {labelFormatter(value, payload)} -
- ) +
{labelFormatter(value, payload)}
+ ); } if (!value) { - return null + return null; } - return
{value}
- }, [ - label, - labelFormatter, - payload, - hideLabel, - labelClassName, - config, - labelKey, - ]) + return
{value}
; + }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]); if (!(active && payload?.length)) { - return null + return null; } - const nestLabel = payload.length === 1 && indicator !== "dot" + const nestLabel = payload.length === 1 && indicator !== "dot"; return (
item.type !== "none") .map((item, index) => { - const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) - const indicatorColor = color ?? item.payload?.fill ?? item.color + const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); + const indicatorColor = color ?? item.payload?.fill ?? item.color; return (
svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center" )} + key={index} > {formatter && item?.value !== undefined && item.name ? ( formatter(item.value, item.name, item, index, item.payload) @@ -261,14 +237,14 @@ function ChartTooltipContent({ )}
- ) + ); })}
- ) + ); } -const ChartLegend = RechartsPrimitive.Legend +const ChartLegend = RechartsPrimitive.Legend; function ChartLegendContent({ className, @@ -277,13 +253,13 @@ function ChartLegendContent({ verticalAlign = "bottom", nameKey, }: React.ComponentProps<"div"> & { - hideIcon?: boolean - nameKey?: string + hideIcon?: boolean; + nameKey?: string; } & RechartsPrimitive.DefaultLegendContentProps) { - const { config } = useChart() + const { config } = useChart(); if (!payload?.length) { - return null + return null; } return ( @@ -297,15 +273,15 @@ function ChartLegendContent({ {payload .filter((item) => item.type !== "none") .map((item, index) => { - const key = `${nameKey ?? item.dataKey ?? "value"}` - const itemConfig = getPayloadConfigFromPayload(config, item, key) + const key = `${nameKey ?? item.dataKey ?? "value"}`; + const itemConfig = getPayloadConfigFromPayload(config, item, key); return (
svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" )} + key={index} > {itemConfig?.icon && !hideIcon ? ( @@ -319,53 +295,42 @@ function ChartLegendContent({ )} {itemConfig?.label}
- ) + ); })}
- ) + ); } -function getPayloadConfigFromPayload( - config: ChartConfig, - payload: unknown, - key: string -) { +function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) { if (typeof payload !== "object" || payload === null) { - return undefined + return undefined; } const payloadPayload = - "payload" in payload && - typeof payload.payload === "object" && - payload.payload !== null + "payload" in payload && typeof payload.payload === "object" && payload.payload !== null ? payload.payload - : undefined + : undefined; - let configLabelKey: string = key + let configLabelKey: string = key; - if ( - key in payload && - typeof payload[key as keyof typeof payload] === "string" - ) { - configLabelKey = payload[key as keyof typeof payload] as string + if (key in payload && typeof payload[key as keyof typeof payload] === "string") { + configLabelKey = payload[key as keyof typeof payload] as string; } else if ( payloadPayload && key in payloadPayload && typeof payloadPayload[key as keyof typeof payloadPayload] === "string" ) { - configLabelKey = payloadPayload[ - key as keyof typeof payloadPayload - ] as string + configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string; } - return configLabelKey in config ? config[configLabelKey] : config[key] + return configLabelKey in config ? config[configLabelKey] : config[key]; } export { ChartContainer, - ChartTooltip, - ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle, -} + ChartTooltip, + ChartTooltipContent, +}; diff --git a/packages/shadcn-ui/src/components/checkbox.tsx b/packages/shadcn-ui/src/components/checkbox.tsx index f52d21b7..14130f7c 100644 --- a/packages/shadcn-ui/src/components/checkbox.tsx +++ b/packages/shadcn-ui/src/components/checkbox.tsx @@ -1,29 +1,27 @@ -"use client" +"use client"; -import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox" - -import { cn } from "@repo/shadcn-ui/lib/utils" -import { CheckIcon } from "lucide-react" +import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { CheckIcon } from "lucide-react"; function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) { return ( - + - ) + ); } -export { Checkbox } +export { Checkbox }; diff --git a/packages/shadcn-ui/src/components/collapsible.tsx b/packages/shadcn-ui/src/components/collapsible.tsx index 4b242f71..1705aa32 100644 --- a/packages/shadcn-ui/src/components/collapsible.tsx +++ b/packages/shadcn-ui/src/components/collapsible.tsx @@ -1,19 +1,15 @@ -import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible" +import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"; function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) { - return + return ; } function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) { - return ( - - ) + return ; } function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) { - return ( - - ) + return ; } -export { Collapsible, CollapsibleTrigger, CollapsibleContent } +export { Collapsible, CollapsibleContent, CollapsibleTrigger }; diff --git a/packages/shadcn-ui/src/components/combobox.tsx b/packages/shadcn-ui/src/components/combobox.tsx index 09e6dad1..2553d6a0 100644 --- a/packages/shadcn-ui/src/components/combobox.tsx +++ b/packages/shadcn-ui/src/components/combobox.tsx @@ -1,50 +1,45 @@ -import * as React from "react" -import { Combobox as ComboboxPrimitive } from "@base-ui/react" - -import { cn } from "@repo/shadcn-ui/lib/utils" -import { Button } from "@repo/shadcn-ui/components/button" +import { Combobox as ComboboxPrimitive } from "@base-ui/react"; +import { Button } from "@repo/shadcn-ui/components/button"; import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, -} from "@repo/shadcn-ui/components/input-group" -import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react" +} from "@repo/shadcn-ui/components/input-group"; +import { cn } from "@repo/shadcn-ui/lib/utils"; +import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"; +import * as React from "react"; -const Combobox = ComboboxPrimitive.Root +const Combobox = ComboboxPrimitive.Root; function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { - return + return ; } -function ComboboxTrigger({ - className, - children, - ...props -}: ComboboxPrimitive.Trigger.Props) { +function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Trigger.Props) { return ( {children} - ) + ); } function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { return ( } className={cn(className)} + data-slot="combobox-clear" + render={} {...props} > - ) + ); } function ComboboxInput({ @@ -55,31 +50,28 @@ function ComboboxInput({ showClear = false, ...props }: ComboboxPrimitive.Input.Props & { - showTrigger?: boolean - showClear?: boolean + showTrigger?: boolean; + showClear?: boolean; }) { return ( - } - {...props} - /> + } {...props} /> {showTrigger && ( } size="icon-xs" variant="ghost" - render={} - data-slot="input-group-button" - className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent" - disabled={disabled} /> )} {showClear && } {children} - ) + ); } function ComboboxContent({ @@ -98,49 +90,48 @@ function ComboboxContent({ return ( - ) + ); } function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { return ( - ) + ); } -function ComboboxItem({ - className, - children, - ...props -}: ComboboxPrimitive.Item.Props) { +function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { return ( {children} @@ -152,79 +143,66 @@ function ComboboxItem({ - ) + ); } function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( - - ) + + ); } -function ComboboxLabel({ - className, - ...props -}: ComboboxPrimitive.GroupLabel.Props) { +function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { return ( - ) + ); } function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { - return ( - - ) + return ; } function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { return ( - ) + ); } -function ComboboxSeparator({ - className, - ...props -}: ComboboxPrimitive.Separator.Props) { +function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { return ( - ) + ); } function ComboboxChips({ className, ...props -}: React.ComponentPropsWithRef & - ComboboxPrimitive.Chips.Props) { +}: React.ComponentPropsWithRef & ComboboxPrimitive.Chips.Props) { return ( - ) + ); } function ComboboxChip({ @@ -233,63 +211,60 @@ function ComboboxChip({ showRemove = true, ...props }: ComboboxPrimitive.Chip.Props & { - showRemove?: boolean + showRemove?: boolean; }) { return ( {children} {showRemove && ( } className="-ml-1 opacity-50 hover:opacity-100" data-slot="combobox-chip-remove" + render={