This commit is contained in:
David Arranz 2025-05-20 12:08:24 +02:00
parent bbd79cc869
commit c05585775d
132 changed files with 234 additions and 468 deletions

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
interface IAccountStatusProps {

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import bcrypt from "bcrypt";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
interface IInvoiceStatusProps {

View File

@ -5,6 +5,7 @@
"main": "index.ts",
"scripts": {
"build": "tsc && tsup",
"dev": "node --import=tsx --watch src/index.ts",
"start:dev": "node --import=tsx --watch src/index.ts",
"start:prod": "node dist/index.js",
"clean": "rm -rf dist node_modules",
@ -41,6 +42,7 @@
"dependencies": {
"@erp/auth": "workspace:*",
"@erp/core": "workspace:*",
"@erp/invoices": "workspace:*",
"bcrypt": "^5.1.1",
"cls-rtracer": "^2.6.3",
"cors": "^2.8.5",
@ -49,9 +51,7 @@
"express": "^4.18.2",
"helmet": "^8.0.0",
"http": "0.0.1-security",
"http-status": "^2.1.0",
"jsonwebtoken": "^9.0.2",
"libphonenumber-js": "^1.11.20",
"luxon": "^3.5.0",
"module-alias": "^2.2.3",
"mysql2": "^3.12.0",

View File

@ -1,10 +1,10 @@
import { globalErrorHandler } from "@erp/core";
//import { initPackages } from "@/core/package-loader";
import dotenv from "dotenv";
import express, { Application } from "express";
import helmet from "helmet";
import responseTime from "response-time";
import { globalErrorHandler } from "./core/common/presentation";
import { logger } from "./core/logger";
import { logger } from "./lib/logger";
dotenv.config();

View File

@ -1,7 +1,6 @@
import { logger } from "@/core/logger";
import { logger } from "@/lib/logger";
import dotenv from "dotenv";
import { Sequelize } from "sequelize";
import { registerModels } from "./register-models";
dotenv.config();
@ -50,20 +49,19 @@ export async function tryConnectToDatabase() {
if (!database) {
const error = new Error("❌ Database not found.");
logger.error({
message: error.message,
logger.error(error.message, {
label: "tryConnectToDatabase",
});
throw error;
}
logger.info({ message: `🔸 Connecting to database...`, label: "tryConnectToDatabase" });
logger.info("🔸 Connecting to database...", {
label: "tryConnectToDatabase",
});
try {
await database.authenticate();
await registerModels(database);
logger.info({
message: `✔️${" "}Database connection established successfully.`,
logger.info(`✔️${" "}Database connection established successfully.`, {
label: "tryConnectToDatabase",
meta: {
host: process.env.DB_HOST,
@ -74,8 +72,7 @@ export async function tryConnectToDatabase() {
});
return database;
} catch (error) {
logger.error({
message: `❌ Unable to connect to the database: ${(error as Error).message}`,
logger.error(`❌ Unable to connect to the database: ${(error as Error).message}`, {
error,
label: "tryConnectToDatabase",
});

View File

@ -1,78 +0,0 @@
import * as path from "path";
import { logger } from "@/core/logger";
import * as glob from "glob";
import { DataTypes, Sequelize } from "sequelize";
/**
* 🔹 Registra todos los modelos en Sequelize
*/
export const registerModels = async (database: Sequelize) => {
if (!database) {
const error = new Error("❌ Database not found.");
logger.error({
message: error.message,
label: "initModels",
});
throw error;
}
const cwd = path.resolve(`${__dirname}/../`);
const models: { [key: string]: any } = {};
// Opciones para buscar los modelos
const globOptions = {
cwd,
nocase: true,
nodir: true,
absolute: false,
};
try {
logger.info(`🔎 Searching models in: ${cwd}`);
// Buscamos los ficheros que terminen en .model.js o .model.ts
for (const file of glob.sync("**/*.model.{js,ts}", globOptions)) {
logger.info({ message: `📄 File >> ${file}...`, label: "registerModels" });
const modelFile = require(path.resolve(cwd, file));
const modelDef = modelFile.default;
const model = typeof modelDef === "function" ? modelDef(database, DataTypes) : false;
if (model) {
models[model.name] = model;
logger.info({
message: `🔸 Model "${model.name}" registered (sequelize)`,
label: "registerModels",
});
} else {
logger.info({ message: "🚫 No model", label: "registerModels" });
}
}
} catch (error) {
logger.error("❌ Error registering models:", error);
process.exit(1);
}
// Configurar asociaciones
for (const model of Object.values(models)) {
if (typeof model.associate === "function") {
model.associate(database);
}
}
try {
// Sincronizamos DB en modo desarrollo
if (process.env.NODE_ENV !== "production") {
await database.sync({ force: false, alter: true });
logger.info({ message: `✔️${" "}Database synchronized successfully.`, label: "initModels" });
} else {
logger.warning({
message: "⚠️ Running in production mode - Skipping database sync.",
label: "initModels",
});
}
} catch (err) {
const error = err as Error;
logger.error({ message: "❌ Error synchronizing database:", error, label: "initModels" });
throw error;
}
};

View File

@ -1,27 +0,0 @@
import { IModuleServer, ModuleParams } from "@/core";
import { logger } from "@/core/logger";
import { invoicesRouter, models } from "../../../../../modules/invoices/src/api/intrastructure";
export const invoicesModule: IModuleServer = {
metadata: {
name: "invoices",
version: "1.0.0",
dependencies: [],
},
init(params: ModuleParams) {
// const contacts = getService<ContactsService>("contacts");
invoicesRouter(params);
logger.info({ message: "🚀 Invoices module initialized", label: "invoices" });
},
registerDependencies(params) {
const { database } = params;
logger.info({ message: "🚀 Invoices module dependencies registered", label: "invoices" });
return {
models,
services: {
getInvoice: () => {},
/*...*/
},
};
},
};

View File

@ -1,2 +0,0 @@
export * from "./infrastructure";
export * from "./presentation";

View File

@ -1,4 +1,2 @@
export * from "./sequelize";
export * from "./database";
//export * from "./passport";
export * from "./sequelize";
//export * from "./sequelize";

View File

@ -1,20 +0,0 @@
export interface IErrorDTO {
detail?: string;
instance?: string;
status: number;
title: string;
type?: string;
context: IErrorContextDTO;
extra: IErrorExtraDTO;
}
export interface IErrorContextDTO {
user?: unknown;
params?: Record<string, any>;
query?: Record<string, any>;
body?: Record<string, any>;
}
export interface IErrorExtraDTO {
errors: Record<string, any>[];
}

View File

@ -1,2 +0,0 @@
export * from "./error.dto";
export * from "./types.dto";

View File

@ -1,15 +0,0 @@
export interface IMoneyDTO {
amount: number | null;
scale: number;
currency_code: string;
}
export interface IPercentageDTO {
amount: number | null;
scale: number;
}
export interface IQuantityDTO {
amount: number | null;
scale: number;
}

View File

@ -1,2 +0,0 @@
export * from "./dto";
export * from "./express";

View File

@ -1 +0,0 @@
export * from "./common";

View File

@ -1 +1 @@
export * from "./httpServer";
export * from "../../../lib/httpServer";

View File

@ -1,11 +1,11 @@
import { logger } from "@/lib/logger";
import { DateTime } from "luxon";
import http from "node:http";
import os from "node:os";
import { logger } from "@/core/logger";
import { DateTime } from "luxon";
import { createApp } from "./app";
import { ENV } from "./config";
import { tryConnectToDatabase } from "./config/database";
import { initModules } from "./core/helpers";
import { initModules } from "./lib/modules";
import { registerModules } from "./register-modules";
// Guardamos información del estado del servidor
@ -91,7 +91,7 @@ const server = http
})
)
.on("close", () =>
logger.info(`Shut down at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`)
logger.info(`Shutdown at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`)
)
.on("connection", serverConnection)
.on("error", serverError);
@ -149,7 +149,7 @@ process.on("uncaughtException", (error: Error) => {
}
}
for (const address in addresses) {
for (const address of addresses) {
logger.info(`⚡️ Server accessible at: http://${address}:${currentState.port}`);
}

View File

@ -0,0 +1,2 @@
export * from "./logger";
export * from "./modules";

View File

@ -12,4 +12,8 @@ export class ConsoleLogger implements ILogger {
error(message: string, error?: Error | any) {
console.error(`[ERROR] ${message}`, error?.stack || error);
}
debug(message: string, meta?: any) {
console.debug(`[DEBUG] ${message}`, meta ?? "");
}
}

View File

@ -19,4 +19,9 @@ export class SentryLogger implements ILogger {
console.error(`[ERROR] ${message}`, error);
//Sentry.captureException(error);
}
debug(message: string, meta?: any) {
console.debug(`[DEBUG] ${message}`, meta);
//Sentry.captureMessage(message, "debug");
}
}

View File

@ -1,7 +1,8 @@
import { authAPIModule } from "@erp/auth";
import { registerModule } from "./core/helpers";
//import { authAPIModule } from "@erp/auth";
import { invoicesAPIModule } from "@erp/invoices";
import { registerModule } from "./lib/modules";
export const registerModules = () => {
//registerPackage(ContactsPackage);
registerModule(authAPIModule);
//registerModule(authAPIModule);
registerModule(invoicesAPIModule);
};

View File

@ -9,12 +9,6 @@
"outDir": "dist"
},
//"files": ["src/index.ts"], // Esta opción compila sólo los archivos listados (y sus dependencias importadas).
"include": [
"src/**/*.ts",
"../../packages/rdx-ddd/src/aggregate-root.ts",
"../../packages/rdx-ddd/src/domain-entity.ts",
"../../packages/rdx-ddd/src/index.ts",
"../../packages/rdx-ddd/src/aggregate-root-repository.interface.ts"
],
"include": ["src"],
"exclude": ["src/**/__tests__/*", "src/**/*.mock.*", "src/**/*.test.*", "node_modules", "dist"]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,11 +10,11 @@ export const authAPIModule: IModuleServer = {
// const contacts = getService<ContactsService>("contacts");
const { logger } = params;
//invoicesRouter(params);
logger.info({ message: "🚀 Auth module initialized", label: "invoices" });
logger.info("🚀 Auth module initialized", { label: "auth" });
},
registerDependencies(params: ModuleParams) {
const { database, logger } = params;
logger.info({ message: "🚀 Auth module dependencies registered", label: "invoices" });
logger.info("🚀 Auth module dependencies registered", { label: "auth" });
return {
//models,
services: {

View File

@ -11,6 +11,7 @@
"./components/*": "./src/web/components/*.tsx"
},
"peerDependencies": {
"express": "^4.18.2",
"joi": "^17.13.3",
"react": "^18 || ^19",
"react-dom": "^18 || ^19",
@ -20,17 +21,22 @@
"@biomejs/biome": "1.9.4",
"@testing-library/react-hooks": "^8.0.1",
"@types/axios": "^0.14.4",
"@types/express": "^4.17.21",
"@types/jest": "29.5.14",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"typescript": "^5.8.3"
},
"dependencies": {
"@repo/rdx-ddd": "workspace:*",
"@repo/rdx-utils": "workspace:*",
"@repo/rdx-criteria": "workspace:*",
"@tanstack/react-query": "^5.75.4",
"axios": "^1.9.0",
"express": "^4.18.2",
"http-status": "^2.1.0",
"joi": "^17.13.3",
"libphonenumber-js": "^1.11.20",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^6.26.0",

View File

@ -1,3 +1,4 @@
export * from "./dto";
export * from "./infrastructure";
export * from "./logger";
export * from "./modules";
export * from "./dto";

View File

@ -1,4 +1,3 @@
import { logger } from "@/core/logger";
import { NextFunction, Request, Response } from "express";
import httpStatus from "http-status";
import { ApiError } from "./api-error";
@ -9,7 +8,6 @@ export abstract class ExpressController {
protected next!: NextFunction;
static errorResponse(apiError: ApiError, res: Response) {
logger.error(`[${apiError.status}] ${apiError.title}: ${apiError.detail}`);
return res.status(apiError.status).json(apiError);
}
@ -159,7 +157,6 @@ export abstract class ExpressController {
this.executeImpl();
} catch (error: unknown) {
const _error = error as Error;
logger.error(`Unhandled error in controller: ${_error.message}`);
this.internalServerError(_error.message);
}
}

View File

@ -1,4 +1,4 @@
import { logger } from "@/core/logger";
import { logger } from "@/lib/logger";
import { NextFunction, Request, Response } from "express";
import { ApiError } from "../api-error";

View File

@ -0,0 +1,2 @@
export * from "./express";
export * from "./sequelize";

View File

@ -1,4 +1,4 @@
import { DomainEntity } from "@/core/common/domain";
import { DomainEntity } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { Model } from "sequelize";
@ -50,6 +50,10 @@ export abstract class SequelizeMapper<
params?: MapperParamsType
): Result<Collection<TEntity>, Error> {
try {
if (source.length === 0) {
return Result.ok(new Collection([], totalCount));
}
const items = source.map(
(value, index) => this.mapToDomain(value, { index, ...params }).data
);

View File

@ -1,7 +1,6 @@
import { IAggregateRootRepository, UniqueID } from "@/core/common/domain";
import { IAggregateRootRepository, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { ModelDefined, Sequelize, Transaction } from "sequelize";
import { logger } from "../logger";
export abstract class SequelizeRepository<T> implements IAggregateRootRepository<T> {
protected readonly _database!: Sequelize;
@ -141,7 +140,7 @@ export abstract class SequelizeRepository<T> implements IAggregateRootRepository
): Result<never, Error> {
const _error = error as Error;
logger.error({ message: `Database error: ${_error.message}`, label: "SequelizeRepository" });
//logger.error({ message: `Database error: ${_error.message}`, label: "SequelizeRepository" });
// Si la clase hija proporciona un mapeo personalizado, lo usa
if (errorMapper) {

View File

@ -2,4 +2,5 @@ export interface ILogger {
info(message: string, meta?: any): void;
warn(message: string, meta?: any): void;
error(message: string, error?: Error | any): void;
debug(message: string, meta?: any): void;
}

View File

@ -16,25 +16,33 @@
"peerDependencies": {
"ag-grid-community": "^33.3.0",
"ag-grid-react": "^33.3.0",
"express": "^4.18.2",
"i18next": "^25.1.1",
"lucide-react": "^0.503.0",
"react": "^18 || ^19",
"react-dom": "^18 || ^19",
"react-i18next": "^15.5.1",
"react-router-dom": "^6.26.0"
"react-router-dom": "^6.26.0",
"sequelize": "^6.37.5"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/express": "^4.17.21",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@types/react-i18next": "^8.1.0",
"sequelize": "^6.37.5",
"typescript": "^5.8.3"
},
"dependencies": {
"@erp/core": "workspace:*",
"@repo/rdx-ddd": "workspace:*",
"@repo/rdx-utils": "workspace:*",
"@repo/rdx-ui": "workspace:*",
"@repo/shadcn-ui": "workspace:*",
"ag-grid-community": "^33.3.0",
"ag-grid-react": "^33.3.0",
"express": "^4.18.2",
"i18next": "^25.1.1",
"lucide-react": "^0.503.0",
"react": "^19.1.0",

View File

@ -1,6 +1,6 @@
import { UniqueID } from "@/core/common/domain";
import { ITransactionManager } from "@/core/common/infrastructure/database";
import { logger } from "@/core/logger";
import { logger } from "@/lib/logger";
import { Result } from "@repo/rdx-utils";
import { IInvoiceService, Invoice } from "../domain";

View File

@ -1,5 +1,5 @@
import { ITransactionManager } from "@/core/common/infrastructure/database";
import { logger } from "@/core/logger";
import { logger } from "@/lib/logger";
import { Collection, Result } from "@repo/rdx-utils";
import { IInvoiceService, Invoice } from "../domain";

View File

@ -1,4 +1,4 @@
import { AggregateRoot, MoneyValue, UniqueID, UtcDate } from "@/core/common/domain";
import { AggregateRoot, MoneyValue, UniqueID, UtcDate } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { InvoiceCustomer, InvoiceItem, InvoiceItems } from "../entities";
import { InvoiceNumber, InvoiceSerie, InvoiceStatus } from "../value-objects";

View File

@ -1,9 +1,4 @@
import {
type EmailAddress,
type Name,
type PostalAddress,
ValueObject,
} from "@/core/common/domain";
import { EmailAddress, Name, PostalAddress, ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { PhoneNumber } from "libphonenumber-js";
import { InvoiceAddressType } from "../../value-objects";
@ -26,12 +21,12 @@ export interface IInvoiceAddress {
export class InvoiceAddress extends ValueObject<IInvoiceAddressProps> implements IInvoiceAddress {
public static create(props: IInvoiceAddressProps) {
return Result.ok(new this(props));
return Result.ok(new InvoiceAddress(props));
}
public static createShippingAddress(props: IInvoiceAddressProps) {
return Result.ok(
new this({
new InvoiceAddress({
...props,
type: InvoiceAddressType.create("shipping").data,
})
@ -40,7 +35,7 @@ export class InvoiceAddress extends ValueObject<IInvoiceAddressProps> implements
public static createBillingAddress(props: IInvoiceAddressProps) {
return Result.ok(
new this({
new InvoiceAddress({
...props,
type: InvoiceAddressType.create("billing").data,
})

View File

@ -1,4 +1,4 @@
import { DomainEntity, Name, TINNumber, UniqueID } from "@/core/common/domain";
import { DomainEntity, Name, TINNumber, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { InvoiceAddress } from "./invoice-address";

View File

@ -1,4 +1,4 @@
import { DomainEntity, MoneyValue, Percentage, Quantity, UniqueID } from "@/core/common/domain";
import { DomainEntity, MoneyValue, Percentage, Quantity, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { InvoiceItemDescription } from "../../value-objects";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
interface IInvoiceAddressTypeProps {

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import { z } from "zod";

View File

@ -1,4 +1,4 @@
import { ValueObject } from "@/core/common/domain";
import { ValueObject } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
interface IInvoiceStatusProps {
@ -22,7 +22,7 @@ export class InvoiceStatus extends ValueObject<IInvoiceStatusProps> {
};
static create(value: string): Result<InvoiceStatus, Error> {
if (!this.ALLOWED_STATUSES.includes(value)) {
if (!InvoiceStatus.ALLOWED_STATUSES.includes(value)) {
return Result.fail(new Error(`Estado de la factura no válido: ${value}`));
}

View File

@ -1,6 +1,7 @@
import { IModuleServer, ModuleParams } from "@erp/core";
import { invoicesRouter, models } from "./infrastructure";
export const invoicesModule: IModuleServer = {
export const invoicesAPIModule: IModuleServer = {
metadata: {
name: "invoices",
version: "1.0.0",
@ -9,14 +10,14 @@ export const invoicesModule: IModuleServer = {
init(params: ModuleParams) {
// const contacts = getService<ContactsService>("contacts");
const { logger } = params;
//invoicesRouter(params);
logger.info({ message: "🚀 Invoices module initialized", label: "invoices" });
invoicesRouter(params);
logger.info("🚀 Invoices module initialized", { label: "invoices" });
},
registerDependencies(params) {
const { database, logger } = params;
logger.info({ message: "🚀 Invoices module dependencies registered", label: "invoices" });
logger.info("🚀 Invoices module dependencies registered", { label: "invoices" });
return {
//models,
models,
services: {
getInvoice: () => {},
/*...*/

View File

@ -1,7 +1,7 @@
import { ModuleParams } from "@/core";
import { ModuleParams } from "@erp/core";
import { Application, NextFunction, Request, Response, Router } from "express";
import { Sequelize } from "sequelize";
import { buildGetInvoiceController, buildListInvoicesController } from "../../presentation";
import { buildListInvoicesController } from "../../presentation";
export const invoicesRouter = (params: ModuleParams) => {
const { app, database, baseRoutePath } = params as {
@ -21,14 +21,14 @@ export const invoicesRouter = (params: ModuleParams) => {
}
);
routes.get(
/*routes.get(
"/:invoiceId",
//checkTabContext,
//checkUser,
(req: Request, res: Response, next: NextFunction) => {
buildGetInvoiceController(database).execute(req, res, next);
}
);
);*/
/*routes.post(
"/",

View File

@ -1,18 +1,9 @@
import { Invoice, InvoiceItem, InvoiceItemDescription } from "@/contexts/invoices/domain";
import {
type ISequelizeMapper,
type MapperParamsType,
MoneyValue,
Percentage,
Quantity,
Result,
SequelizeMapper,
UniqueID,
} from "@/core";
import { InvoiceModel } from "../sequelize";
import { ISequelizeMapper, MapperParamsType, SequelizeMapper } from "@erp/core";
import { MoneyValue, Percentage, Quantity, UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { InferCreationAttributes } from "sequelize";
import { InvoiceItemCreationAttributes, InvoiceItemModel } from "../sequelize";
import { Invoice, InvoiceItem, InvoiceItemDescription } from "../../domain";
import { InvoiceItemCreationAttributes, InvoiceItemModel, InvoiceModel } from "../sequelize";
export interface IInvoiceItemMapper
extends ISequelizeMapper<InvoiceItemModel, InvoiceItemCreationAttributes, InvoiceItem> {}
@ -21,6 +12,25 @@ export class InvoiceItemMapper
extends SequelizeMapper<InvoiceItemModel, InvoiceItemCreationAttributes, InvoiceItem>
implements IInvoiceItemMapper
{
mapArrayToDomain(
source: InvoiceItemModel[],
params?: MapperParamsType
): Result<Collection<InvoiceItem>, Error> {
throw new Error("Method not implemented.");
}
mapArrayAndCountToDomain(
source: InvoiceItemModel[],
totalCount: number,
params?: MapperParamsType
): Result<Collection<InvoiceItem>, Error> {
throw new Error("Method not implemented.");
}
mapCollectionToPersistence(
source: Collection<InvoiceItem>,
params?: MapperParamsType
): InferCreationAttributes<InvoiceItemModel, { omit: "invoice" }>[] {
throw new Error("Method not implemented.");
}
public mapToDomain(
source: InvoiceItemModel,
params?: MapperParamsType

View File

@ -1,12 +1,7 @@
import { Invoice, InvoiceNumber, InvoiceSerie, InvoiceStatus } from "@/contexts/invoices/domain";
import {
type ISequelizeMapper,
type MapperParamsType,
Result,
SequelizeMapper,
UniqueID,
UtcDate,
} from "@/core";
import { ISequelizeMapper, MapperParamsType, SequelizeMapper } from "@erp/core";
import { UniqueID, UtcDate } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { Invoice, InvoiceNumber, InvoiceSerie, InvoiceStatus } from "../../domain";
import { InvoiceCreationAttributes, InvoiceModel } from "../sequelize";
import { InvoiceItemMapper } from "./invoice-item.mapper";

View File

@ -1,5 +1,6 @@
import { Collection, Result, SequelizeRepository, UniqueID } from "@/core";
import { logger } from "@/core/logger";
import { SequelizeRepository } from "@erp/core";
import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize";
import { IInvoiceRepository, Invoice } from "../../domain";
import { IInvoiceMapper } from "../mappers/invoice.mapper";
@ -37,6 +38,8 @@ export class InvoiceRepository extends SequelizeRepository<Invoice> implements I
async findAll(transaction?: Transaction): Promise<Result<Collection<Invoice>, Error>> {
try {
console.error(this._database.models);
const rawInvoices = await InvoiceModel.findAll({
include: [
{
@ -47,18 +50,11 @@ export class InvoiceRepository extends SequelizeRepository<Invoice> implements I
transaction,
});
/*if (!rawInvoices === true) {
return Result.fail(new Error("Invoice not exists"));
}*/
logger.debug({
message: "rawInvoices",
label: "InvoiceRepository",
meta: { data: rawInvoices },
});
console.error("aqui");
return this._mapper.mapArrayToDomain(rawInvoices);
} catch (error: unknown) {
console.error("Error in findAll", error);
return this._handleDatabaseError(error, this._customErrorMapper);
}
}
@ -82,8 +78,6 @@ export class InvoiceRepository extends SequelizeRepository<Invoice> implements I
if (!rawInvoice === true) {
return Result.fail(new Error(`Invoice with id ${id.toString()} not exists`));
}
return this._mapper.mapToDomain(rawInvoice);
} catch (error: any) {
return this._handleDatabaseError(error, this._customErrorMapper);
}

View File

@ -1,44 +0,0 @@
import { CreateInvoiceUseCase } from "@/contexts/invoices/application/create-invoice.use-case";
import { ExpressController, UniqueID } from "@/core";
import { ICreateInvoiceRequestDTO } from "../../../../common/dto";
import { ICreateInvoicePresenter } from "./presenter";
export class CreateInvoiceController extends ExpressController {
public constructor(
private readonly createInvoice: CreateInvoiceUseCase,
private readonly presenter: ICreateInvoicePresenter
) {
super();
}
protected async executeImpl() {
const createDTO: ICreateInvoiceRequestDTO = this.req.body;
// Validar ID
const invoiceIdOrError = UniqueID.create(createDTO.id);
if (invoiceIdOrError.isFailure) return this.invalidInputError("Invoice ID not valid");
const invoiceOrError = await this.createInvoice.execute(invoiceIdOrError.data, createDTO);
if (invoiceOrError.isFailure) {
return this.handleError(invoiceOrError.error);
}
return this.ok(this.presenter.toDTO(invoiceOrError.data));
}
private handleError(error: Error) {
const message = error.message;
if (
message.includes("Database connection lost") ||
message.includes("Database request timed out")
) {
return this.unavailableError(
"Database service is currently unavailable. Please try again later."
);
}
return this.conflictError(message);
}
}

View File

@ -1,18 +0,0 @@
import { CreateInvoiceUseCase } from "@/contexts/invoices/application/create-invoice.use-case";
import { InvoiceService } from "@/contexts/invoices/domain";
import { InvoiceRepository, invoiceMapper } from "@/contexts/invoices/intrastructure";
import { SequelizeTransactionManager } from "@/core";
import { Sequelize } from "sequelize";
import { CreateInvoiceController } from "./create-invoice.controller";
import { createInvoicePresenter } from "./presenter";
export const buildCreateInvoiceController = (database: Sequelize) => {
const transactionManager = new SequelizeTransactionManager(database);
const invoiceRepository = new InvoiceRepository(database, invoiceMapper);
const invoiceService = new InvoiceService(invoiceRepository);
const useCase = new CreateInvoiceUseCase(invoiceService, transactionManager);
const presenter = createInvoicePresenter;
return new CreateInvoiceController(useCase, presenter);
};

View File

@ -1,26 +0,0 @@
import { IInvoiceParticipant } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateInvoice_Participant_Response_DTO } from "@shared/contexts";
import { InvoiceParticipantAddressPresenter } from "./InvoiceParticipantAddress.presenter";
export const InvoiceParticipantPresenter = (
participant: IInvoiceParticipant,
context: IInvoicingContext,
): ICreateInvoice_Participant_Response_DTO | undefined => {
return {
id: participant.id.toString(),
tin: participant.tin.toString(),
first_name: participant.firstName.toString(),
last_name: participant.lastName.toString(),
company_name: participant.companyName.toString(),
billing_address: InvoiceParticipantAddressPresenter(
participant.billingAddress!,
context,
),
shipping_address: InvoiceParticipantAddressPresenter(
participant.shippingAddress!,
context,
),
};
};

View File

@ -1,19 +0,0 @@
import { InvoiceParticipantAddress } from "@/contexts/invoicing/domain";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICreateInvoice_AddressParticipant_Response_DTO } from "@shared/contexts";
export const InvoiceParticipantAddressPresenter = (
address: InvoiceParticipantAddress,
context: IInvoicingContext,
): ICreateInvoice_AddressParticipant_Response_DTO => {
return {
id: address.id.toString(),
street: address.street.toString(),
city: address.city.toString(),
postal_code: address.postalCode.toString(),
province: address.province.toString(),
country: address.country.toString(),
email: address.email.toString(),
phone: address.phone.toString(),
};
};

View File

@ -1,28 +0,0 @@
import { Invoice } from "@/contexts/invoices/domain";
import { ICreateInvoiceResponseDTO } from "../../../../../common/dto";
export interface ICreateInvoicePresenter {
toDTO: (invoice: Invoice) => ICreateInvoiceResponseDTO;
}
export const createInvoicePresenter: ICreateInvoicePresenter = {
toDTO: (invoice: Invoice): ICreateInvoiceResponseDTO => ({
id: invoice.id.toString(),
invoice_status: invoice.status.toString(),
invoice_number: invoice.invoiceNumber.toString(),
invoice_series: invoice.invoiceSeries.toString(),
issue_date: invoice.issueDate.toDateString(),
operation_date: invoice.operationDate.toDateString(),
language_code: "es",
currency: invoice.invoiceCurrency || "EUR",
subtotal: invoice.calculateSubtotal().toPrimitive(),
total: invoice.calculateTotal().toPrimitive(),
//sender: {}, //await InvoiceParticipantPresenter(invoice.senderId, context),
//customer: InvoiceParticipantPresenter(invoice.recipient, context),
//items: invoiceItemPresenter(invoice.items, context),
}),
};

View File

@ -1 +0,0 @@
export * from "./create-invoice.presenter";

View File

@ -1,5 +0,0 @@
//export * from "./create-invoice";
//export * from "./delete-invoice";
export * from "./get-invoice";
export * from "./list-invoices";
///export * from "./update-invoice";

View File

@ -1,19 +0,0 @@
import { InvoiceItem } from "@/contexts/invoicing/domain/InvoiceItems";
import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext";
import { ICollection, IMoney_Response_DTO } from "@shared/contexts";
export const invoiceItemPresenter = (
items: ICollection<InvoiceItem>,
context: IInvoicingContext
) =>
items.totalCount > 0
? items.items.map((item: InvoiceItem) => ({
description: item.description.toString(),
quantity: item.quantity.toString(),
unit_measure: "",
unit_price: item.unitPrice.toPrimitive() as IMoney_Response_DTO,
subtotal: item.calculateSubtotal().toPrimitive() as IMoney_Response_DTO,
tax_amount: item.calculateTaxAmount().toPrimitive() as IMoney_Response_DTO,
total: item.calculateTotal().toPrimitive() as IMoney_Response_DTO,
}))
: [];

Some files were not shown because too many files have changed in this diff Show More