Clientes y Facturas de cliente

This commit is contained in:
David Arranz 2025-10-28 18:52:30 +01:00
parent 88f5062c03
commit 8a3bc7ac45
47 changed files with 321 additions and 210 deletions

20
.vscode/tasks.json vendored
View File

@ -1,20 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "web:dev",
"type": "shell",
"command": "pnpm --filter web dev",
"isBackground": true,
"problemMatcher": {
"owner": "vite",
"pattern": [{ "regexp": "." }],
"background": {
"activeOnStart": true,
"beginsPattern": ".*Local:.*http://.*:5173/.*",
"endsPattern": ".*ready in .*"
}
}
}
]
}

13
apps/server/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
# server/Dockerfile
FROM node:22.13.1
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 5000
CMD ["npm", "start"]

View File

@ -43,7 +43,6 @@
"@erp/core": "workspace:*", "@erp/core": "workspace:*",
"@erp/customer-invoices": "workspace:*", "@erp/customer-invoices": "workspace:*",
"@erp/customers": "workspace:*", "@erp/customers": "workspace:*",
"@erp/verifactu": "workspace:*",
"@repo/rdx-logger": "workspace:*", "@repo/rdx-logger": "workspace:*",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"cls-rtracer": "^2.6.3", "cls-rtracer": "^2.6.3",
@ -79,9 +78,14 @@
"node": ">=22" "node": ">=22"
}, },
"tsup": { "tsup": {
"entry": ["src/index.ts"], "entry": [
"src/index.ts"
],
"outDir": "dist", "outDir": "dist",
"format": ["esm", "cjs"], "format": [
"esm",
"cjs"
],
"target": "ES2022", "target": "ES2022",
"sourcemap": true, "sourcemap": true,
"clean": true, "clean": true,

View File

@ -18,7 +18,7 @@ export function getService<T = any>(name: string): T {
if (!service) { if (!service) {
throw new Error(`❌ Servicio "${name}" no encontrado.`); throw new Error(`❌ Servicio "${name}" no encontrado.`);
} }
return service; return service as T;
} }
/** /**

View File

@ -1,6 +1,6 @@
import customerInvoicesAPIModule from "@erp/customer-invoices/api"; import customerInvoicesAPIModule from "@erp/customer-invoices/api";
import customersAPIModule from "@erp/customers/api"; import customersAPIModule from "@erp/customers/api";
import verifactuAPIModule from "@erp/verifactu/api"; //import verifactuAPIModule from "@erp/verifactu/api";
import { registerModule } from "./lib"; import { registerModule } from "./lib";
@ -8,5 +8,5 @@ export const registerModules = () => {
//registerModule(authAPIModule); //registerModule(authAPIModule);
registerModule(customersAPIModule); registerModule(customersAPIModule);
registerModule(customerInvoicesAPIModule); registerModule(customerInvoicesAPIModule);
registerModule(verifactuAPIModule); //registerModule(verifactuAPIModule);
}; };

14
apps/web/Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM node:22.13.1 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Servir con Vite (modo dev) o un servidor web
FROM node:22.13.1
WORKDIR /app
COPY --from=build /app .
EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host"]

View File

@ -10,7 +10,6 @@ export default defineConfig({
}, },
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
resolve: { resolve: {
dedupe: ["react", "react-dom"],
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),
}, },

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
"files": { "ignoreUnknown": false, "ignore": ["dist"] }, "files": { "ignoreUnknown": false, "includes": ["**", "!**/dist"] },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"useEditorconfig": true, "useEditorconfig": true,
@ -13,7 +13,7 @@
"attributePosition": "auto", "attributePosition": "auto",
"bracketSpacing": true "bracketSpacing": true
}, },
"organizeImports": { "enabled": true }, "assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
@ -38,7 +38,15 @@
"useImportType": "off", "useImportType": "off",
"noInferrableTypes": "off", "noInferrableTypes": "off",
"noNonNullAssertion": "info", "noNonNullAssertion": "info",
"noUselessElse": "off" "noUselessElse": "off",
"noParameterAssign": "error",
"useAsConstAssertion": "error",
"useDefaultParameterLast": "error",
"useEnumInitializers": "error",
"useSelfClosingElements": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error"
}, },
"a11y": { "a11y": {
"useSemanticElements": "info" "useSemanticElements": "info"

View File

@ -1,40 +1,31 @@
version: "3.8" name: factuges
services: services:
database: mariadb:
image: postgres:15 image: mariadb:latest
container_name: myapp_db container_name: mariadb
restart: always
environment: environment:
POSTGRES_USER: myuser MYSQL_ROOT_PASSWORD: rootpass
POSTGRES_PASSWORD: mypassword MYSQL_DATABASE: factuges_db
POSTGRES_DB: mydatabase MYSQL_USER: factuges_usr
ports: MYSQL_PASSWORD: factuges_pass
- "5432:5432"
volumes: volumes:
- db_data:/var/lib/postgresql/data - mariadb_data:/var/lib/mysql
ports:
- "3306:3306"
backend: phpmyadmin:
build: ./apps/server image: phpmyadmin/phpmyadmin
container_name: myapp_backend container_name: phpmyadmin
restart: always
environment:
PMA_HOST: mariadb
MYSQL_ROOT_PASSWORD: rootpass
ports: ports:
- "5000:5000" - "8080:80"
depends_on: depends_on:
- database - mariadb
env_file: ./apps/server/.env
volumes:
- ./apps/server:/app
- /app/node_modules
frontend:
build: ./apps/client
container_name: myapp_frontend
ports:
- "3000:3000"
depends_on:
- backend
env_file: ./apps/client/.env
volumes:
- ./apps/client:/app
- /app/node_modules
volumes: volumes:
db_data: mariadb_data:

View File

@ -19,7 +19,6 @@
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3", "@types/react-dom": "^19.1.3",
"@types/react-i18next": "^8.1.0",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
"dependencies": { "dependencies": {

View File

@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"exports": { "exports": {
".": "./src/common/index.ts", ".": "./src/common/index.ts",
"./common": "./src/common/index.ts",
"./api": "./src/api/index.ts", "./api": "./src/api/index.ts",
"./client": "./src/web/manifest.ts", "./client": "./src/web/manifest.ts",
"./globals.css": "./src/web/globals.css", "./globals.css": "./src/web/globals.css",
@ -15,7 +16,6 @@
}, },
"devDependencies": { "devDependencies": {
"@hookform/devtools": "^4.4.0", "@hookform/devtools": "^4.4.0",
"@types/axios": "^0.14.4",
"@types/dinero.js": "^1.9.4", "@types/dinero.js": "^1.9.4",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
@ -25,6 +25,7 @@
"@hookform/resolvers": "^5.0.1", "@hookform/resolvers": "^5.0.1",
"@repo/rdx-criteria": "workspace:*", "@repo/rdx-criteria": "workspace:*",
"@repo/rdx-ddd": "workspace:*", "@repo/rdx-ddd": "workspace:*",
"@repo/rdx-logger": "workspace:*",
"@repo/rdx-ui": "workspace:*", "@repo/rdx-ui": "workspace:*",
"@repo/rdx-utils": "workspace:*", "@repo/rdx-utils": "workspace:*",
"@repo/shadcn-ui": "workspace:*", "@repo/shadcn-ui": "workspace:*",

View File

@ -1 +1,2 @@
export * from "./logger";
export * from "./sequelize-func"; export * from "./sequelize-func";

View File

@ -0,0 +1,3 @@
import { loggerSingleton } from "@repo/rdx-logger";
export const logger = loggerSingleton();

View File

@ -1,35 +1,26 @@
import { loggerSingleton } from "@repo/rdx-logger";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { ILogger } from "../../logger";
import { ITransactionManager } from "./transaction-manager.interface"; import { ITransactionManager } from "./transaction-manager.interface";
const logger = loggerSingleton();
export abstract class TransactionManager implements ITransactionManager { export abstract class TransactionManager implements ITransactionManager {
protected _transaction: unknown | null = null; protected _transaction: unknown | null = null;
protected _isCompleted = false; protected _isCompleted = false;
protected readonly logger!: ILogger;
constructor(logger?: ILogger) {
// Si no hay logger, usa console adaptado
this.logger = logger ?? {
info: (msg, meta) => console.info(msg, meta),
warn: (msg, meta) => console.warn(msg, meta),
error: (msg, err) => console.error(msg, err),
debug: (msg, meta) => console.debug(msg, meta),
};
}
/** /**
* 🔹 Inicia una transacción si no hay una activa * 🔹 Inicia una transacción si no hay una activa
*/ */
async start(): Promise<void> { async start(): Promise<void> {
if (this._transaction) { if (this._transaction) {
this.logger.error("❌ Transaction already started. Nested transactions are not allowed.", { logger.error("❌ Transaction already started. Nested transactions are not allowed.", {
label: "TransactionManager.start", label: "TransactionManager.start",
}); });
throw new Error("A transaction is already active. Nested transactions are not allowed."); throw new Error("A transaction is already active. Nested transactions are not allowed.");
} }
this._transaction = await this._startTransaction(); this._transaction = await this._startTransaction();
this._isCompleted = false; this._isCompleted = false;
this.logger.debug("Transaction started", { logger.debug("Transaction started", {
label: "TransactionManager.start", label: "TransactionManager.start",
}); });
} }
@ -39,13 +30,13 @@ export abstract class TransactionManager implements ITransactionManager {
*/ */
getTransaction(): any { getTransaction(): any {
if (!this._transaction) { if (!this._transaction) {
this.logger.error("❌ No active transaction. Call start() first.", { logger.error("❌ No active transaction. Call start() first.", {
label: "TransactionManager.getTransaction", label: "TransactionManager.getTransaction",
}); });
throw new Error("No active transaction. Call start() first."); throw new Error("No active transaction. Call start() first.");
} }
if (this._isCompleted) { if (this._isCompleted) {
this.logger.error("❌ Transaction already completed."); logger.error("❌ Transaction already completed.");
throw new Error("Transaction already completed."); throw new Error("Transaction already completed.");
} }
@ -57,7 +48,7 @@ export abstract class TransactionManager implements ITransactionManager {
*/ */
async complete<T>(work: (transaction: unknown) => Promise<T>): Promise<T> { async complete<T>(work: (transaction: unknown) => Promise<T>): Promise<T> {
if (this._transaction) { if (this._transaction) {
this.logger.error( logger.error(
"❌ Cannot start a new transaction inside another. Nested transactions are not allowed.", "❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
{ label: "TransactionManager.complete" } { label: "TransactionManager.complete" }
); );
@ -77,7 +68,7 @@ export abstract class TransactionManager implements ITransactionManager {
} catch (err) { } catch (err) {
await this.rollback(); await this.rollback();
const error = err as Error; const error = err as Error;
this.logger.error(`❌ Transaction rolled back due to error: ${error.message}`, { logger.error(`❌ Transaction rolled back due to error: ${error.message}`, {
//stack: error.stack, //stack: error.stack,
label: "TransactionManager.start", label: "TransactionManager.start",
}); });
@ -89,27 +80,27 @@ export abstract class TransactionManager implements ITransactionManager {
* 🔹 Métodos abstractos para manejar transacciones * 🔹 Métodos abstractos para manejar transacciones
*/ */
protected abstract _startTransaction(): Promise<any>; protected abstract _startTransaction(): Promise<any>;
protected abstract _commitTransaction(): Promise<void>; protected abstract _commitTransaction(transaction: unknown): Promise<void>;
protected abstract _rollbackTransaction(): Promise<void>; protected abstract _rollbackTransaction(): Promise<void>;
async commit(): Promise<void> { async commit(): Promise<void> {
if (!this._transaction) { if (!this._transaction) {
this.logger.error("❌ No transaction to commit.", { label: "TransactionManager.commit" }); logger.error("❌ No transaction to commit.", { label: "TransactionManager.commit" });
throw new Error("No transaction to commit."); throw new Error("No transaction to commit.");
} }
if (this._isCompleted) { if (this._isCompleted) {
this.logger.error("❌ Transaction already completed. Cannot commit again.", { logger.error("❌ Transaction already completed. Cannot commit again.", {
label: "TransactionManager.commit", label: "TransactionManager.commit",
}); });
throw new Error("Transaction already completed."); throw new Error("Transaction already completed.");
} }
try { try {
await this._commitTransaction(); await this._commitTransaction(this._transaction);
this.logger.info("Transaction committed.", { label: "TransactionManager.commit" }); logger.info("Transaction committed.", { label: "TransactionManager.commit" });
} catch (err) { } catch (err) {
const error = err as Error; const error = err as Error;
this.logger.error(`❌ Error during commit: ${error.message}`, { logger.error(`❌ Error during commit: ${error.message}`, {
stack: error.stack, stack: error.stack,
label: "TransactionManager.commit", label: "TransactionManager.commit",
}); });
@ -123,11 +114,11 @@ export abstract class TransactionManager implements ITransactionManager {
async rollback(): Promise<void> { async rollback(): Promise<void> {
if (!this._transaction) { if (!this._transaction) {
this.logger.error("❌ No transaction to rollback.", { label: "TransactionManager.rollback" }); logger.error("❌ No transaction to rollback.", { label: "TransactionManager.rollback" });
throw new Error("No transaction to rollback."); throw new Error("No transaction to rollback.");
} }
if (this._isCompleted) { if (this._isCompleted) {
this.logger.error("❌ Transaction already completed. Cannot rollback again.", { logger.error("❌ Transaction already completed. Cannot rollback again.", {
label: "TransactionManager.rollback", label: "TransactionManager.rollback",
}); });
throw new Error("Transaction already completed."); throw new Error("Transaction already completed.");
@ -135,10 +126,10 @@ export abstract class TransactionManager implements ITransactionManager {
try { try {
await this._rollbackTransaction(); await this._rollbackTransaction();
this.logger.info("Transaction rolled back."); logger.info("Transaction rolled back.");
} catch (err) { } catch (err) {
const error = err as Error; const error = err as Error;
this.logger.error(`❌ Error during rollback: ${error.message}`, { logger.error(`❌ Error during rollback: ${error.message}`, {
stack: error.stack, stack: error.stack,
label: "TransactionManager.rollback", label: "TransactionManager.rollback",
}); });

View File

@ -1,5 +1,6 @@
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize"; import { Sequelize, Transaction } from "sequelize";
import { logger } from "../../helpers";
import { TransactionManager } from "../database"; import { TransactionManager } from "../database";
import { InfrastructureError, InfrastructureUnavailableError } from "../errors"; import { InfrastructureError, InfrastructureUnavailableError } from "../errors";
@ -14,9 +15,9 @@ export class SequelizeTransactionManager extends TransactionManager {
}); });
} }
protected async _commitTransaction(): Promise<void> { protected async _commitTransaction(transaction: Transaction): Promise<void> {
if (this._transaction) { if (transaction) {
await this._transaction.commit(); await transaction.commit();
} }
} }
@ -52,7 +53,7 @@ export class SequelizeTransactionManager extends TransactionManager {
// Evita transacciones anidadas según la política del TransactionManager base // Evita transacciones anidadas según la política del TransactionManager base
if (this._transaction) { if (this._transaction) {
this.logger.error( logger.error(
"❌ Cannot start a new transaction inside another. Nested transactions are not allowed.", "❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
{ label: "SequelizeTransactionManager.complete" } { label: "SequelizeTransactionManager.complete" }
); );
@ -74,7 +75,7 @@ export class SequelizeTransactionManager extends TransactionManager {
return result as T; return result as T;
} catch (err) { } catch (err) {
const error = err as Error; const error = err as Error;
this.logger.error(`❌ Transaction rolled back due to error: ${error.message}`, { logger.error(`❌ Transaction rolled back due to error: ${error.message}`, {
//stack: error.stack, //stack: error.stack,
label: "SequelizeTransactionManager.complete", label: "SequelizeTransactionManager.complete",
}); });

View File

@ -45,7 +45,7 @@ export function normalizeCriteriaDTO(criteria: CriteriaDTO = {}) {
// Para mantener un orden estable de filtros // Para mantener un orden estable de filtros
const stableFilters = [...filters].sort( const stableFilters = [...filters].sort(
(a, b) => a.field.localeCompare(b.field) || a.op.localeCompare(b.op) (a, b) => a.field.localeCompare(b.field) || a.operator.localeCompare(b.operator)
); );
return { pageNumber, pageSize, q, filters: stableFilters, orderBy, order }; return { pageNumber, pageSize, q, filters: stableFilters, orderBy, order };

View File

@ -1,5 +1,5 @@
import type { MoneyDTO } from "@erp/core/common"; import Dinero, { Currency } from "dinero.js";
import Dinero from "dinero.js"; import { MoneyDTO } from "../dto";
type DineroPlain = { amount: number; precision: number; currency: string }; type DineroPlain = { amount: number; precision: number; currency: string };
@ -93,7 +93,7 @@ function dineroFromDTO(dto: MoneyDTO, fallbackCurrency = "EUR"): Dinero.Dinero {
return Dinero({ return Dinero({
amount: Number.parseInt(n.value, 10), amount: Number.parseInt(n.value, 10),
precision: Number.parseInt(n.scale, 10), precision: Number.parseInt(n.scale, 10),
currency: n.currency_code as string, currency: n.currency_code as Currency,
}); });
} }

View File

@ -5,6 +5,7 @@
"types": "src/index.ts", "types": "src/index.ts",
"exports": { "exports": {
".": "./src/common/index.ts", ".": "./src/common/index.ts",
"./common": "./src/common/index.ts",
"./api": "./src/api/index.ts", "./api": "./src/api/index.ts",
"./client": "./src/web/manifest.ts", "./client": "./src/web/manifest.ts",
"./globals.css": "./src/web/globals.css" "./globals.css": "./src/web/globals.css"
@ -31,7 +32,6 @@
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3", "@types/react-dom": "^19.1.3",
"@types/react-i18next": "^8.1.0",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },

View File

@ -3,4 +3,4 @@ export * from "./format-money-dto";
export * from "./format-payment_method-dto"; export * from "./format-payment_method-dto";
export * from "./format-percentage-dto"; export * from "./format-percentage-dto";
export * from "./format-quantity-dto"; export * from "./format-quantity-dto";
export * from "./map-dto-to-customer-invoice-props"; //export * from "./map-dto-to-customer-invoice-props";

View File

@ -1,8 +1,8 @@
import { import {
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError, extractOrPushError,
maybeFromNullableVO, maybeFromNullableVO,
ValidationErrorCollection,
ValidationErrorDetail,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { CreateCustomerInvoiceRequestDTO } from "../../../common"; import { CreateCustomerInvoiceRequestDTO } from "../../../common";

View File

@ -1,9 +1,9 @@
import { Presenter } from "@erp/core/api"; import { Presenter } from "@erp/core/api";
import { CustomerInvoiceListDTO } from "@erp/customer-invoices/api/infrastructure";
import { Criteria } from "@repo/rdx-criteria/server"; import { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd"; import { toEmptyString } from "@repo/rdx-ddd";
import { ArrayElement, Collection } from "@repo/rdx-utils"; import { ArrayElement, Collection } from "@repo/rdx-utils";
import { ListCustomerInvoicesResponseDTO } from "../../../../common/dto"; import { ListCustomerInvoicesResponseDTO } from "../../../../common/dto";
import { CustomerInvoiceListDTO } from "../../../infrastructure";
export class ListCustomerInvoicesPresenter extends Presenter { export class ListCustomerInvoicesPresenter extends Presenter {
protected _mapInvoice(invoice: CustomerInvoiceListDTO) { protected _mapInvoice(invoice: CustomerInvoiceListDTO) {
@ -24,10 +24,7 @@ export class ListCustomerInvoicesPresenter extends Presenter {
reference: toEmptyString(invoice.reference, (value) => value.toString()), reference: toEmptyString(invoice.reference, (value) => value.toString()),
description: toEmptyString(invoice.description, (value) => value.toString()), description: toEmptyString(invoice.description, (value) => value.toString()),
recipient: { recipient: recipientDTO,
customer_id: invoice.customerId.toString(),
...recipientDTO,
},
language_code: invoice.languageCode.code, language_code: invoice.languageCode.code,
currency_code: invoice.currencyCode.code, currency_code: invoice.currencyCode.code,

View File

@ -30,7 +30,7 @@ export class CreateCustomerInvoiceUseCase {
// 1) Mapear DTO → props de dominio // 1) Mapear DTO → props de dominio
const dtoMapper = new CreateCustomerInvoicePropsMapper({ taxCatalog: this.taxCatalog }); const dtoMapper = new CreateCustomerInvoicePropsMapper({ taxCatalog: this.taxCatalog });
const dtoResult = dtoMapper.map(dto); const dtoResult = dtoMapper.map(dto, companyId);
if (dtoResult.isFailure) { if (dtoResult.isFailure) {
return Result.fail(dtoResult.error); return Result.fail(dtoResult.error);
} }

View File

@ -1,19 +1,19 @@
import { JsonTaxCatalogProvider } from "@erp/core"; import { JsonTaxCatalogProvider } from "@erp/core";
import { Tax, Taxes } from "@erp/core/api"; import { Tax } from "@erp/core/api";
import { import {
CurrencyCode, CurrencyCode,
DomainError, DomainError,
extractOrPushError,
LanguageCode, LanguageCode,
maybeFromNullableVO,
Percentage, Percentage,
TextValue, TextValue,
UniqueID, UniqueID,
UtcDate, UtcDate,
ValidationErrorCollection, ValidationErrorCollection,
ValidationErrorDetail, ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Maybe, Result } from "@repo/rdx-utils";
import { import {
CreateCustomerInvoiceItemRequestDTO, CreateCustomerInvoiceItemRequestDTO,
CreateCustomerInvoiceRequestDTO, CreateCustomerInvoiceRequestDTO,
@ -27,9 +27,12 @@ import {
CustomerInvoiceProps, CustomerInvoiceProps,
CustomerInvoiceSerie, CustomerInvoiceSerie,
CustomerInvoiceStatus, CustomerInvoiceStatus,
InvoicePaymentMethod,
InvoiceRecipient,
ItemAmount, ItemAmount,
ItemDiscount, ItemDiscount,
ItemQuantity, ItemQuantity,
ItemTaxes,
} from "../../../domain"; } from "../../../domain";
/** /**
@ -53,24 +56,24 @@ export class CreateCustomerInvoicePropsMapper {
this.errors = []; this.errors = [];
} }
public map(dto: CreateCustomerInvoiceRequestDTO) { public map(dto: CreateCustomerInvoiceRequestDTO, companyId: UniqueID) {
try { try {
this.errors = []; this.errors = [];
const defaultStatus = CustomerInvoiceStatus.createDraft(); const defaultStatus = CustomerInvoiceStatus.createDraft();
const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors); const invoiceId = extractOrPushError(UniqueID.create(dto.id), "id", this.errors);
const companyId = extractOrPushError(
UniqueID.create(dto.company_id), const isProforma = true;
"company_id",
this.errors
);
const customerId = extractOrPushError( const customerId = extractOrPushError(
UniqueID.create(dto.customer_id), UniqueID.create(dto.customer_id),
"customer_id", "customer_id",
this.errors this.errors
); );
const recipient = Maybe.none<InvoiceRecipient>();
const invoiceNumber = extractOrPushError( const invoiceNumber = extractOrPushError(
maybeFromNullableVO(dto.invoice_number, (value) => CustomerInvoiceNumber.create(value)), maybeFromNullableVO(dto.invoice_number, (value) => CustomerInvoiceNumber.create(value)),
"invoice_number", "invoice_number",
@ -95,6 +98,18 @@ export class CreateCustomerInvoicePropsMapper {
this.errors this.errors
); );
const reference = extractOrPushError(
maybeFromNullableVO(dto.reference, (value) => Result.ok(String(value))),
"reference",
this.errors
);
const description = extractOrPushError(
maybeFromNullableVO(dto.reference, (value) => Result.ok(String(value))),
"description",
this.errors
);
const notes = extractOrPushError( const notes = extractOrPushError(
maybeFromNullableVO(dto.notes, (value) => TextValue.create(value)), maybeFromNullableVO(dto.notes, (value) => TextValue.create(value)),
"notes", "notes",
@ -113,6 +128,14 @@ export class CreateCustomerInvoicePropsMapper {
this.errors this.errors
); );
const paymentMethod = extractOrPushError(
maybeFromNullableVO(dto.payment_method, (value) =>
InvoicePaymentMethod.create({ paymentDescription: value })
),
"payment_method",
this.errors
);
const discountPercentage = extractOrPushError( const discountPercentage = extractOrPushError(
Percentage.create({ Percentage.create({
value: Number(dto.discount_percentage.value), value: Number(dto.discount_percentage.value),
@ -131,26 +154,31 @@ export class CreateCustomerInvoicePropsMapper {
} }
const invoiceProps: CustomerInvoiceProps = { const invoiceProps: CustomerInvoiceProps = {
companyId: companyId!, companyId,
isProforma,
status: defaultStatus!, status: defaultStatus!,
invoiceNumber: invoiceNumber!,
invoiceDate: invoiceDate!,
operationDate: operationDate!,
series: series!, series: series!,
invoiceNumber: invoiceNumber!,
notes: notes!, invoiceDate: invoiceDate!,
operationDate: operationDate!,
customerId: customerId!, customerId: customerId!,
recipient: recipient!,
reference: reference!,
description: description!,
notes: notes!,
languageCode: this.languageCode!, languageCode: this.languageCode!,
currencyCode: this.currencyCode!, currencyCode: this.currencyCode!,
discountPercentage: discountPercentage!,
taxes: Taxes.create([]),
items: items, items: items,
paymentMethod: paymentMethod!,
discountPercentage: discountPercentage!,
}; };
return Result.ok({ id: invoiceId!, props: invoiceProps }); return Result.ok({ id: invoiceId!, props: invoiceProps });
@ -202,7 +230,7 @@ export class CreateCustomerInvoicePropsMapper {
quantity: quantity!, quantity: quantity!,
unitAmount: unitAmount!, unitAmount: unitAmount!,
discountPercentage: discountPercentage!, discountPercentage: discountPercentage!,
taxes, taxes: taxes,
}; };
const itemResult = CustomerInvoiceItem.create(itemProps); const itemResult = CustomerInvoiceItem.create(itemProps);
@ -219,7 +247,7 @@ export class CreateCustomerInvoicePropsMapper {
} }
private mapTaxes(item: CreateCustomerInvoiceItemRequestDTO, itemIndex: number) { private mapTaxes(item: CreateCustomerInvoiceItemRequestDTO, itemIndex: number) {
const taxes = Taxes.create([]); const taxes = ItemTaxes.create([]);
item.taxes.split(",").every((tax_code, taxIndex) => { item.taxes.split(",").every((tax_code, taxIndex) => {
const taxResult = Tax.createFromCode(tax_code, this.taxCatalog); const taxResult = Tax.createFromCode(tax_code, this.taxCatalog);

View File

@ -1,7 +1,8 @@
import { EntityNotFoundError, ITransactionManager } from "@erp/core/api"; import { EntityNotFoundError, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { CustomerInvoiceApplicationService, CustomerInvoiceNumber } from "../../domain"; import { CustomerInvoiceNumber } from "../../domain";
import { CustomerInvoiceApplicationService } from "../customer-invoice-application.service";
import { StatusInvoiceIsApprovedSpecification } from "../specs"; import { StatusInvoiceIsApprovedSpecification } from "../specs";
type IssueCustomerInvoiceUseCaseInput = { type IssueCustomerInvoiceUseCaseInput = {
@ -56,12 +57,14 @@ export class IssueCustomerInvoiceUseCase {
const issuedInvoiceResult = invoiceProforma.issueInvoice(newInvoiceNumber); const issuedInvoiceResult = invoiceProforma.issueInvoice(newInvoiceNumber);
if (issuedInvoiceResult.isFailure) { if (issuedInvoiceResult.isFailure) {
return Result.fail(new EntityNotFoundError("Customer invoice", "id", error)); return Result.fail(
new EntityNotFoundError("Customer invoice", "id", issuedInvoiceResult.error)
);
} }
const issuedInvoice = issuedInvoiceResult.data; const issuedInvoice = issuedInvoiceResult.data;
this.service.saveInvoice(issuedInvoice, transaction); this.service.updateInvoiceInCompany(companyId, issuedInvoice, transaction);
//return await this.service.IssueInvoiceByIdInCompany(companyId, invoiceId, transaction); //return await this.service.IssueInvoiceByIdInCompany(companyId, invoiceId, transaction);
} catch (error: unknown) { } catch (error: unknown) {

View File

@ -1,7 +1,7 @@
import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd"; import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { CustomerInvoiceApplicationService } from "../../../domain"; import { CustomerInvoiceApplicationService } from "../../customer-invoice-application.service";
import { CustomerInvoiceReportPDFPresenter } from "./reporter"; import { CustomerInvoiceReportPDFPresenter } from "./reporter";
type ReportCustomerInvoiceUseCaseInput = { type ReportCustomerInvoiceUseCaseInput = {

View File

@ -1,7 +1,7 @@
export * from "./create-customer-invoice.controller"; export * from "./create-customer-invoice.controller";
export * from "./delete-customer-invoice.controller"; //export * from "./delete-customer-invoice.controller";
export * from "./get-customer-invoice.controller"; export * from "./get-customer-invoice.controller";
export * from "./issue-customer-invoice.controller"; //export * from "./issue-customer-invoice.controller";
export * from "./list-customer-invoices.controller"; export * from "./list-customer-invoices.controller";
export * from "./report-customer-invoice.controller"; export * from "./report-customer-invoice.controller";
export * from "./update-customer-invoice.controller"; export * from "./update-customer-invoice.controller";

View File

@ -1,18 +1,18 @@
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api"; import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { import {
CurrencyCode, CurrencyCode,
extractOrPushError,
LanguageCode, LanguageCode,
maybeFromNullableVO,
Percentage, Percentage,
TextValue, TextValue,
toNullable,
UniqueID, UniqueID,
UtcDate, UtcDate,
ValidationErrorCollection, ValidationErrorCollection,
ValidationErrorDetail, ValidationErrorDetail,
extractOrPushError,
maybeFromNullableVO,
toNullable,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Collection, Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils"; import { Collection, isNullishOrEmpty, Maybe, Result } from "@repo/rdx-utils";
import { import {
CustomerInvoice, CustomerInvoice,
CustomerInvoiceItems, CustomerInvoiceItems,
@ -315,8 +315,7 @@ export class CustomerInvoiceDomainMapper
const items = itemsResult.data; const items = itemsResult.data;
// 2) Taxes // 2) Taxes
const taxesResult = this._taxesMapper.mapToPersistenceArray(new Collection(source.getTaxes()), {
const taxesResult = this._taxesMapper.mapToPersistenceArray(new Collection(source.taxes), {
errors, errors,
parent: source, parent: source,
...params, ...params,
@ -407,7 +406,7 @@ export class CustomerInvoiceDomainMapper
}; };
const hasRecipient = source.hasRecipient; const hasRecipient = source.hasRecipient;
const recipient = source.recipient!.getOrUndefined(); const recipient = source.recipient?.getOrUndefined();
if (!source.isProforma && !hasRecipient) { if (!source.isProforma && !hasRecipient) {
errors.push({ errors.push({
@ -417,25 +416,25 @@ export class CustomerInvoiceDomainMapper
} }
const recipientValues = { const recipientValues = {
customer_tin: !source.isProforma ? recipient!.tin.toPrimitive() : null, customer_tin: !source.isProforma ? recipient?.tin.toPrimitive() : null,
customer_name: !source.isProforma ? recipient!.name.toPrimitive() : null, customer_name: !source.isProforma ? recipient?.name.toPrimitive() : null,
customer_street: !source.isProforma customer_street: !source.isProforma
? toNullable(recipient!.street, (v) => v.toPrimitive()) ? toNullable(recipient?.street, (v) => v.toPrimitive())
: null, : null,
customer_street2: !source.isProforma customer_street2: !source.isProforma
? toNullable(recipient!.street2, (v) => v.toPrimitive()) ? toNullable(recipient?.street2, (v) => v.toPrimitive())
: null, : null,
customer_city: !source.isProforma customer_city: !source.isProforma
? toNullable(recipient!.city, (v) => v.toPrimitive()) ? toNullable(recipient?.city, (v) => v.toPrimitive())
: null, : null,
customer_province: !source.isProforma customer_province: !source.isProforma
? toNullable(recipient!.province, (v) => v.toPrimitive()) ? toNullable(recipient?.province, (v) => v.toPrimitive())
: null, : null,
customer_postal_code: !source.isProforma customer_postal_code: !source.isProforma
? toNullable(recipient!.postalCode, (v) => v.toPrimitive()) ? toNullable(recipient?.postalCode, (v) => v.toPrimitive())
: null, : null,
customer_country: !source.isProforma customer_country: !source.isProforma
? toNullable(recipient!.country, (v) => v.toPrimitive()) ? toNullable(recipient?.country, (v) => v.toPrimitive())
: null, : null,
}; };

View File

@ -1,4 +1,3 @@
import { JsonTaxCatalogProvider } from "@erp/core";
import { import {
ISequelizeDomainMapper, ISequelizeDomainMapper,
MapperParamsType, MapperParamsType,
@ -6,6 +5,8 @@ import {
Tax, Tax,
} from "@erp/core/api"; } from "@erp/core/api";
import { JsonTaxCatalogProvider } from "@erp/core";
import { import {
UniqueID, UniqueID,
ValidationErrorCollection, ValidationErrorCollection,

View File

@ -1,18 +1,14 @@
import { ISequelizeQueryMapper, MapperParamsType, SequelizeQueryMapper } from "@erp/core/api"; import { ISequelizeQueryMapper, MapperParamsType, SequelizeQueryMapper } from "@erp/core/api";
import {
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError,
} from "@repo/rdx-ddd";
import { import {
CurrencyCode, CurrencyCode,
extractOrPushError,
LanguageCode, LanguageCode,
maybeFromNullableVO,
Percentage, Percentage,
UniqueID, UniqueID,
UtcDate, UtcDate,
maybeFromNullableVO, ValidationErrorCollection,
ValidationErrorDetail,
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils"; import { Maybe, Result } from "@repo/rdx-utils";

View File

@ -22,6 +22,7 @@ export const CreateCustomerInvoiceRequestSchema = z.object({
customer_id: z.uuid(), customer_id: z.uuid(),
reference: z.string().default(""),
notes: z.string().default(""), notes: z.string().default(""),
language_code: z.string().toLowerCase().default("es"), language_code: z.string().toLowerCase().default("es"),
@ -32,6 +33,8 @@ export const CreateCustomerInvoiceRequestSchema = z.object({
scale: "2", scale: "2",
}), }),
payment_method: z.string().default(""),
items: z.array(CreateCustomerInvoiceItemRequestSchema).default([]), items: z.array(CreateCustomerInvoiceItemRequestSchema).default([]),
}); });

View File

@ -5,6 +5,7 @@
"types": "src/index.ts", "types": "src/index.ts",
"exports": { "exports": {
".": "./src/common/index.ts", ".": "./src/common/index.ts",
"./common": "./src/common/index.ts",
"./api": "./src/api/index.ts", "./api": "./src/api/index.ts",
"./client": "./src/web/manifest.ts", "./client": "./src/web/manifest.ts",
"./globals.css": "./src/web/globals.css", "./globals.css": "./src/web/globals.css",

View File

@ -1,10 +1,10 @@
import { CriteriaDTO } from "@erp/core"; import { CriteriaDTO } from "@erp/core";
import { Presenter } from "@erp/core/api"; import { Presenter } from "@erp/core/api";
import { CustomerListDTO } from "@erp/customer-invoices/api/infrastructure";
import { Criteria } from "@repo/rdx-criteria/server"; import { Criteria } from "@repo/rdx-criteria/server";
import { toEmptyString } from "@repo/rdx-ddd"; import { toEmptyString } from "@repo/rdx-ddd";
import { Collection } from "@repo/rdx-utils"; import { Collection } from "@repo/rdx-utils";
import { ListCustomersResponseDTO } from "../../../../common/dto"; import { ListCustomersResponseDTO } from "../../../../common/dto";
import { CustomerListDTO } from "../../../infrastructure/mappers";
export class ListCustomersPresenter extends Presenter { export class ListCustomersPresenter extends Presenter {
protected _mapCustomer(customer: CustomerListDTO) { protected _mapCustomer(customer: CustomerListDTO) {

View File

@ -1,4 +1,5 @@
import { CompositeSpecification, UniqueID } from "@repo/rdx-ddd"; import { CompositeSpecification, UniqueID } from "@repo/rdx-ddd";
import { Transaction } from "sequelize";
import { CustomerApplicationService } from "../../application"; import { CustomerApplicationService } from "../../application";
import { logger } from "../../helpers"; import { logger } from "../../helpers";
@ -6,7 +7,7 @@ export class CustomerNotExistsInCompanySpecification extends CompositeSpecificat
constructor( constructor(
private readonly service: CustomerApplicationService, private readonly service: CustomerApplicationService,
private readonly companyId: UniqueID, private readonly companyId: UniqueID,
private readonly transaction?: unknown private readonly transaction?: Transaction
) { ) {
super(); super();
} }

View File

@ -1,5 +1,3 @@
import { ValidationErrorCollection, ValidationErrorDetail } from "@repo/rdx-ddd";
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api"; import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { import {
City, City,
@ -18,6 +16,8 @@ import {
TextValue, TextValue,
URLAddress, URLAddress,
UniqueID, UniqueID,
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError, extractOrPushError,
maybeFromNullableVO, maybeFromNullableVO,
toNullable, toNullable,

View File

@ -28,10 +28,6 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": [ "include": ["src"],
"src",
"../core/src/web/components/form/form-debug.tsx",
"../customer-invoices/src/web/components/editor/invoice-basic-info-fields.tsx"
],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

View File

@ -12,7 +12,9 @@
"express": "^4.18.2", "express": "^4.18.2",
"zod": "^4.1.11" "zod": "^4.1.11"
}, },
"devDependencies": { "@types/express": "^4.17.21" }, "devDependencies": {
"@types/express": "^4.17.21"
},
"dependencies": { "dependencies": {
"@erp/auth": "workspace:*", "@erp/auth": "workspace:*",
"@erp/core": "workspace:*", "@erp/core": "workspace:*",

View File

@ -1,5 +1,5 @@
import { Presenter } from "@erp/core/api"; import { Presenter } from "@erp/core/api";
import { GetVerifactuRecordByIdResponseDTO } from "@erp/verifactu-records/common"; import { GetVerifactuRecordByIdResponseDTO } from "../../../../common";
import { VerifactuRecord } from "../../../domain"; import { VerifactuRecord } from "../../../domain";
export class VerifactuRecordFullPresenter extends Presenter< export class VerifactuRecordFullPresenter extends Presenter<

View File

@ -54,7 +54,7 @@ export class SendInvoiceUseCase {
], ],
importe_total: "352.00", importe_total: "352.00",
}; };
const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoice, transaction); const invoiceOrError = await this.service.sendInvoiceToVerifactu(invoiceId, transaction);
if (invoiceOrError.isFailure) { if (invoiceOrError.isFailure) {
return Result.fail(invoiceOrError.error); return Result.fail(invoiceOrError.error);
} }

View File

@ -1,7 +1,11 @@
{ {
"name": "uecko-erp-2025", "name": "uecko-erp-2025",
"private": true, "private": true,
"workspaces": ["apps/*", "modules/*", "packages/*"], "workspaces": [
"apps/*",
"modules/*",
"packages/*"
],
"scripts": { "scripts": {
"build": "turbo build", "build": "turbo build",
"dev": "turbo dev", "dev": "turbo dev",
@ -15,7 +19,7 @@
"clean": "find . -name 'node_modules' -type d -prune -print -exec rm -rf '{}' \\;" "clean": "find . -name 'node_modules' -type d -prune -print -exec rm -rf '{}' \\;"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "2.0.6",
"@repo/typescript-config": "workspace:*", "@repo/typescript-config": "workspace:*",
"change-case": "^5.4.4", "change-case": "^5.4.4",
"inquirer": "^12.5.2", "inquirer": "^12.5.2",

View File

@ -10,9 +10,6 @@ export class DomainValidationError extends DomainError {
/** Discriminante para routing/telemetría */ /** Discriminante para routing/telemetría */
public readonly kind = "VALIDATION" as const; public readonly kind = "VALIDATION" as const;
/** Regla/identificador de error de validación (ej. INVALID_EMAIL) */
public readonly code: string;
/** Campo afectado (path) */ /** Campo afectado (path) */
public readonly field: string; public readonly field: string;
@ -20,13 +17,13 @@ export class DomainValidationError extends DomainError {
public readonly detail: string; public readonly detail: string;
constructor( constructor(
code: string, _code: string,
field: string, field: string,
detail: string, detail: string,
options?: ErrorOptions & { metadata?: Record<string, unknown> } options?: ErrorOptions & { metadata?: Record<string, unknown> }
) { ) {
// Mensaje humano compacto y útil para logs // Mensaje humano compacto y útil para logs
super(`[${field}] ${detail}`, code, { super(`[${field}] ${detail}`, {
...options, ...options,
// Aseguramos metadatos ricos y estables // Aseguramos metadatos ricos y estables
metadata: { metadata: {
@ -38,7 +35,7 @@ export class DomainValidationError extends DomainError {
}); });
this.name = "DomainValidationError"; this.name = "DomainValidationError";
this.code = code; //this.code = code;
this.field = field; this.field = field;
this.detail = detail; this.detail = detail;

View File

@ -15,7 +15,7 @@
* *
*/ */
import { DomainError } from "./domain-error"; import { BaseError } from "./base-error";
import { DomainValidationError } from "./domain-validation-error"; import { DomainValidationError } from "./domain-validation-error";
export interface ValidationErrorDetail { export interface ValidationErrorDetail {
@ -38,7 +38,9 @@ export interface ValidationErrorDetail {
* ]; * ];
* throw new ValidationErrorCollection(message, errors); * throw new ValidationErrorCollection(message, errors);
*/ */
export class ValidationErrorCollection extends DomainError { export class ValidationErrorCollection extends BaseError<"domain"> {
public readonly layer = "domain" as const;
public readonly kind = "VALIDATION" as const; public readonly kind = "VALIDATION" as const;
public readonly code = "MULTIPLE_VALIDATION_ERRORS" as const; public readonly code = "MULTIPLE_VALIDATION_ERRORS" as const;
public readonly details: ValidationErrorDetail[]; public readonly details: ValidationErrorDetail[];
@ -48,7 +50,7 @@ export class ValidationErrorCollection extends DomainError {
details: ValidationErrorDetail[], details: ValidationErrorDetail[],
options?: ErrorOptions & { metadata?: Record<string, unknown> } options?: ErrorOptions & { metadata?: Record<string, unknown> }
) { ) {
super(message, "MULTIPLE_VALIDATION_ERRORS", { super("DomainError", message, "MULTIPLE_VALIDATION_ERRORS", {
...options, ...options,
metadata: { ...(options?.metadata ?? {}), errors: details }, metadata: { ...(options?.metadata ?? {}), errors: details },
}); });

View File

@ -69,7 +69,7 @@ export class MoneyValue extends ValueObject<MoneyValueProps> implements IMoneyVa
DineroFactory({ DineroFactory({
amount, amount,
precision: scale || MoneyValue.DEFAULT_SCALE, precision: scale || MoneyValue.DEFAULT_SCALE,
currency: (currency_code as Currency) || MoneyValue.DEFAULT_CURRENCY_CODE, currency: (currency_code || MoneyValue.DEFAULT_CURRENCY_CODE) as Currency,
}) })
); // 🔒 Garantiza inmutabilidad ); // 🔒 Garantiza inmutabilidad
} }

View File

@ -3,6 +3,6 @@
"compilerOptions": { "compilerOptions": {
"composite": true "composite": true
}, },
"include": ["src", "../../modules/core/src/web/lib/helpers/money-funcs.ts"], "include": ["src"],
"exclude": ["src/**/__tests__/*"] "exclude": ["src/**/__tests__/*"]
} }

View File

@ -3,6 +3,6 @@
"compilerOptions": { "compilerOptions": {
"composite": true "composite": true
}, },
"include": ["src", "../../modules/core/src/web/lib/helpers/money-funcs.ts"], "include": ["src"],
"exclude": ["src/**/__tests__/*"] "exclude": ["src/**/__tests__/*"]
} }

View File

@ -7,6 +7,7 @@
"paths": { "paths": {
"@erp/core/*": ["modules/core/src/*"], "@erp/core/*": ["modules/core/src/*"],
"@erp/auth/*": ["modules/auth/src/*"], "@erp/auth/*": ["modules/auth/src/*"],
"@erp/customers/*": ["modules/customers/src/*"],
"@erp/customer-invoices/*": ["modules/customer-invoices/src/*"] "@erp/customer-invoices/*": ["modules/customer-invoices/src/*"]
}, },

View File

@ -9,8 +9,8 @@ importers:
.: .:
devDependencies: devDependencies:
'@biomejs/biome': '@biomejs/biome':
specifier: 1.9.4 specifier: 2.0.6
version: 1.9.4 version: 2.0.6
'@repo/typescript-config': '@repo/typescript-config':
specifier: workspace:* specifier: workspace:*
version: link:packages/typescript-config version: link:packages/typescript-config
@ -359,9 +359,6 @@ importers:
'@types/react-dom': '@types/react-dom':
specifier: ^19.1.3 specifier: ^19.1.3
version: 19.2.1(@types/react@19.2.2) version: 19.2.1(@types/react@19.2.2)
'@types/react-i18next':
specifier: ^8.1.0
version: 8.1.0(i18next@25.6.0(typescript@5.8.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)
typescript: typescript:
specifier: ^5.8.3 specifier: ^5.8.3
version: 5.8.3 version: 5.8.3
@ -377,6 +374,9 @@ importers:
'@repo/rdx-ddd': '@repo/rdx-ddd':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/rdx-ddd version: link:../../packages/rdx-ddd
'@repo/rdx-logger':
specifier: workspace:*
version: link:../../packages/rdx-logger
'@repo/rdx-ui': '@repo/rdx-ui':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/rdx-ui version: link:../../packages/rdx-ui
@ -432,9 +432,6 @@ importers:
'@hookform/devtools': '@hookform/devtools':
specifier: ^4.4.0 specifier: ^4.4.0
version: 4.4.0(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) version: 4.4.0(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@types/axios':
specifier: ^0.14.4
version: 0.14.4
'@types/dinero.js': '@types/dinero.js':
specifier: ^1.9.4 specifier: ^1.9.4
version: 1.9.4 version: 1.9.4
@ -571,9 +568,6 @@ importers:
'@types/react-dom': '@types/react-dom':
specifier: ^19.1.3 specifier: ^19.1.3
version: 19.2.1(@types/react@19.2.2) version: 19.2.1(@types/react@19.2.2)
'@types/react-i18next':
specifier: ^8.1.0
version: 8.1.0(i18next@25.6.0(typescript@5.8.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)
'@types/react-router-dom': '@types/react-router-dom':
specifier: ^5.3.3 specifier: ^5.3.3
version: 5.3.3 version: 5.3.3
@ -1246,54 +1240,107 @@ packages:
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
hasBin: true hasBin: true
'@biomejs/biome@2.0.6':
resolution: {integrity: sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@1.9.4': '@biomejs/cli-darwin-arm64@1.9.4':
resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@biomejs/cli-darwin-arm64@2.0.6':
resolution: {integrity: sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@1.9.4': '@biomejs/cli-darwin-x64@1.9.4':
resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@biomejs/cli-darwin-x64@2.0.6':
resolution: {integrity: sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@1.9.4': '@biomejs/cli-linux-arm64-musl@1.9.4':
resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-arm64-musl@2.0.6':
resolution: {integrity: sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-arm64@1.9.4': '@biomejs/cli-linux-arm64@1.9.4':
resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@biomejs/cli-linux-arm64@2.0.6':
resolution: {integrity: sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-x64-musl@1.9.4': '@biomejs/cli-linux-x64-musl@1.9.4':
resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64-musl@2.0.6':
resolution: {integrity: sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-linux-x64@1.9.4': '@biomejs/cli-linux-x64@1.9.4':
resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@biomejs/cli-linux-x64@2.0.6':
resolution: {integrity: sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-win32-arm64@1.9.4': '@biomejs/cli-win32-arm64@1.9.4':
resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@biomejs/cli-win32-arm64@2.0.6':
resolution: {integrity: sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@1.9.4': '@biomejs/cli-win32-x64@1.9.4':
resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==}
engines: {node: '>=14.21.3'} engines: {node: '>=14.21.3'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@biomejs/cli-win32-x64@2.0.6':
resolution: {integrity: sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
'@codelytv/criteria@2.0.0': '@codelytv/criteria@2.0.0':
resolution: {integrity: sha512-EgXNABlN3vBsfIplqsEMcUX836SzoTeHoE4m8H/9pujiAw/vuDam27x+AnwpJmW2XHkzMk39G9wsejopFN4kTQ==} resolution: {integrity: sha512-EgXNABlN3vBsfIplqsEMcUX836SzoTeHoE4m8H/9pujiAw/vuDam27x+AnwpJmW2XHkzMk39G9wsejopFN4kTQ==}
@ -2778,10 +2825,6 @@ packages:
resolution: {integrity: sha512-EE/27azLteK24It0B0IrjA7yWFC6jYZoTTUzL7R7HgiN0BWBPrTp6Ugpn0iE6+Bn9fFcjSp/IBBG8D8c7vXD1g==} resolution: {integrity: sha512-EE/27azLteK24It0B0IrjA7yWFC6jYZoTTUzL7R7HgiN0BWBPrTp6Ugpn0iE6+Bn9fFcjSp/IBBG8D8c7vXD1g==}
hasBin: true hasBin: true
'@types/axios@0.14.4':
resolution: {integrity: sha512-9JgOaunvQdsQ/qW2OPmE5+hCeUB52lQSolecrFrthct55QekhmXEwT203s20RL+UHtCQc15y3VXpby9E7Kkh/g==}
deprecated: This is a stub types definition. axios provides its own type definitions, so you do not need this installed.
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -6541,30 +6584,65 @@ snapshots:
'@biomejs/cli-win32-arm64': 1.9.4 '@biomejs/cli-win32-arm64': 1.9.4
'@biomejs/cli-win32-x64': 1.9.4 '@biomejs/cli-win32-x64': 1.9.4
'@biomejs/biome@2.0.6':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.0.6
'@biomejs/cli-darwin-x64': 2.0.6
'@biomejs/cli-linux-arm64': 2.0.6
'@biomejs/cli-linux-arm64-musl': 2.0.6
'@biomejs/cli-linux-x64': 2.0.6
'@biomejs/cli-linux-x64-musl': 2.0.6
'@biomejs/cli-win32-arm64': 2.0.6
'@biomejs/cli-win32-x64': 2.0.6
'@biomejs/cli-darwin-arm64@1.9.4': '@biomejs/cli-darwin-arm64@1.9.4':
optional: true optional: true
'@biomejs/cli-darwin-arm64@2.0.6':
optional: true
'@biomejs/cli-darwin-x64@1.9.4': '@biomejs/cli-darwin-x64@1.9.4':
optional: true optional: true
'@biomejs/cli-darwin-x64@2.0.6':
optional: true
'@biomejs/cli-linux-arm64-musl@1.9.4': '@biomejs/cli-linux-arm64-musl@1.9.4':
optional: true optional: true
'@biomejs/cli-linux-arm64-musl@2.0.6':
optional: true
'@biomejs/cli-linux-arm64@1.9.4': '@biomejs/cli-linux-arm64@1.9.4':
optional: true optional: true
'@biomejs/cli-linux-arm64@2.0.6':
optional: true
'@biomejs/cli-linux-x64-musl@1.9.4': '@biomejs/cli-linux-x64-musl@1.9.4':
optional: true optional: true
'@biomejs/cli-linux-x64-musl@2.0.6':
optional: true
'@biomejs/cli-linux-x64@1.9.4': '@biomejs/cli-linux-x64@1.9.4':
optional: true optional: true
'@biomejs/cli-linux-x64@2.0.6':
optional: true
'@biomejs/cli-win32-arm64@1.9.4': '@biomejs/cli-win32-arm64@1.9.4':
optional: true optional: true
'@biomejs/cli-win32-arm64@2.0.6':
optional: true
'@biomejs/cli-win32-x64@1.9.4': '@biomejs/cli-win32-x64@1.9.4':
optional: true optional: true
'@biomejs/cli-win32-x64@2.0.6':
optional: true
'@codelytv/criteria@2.0.0': {} '@codelytv/criteria@2.0.0': {}
'@colors/colors@1.6.0': {} '@colors/colors@1.6.0': {}
@ -8026,12 +8104,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
'@types/axios@0.14.4':
dependencies:
axios: 1.12.2
transitivePeerDependencies:
- debug
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.28.4 '@babel/parser': 7.28.4

View File

@ -2,12 +2,15 @@ packages:
- packages/* - packages/*
- modules/* - modules/*
- apps/* - apps/*
ignoredBuiltDependencies: ignoredBuiltDependencies:
- esbuild - esbuild
onlyBuiltDependencies: onlyBuiltDependencies:
- '@biomejs/biome' - '@biomejs/biome'
- '@parcel/watcher' - '@parcel/watcher'
- '@tailwindcss/oxide' - '@tailwindcss/oxide'
- bcrypt - bcrypt
- core-js-pure - core-js-pure
- puppeteer
- sharp - sharp