.
This commit is contained in:
parent
42987d688f
commit
2e397900e8
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -14,7 +14,6 @@
|
|||||||
"name": "Launch Chrome localhost",
|
"name": "Launch Chrome localhost",
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"reAttach": true,
|
|
||||||
"url": "http://localhost:5173",
|
"url": "http://localhost:5173",
|
||||||
"webRoot": "${workspaceFolder}/client"
|
"webRoot": "${workspaceFolder}/client"
|
||||||
},
|
},
|
||||||
@ -30,10 +29,12 @@
|
|||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"name": "SERVER: Attach to dev:debug",
|
"name": "Attach to ts-node-dev",
|
||||||
"port": 4321,
|
"port": 4321,
|
||||||
"restart": true,
|
"restart": true,
|
||||||
"cwd": "${workspaceRoot}"
|
"timeout": 10000,
|
||||||
|
"sourceMaps": true,
|
||||||
|
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,7 +2,6 @@ DB_HOST=localhost
|
|||||||
DB_USER=rodax
|
DB_USER=rodax
|
||||||
DB_PASSWORD=rodax
|
DB_PASSWORD=rodax
|
||||||
DB_NAME=uecko_erp
|
DB_NAME=uecko_erp
|
||||||
DB_DIALECT=mariadb
|
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
|
||||||
PORT=3002
|
PORT=3002
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "ts-node-dev -r tsconfig-paths/register ./src/index.ts",
|
"dev:nodebug": "ts-node-dev -r tsconfig-paths/register ./src/index.ts",
|
||||||
"dev:debug": "ts-node-dev --transpile-only --respawn --inspect=4321 -r tsconfig-paths/register ./src/index.ts",
|
"dev": "ts-node-dev --transpile-only --respawn --inspect=4321 -r tsconfig-paths/register ./src/index.ts",
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"build": "npm run clean && npm run typecheck && esbuild src/index.ts --platform=node --format=cjs --bundle --sourcemap --minify --outdir=dist",
|
"build": "npm run clean && npm run typecheck && esbuild src/index.ts --platform=node --format=cjs --bundle --sourcemap --minify --outdir=dist",
|
||||||
@ -45,6 +45,7 @@
|
|||||||
"nodemon": "^3.1.9",
|
"nodemon": "^3.1.9",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^2.0.0",
|
||||||
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -63,7 +64,6 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"libphonenumber-js": "^1.11.20",
|
"libphonenumber-js": "^1.11.20",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
"mariadb": "^3.4.0",
|
|
||||||
"module-alias": "^2.2.3",
|
"module-alias": "^2.2.3",
|
||||||
"mysql2": "^3.12.0",
|
"mysql2": "^3.12.0",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
@ -75,7 +75,6 @@
|
|||||||
"sequelize": "^6.37.5",
|
"sequelize": "^6.37.5",
|
||||||
"shallow-equal-object": "^1.1.1",
|
"shallow-equal-object": "^1.1.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsconfig-paths": "^4.2.0",
|
|
||||||
"uuid": "^11.0.5",
|
"uuid": "^11.0.5",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-daily-rotate-file": "^5.0.0",
|
"winston-daily-rotate-file": "^5.0.0",
|
||||||
|
|||||||
@ -11,14 +11,11 @@ const postalCodeSchema = z
|
|||||||
message: "Invalid postal code format",
|
message: "Invalid postal code format",
|
||||||
});
|
});
|
||||||
|
|
||||||
const countrySchema = z.string().min(2).max(56);
|
|
||||||
|
|
||||||
const provinceSchema = z.string().min(2).max(50);
|
|
||||||
|
|
||||||
const citySchema = z.string().min(2).max(50);
|
|
||||||
|
|
||||||
const streetSchema = z.string().min(2).max(255);
|
const streetSchema = z.string().min(2).max(255);
|
||||||
const street2Schema = z.string().optional();
|
const street2Schema = z.string().optional();
|
||||||
|
const citySchema = z.string().min(2).max(50);
|
||||||
|
const stateSchema = z.string().min(2).max(50);
|
||||||
|
const countrySchema = z.string().min(2).max(56);
|
||||||
|
|
||||||
interface IPostalAddressProps {
|
interface IPostalAddressProps {
|
||||||
street: string;
|
street: string;
|
||||||
@ -37,7 +34,7 @@ export class PostalAddress extends ValueObject<IPostalAddressProps> {
|
|||||||
street2: street2Schema,
|
street2: street2Schema,
|
||||||
city: citySchema,
|
city: citySchema,
|
||||||
postalCode: postalCodeSchema,
|
postalCode: postalCodeSchema,
|
||||||
state: provinceSchema,
|
state: stateSchema,
|
||||||
country: countrySchema,
|
country: countrySchema,
|
||||||
})
|
})
|
||||||
.safeParse(values);
|
.safeParse(values);
|
||||||
@ -60,6 +57,20 @@ export class PostalAddress extends ValueObject<IPostalAddressProps> {
|
|||||||
return PostalAddress.create(values!).map((value) => Maybe.some(value));
|
return PostalAddress.create(values!).map((value) => Maybe.some(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static update(
|
||||||
|
oldAddress: PostalAddress,
|
||||||
|
data: Partial<PostalAddress>
|
||||||
|
): Result<PostalAddress, Error> {
|
||||||
|
return PostalAddress.create({
|
||||||
|
street: data.street ?? oldAddress.street,
|
||||||
|
street2: data.street2?.getOrUndefined() ?? oldAddress.street2.getOrUndefined(),
|
||||||
|
city: data.city ?? oldAddress.city,
|
||||||
|
postalCode: data.postalCode ?? oldAddress.postalCode,
|
||||||
|
state: data.state ?? oldAddress.state,
|
||||||
|
country: data.country ?? oldAddress.country,
|
||||||
|
}).getOrElse(this);
|
||||||
|
}
|
||||||
|
|
||||||
get street(): string {
|
get street(): string {
|
||||||
return this.props.street;
|
return this.props.street;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,11 +15,11 @@ interface IDomainMapper<TModel extends Model, TEntity extends DomainEntity<any>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IPersistenceMapper<TModelAttributes, TEntity extends DomainEntity<any>> {
|
interface IPersistenceMapper<TModelAttributes, TEntity extends DomainEntity<any>> {
|
||||||
mapToPersistence(source: TEntity, params?: MapperParamsType): Result<TModelAttributes, Error>;
|
mapToPersistence(source: TEntity, params?: MapperParamsType): TModelAttributes;
|
||||||
mapCollectionToPersistence(
|
mapCollectionToPersistence(
|
||||||
source: Collection<TEntity>,
|
source: Collection<TEntity>,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<TModelAttributes[], Error>;
|
): TModelAttributes[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISequelizeMapper<
|
export interface ISequelizeMapper<
|
||||||
@ -59,23 +59,13 @@ export abstract class SequelizeMapper<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract mapToPersistence(
|
public abstract mapToPersistence(source: TEntity, params?: MapperParamsType): TModelAttributes;
|
||||||
source: TEntity,
|
|
||||||
params?: MapperParamsType
|
|
||||||
): Result<TModelAttributes, Error>;
|
|
||||||
|
|
||||||
public mapCollectionToPersistence(
|
public mapCollectionToPersistence(
|
||||||
source: Collection<TEntity>,
|
source: Collection<TEntity>,
|
||||||
params?: MapperParamsType
|
params?: MapperParamsType
|
||||||
): Result<TModelAttributes[], Error> {
|
): TModelAttributes[] {
|
||||||
try {
|
return source.map((value, index) => this.mapToPersistence(value, { index, ...params }));
|
||||||
const result = source.map(
|
|
||||||
(value, index) => this.mapToPersistence(value, { index, ...params }).data
|
|
||||||
);
|
|
||||||
return Result.ok(result);
|
|
||||||
} catch (error) {
|
|
||||||
return Result.fail(error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected safeMap<T>(operation: () => T, key: string): Result<T, Error> {
|
protected safeMap<T>(operation: () => T, key: string): Result<T, Error> {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const sequelize = new Sequelize(
|
|||||||
process.env.DB_PASSWORD as string, // password
|
process.env.DB_PASSWORD as string, // password
|
||||||
{
|
{
|
||||||
host: process.env.DB_HOST as string,
|
host: process.env.DB_HOST as string,
|
||||||
dialect: "mariadb",
|
dialect: "mysql",
|
||||||
port: parseInt(process.env.DB_PORT || "3306", 10),
|
port: parseInt(process.env.DB_PORT || "3306", 10),
|
||||||
dialectOptions: {
|
dialectOptions: {
|
||||||
multipleStatements: true,
|
multipleStatements: true,
|
||||||
|
|||||||
@ -0,0 +1,88 @@
|
|||||||
|
import { EmailAddress, PhoneNumber, PostalAddress, TINNumber, UniqueID } from "@common/domain";
|
||||||
|
|
||||||
|
import { Maybe, Result } from "@common/helpers";
|
||||||
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { logger } from "@common/infrastructure/logger";
|
||||||
|
import { Account, AccountStatus, IAccountProps, IAccountService } from "@contexts/accounts/domain";
|
||||||
|
import { ICreateAccountRequestDTO } from "../presentation";
|
||||||
|
|
||||||
|
export class CreateAccountUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly accountService: IAccountService,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(
|
||||||
|
accountID: UniqueID,
|
||||||
|
dto: ICreateAccountRequestDTO
|
||||||
|
): Promise<Result<Account, Error>> {
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
const validOrErrors = this.validateAccountData(dto);
|
||||||
|
if (validOrErrors.isFailure) {
|
||||||
|
return Result.fail(validOrErrors.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = validOrErrors.data;
|
||||||
|
|
||||||
|
// Update account with dto
|
||||||
|
return await this.accountService.createAccount(accountID, data, transaction);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
logger.error(error as Error);
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateAccountData(dto: ICreateAccountRequestDTO): Result<IAccountProps, Error> {
|
||||||
|
const errors: Error[] = [];
|
||||||
|
|
||||||
|
const tinOrError = TINNumber.create(dto.tin);
|
||||||
|
const emailOrError = EmailAddress.create(dto.email);
|
||||||
|
const phoneOrError = PhoneNumber.create(dto.phone);
|
||||||
|
const faxOrError = PhoneNumber.createNullable(dto.fax);
|
||||||
|
const postalAddressOrError = PostalAddress.create({
|
||||||
|
street: dto.street,
|
||||||
|
city: dto.city,
|
||||||
|
state: dto.state,
|
||||||
|
postalCode: dto.postal_code,
|
||||||
|
country: dto.country,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = Result.combine([
|
||||||
|
tinOrError,
|
||||||
|
emailOrError,
|
||||||
|
phoneOrError,
|
||||||
|
faxOrError,
|
||||||
|
postalAddressOrError,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (result.isFailure) {
|
||||||
|
return Result.fail(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatedData: IAccountProps = {
|
||||||
|
status: AccountStatus.createInactive(),
|
||||||
|
isFreelancer: dto.is_freelancer,
|
||||||
|
name: dto.name,
|
||||||
|
tradeName: dto.trade_name ? Maybe.some(dto.trade_name) : Maybe.none(),
|
||||||
|
tin: tinOrError.data,
|
||||||
|
address: postalAddressOrError.data,
|
||||||
|
email: emailOrError.data,
|
||||||
|
phone: phoneOrError.data,
|
||||||
|
fax: faxOrError.data,
|
||||||
|
website: dto.website ? Maybe.some(dto.website) : Maybe.none(),
|
||||||
|
legalRecord: dto.legal_record,
|
||||||
|
defaultTax: dto.default_tax,
|
||||||
|
langCode: dto.lang_code,
|
||||||
|
currencyCode: dto.currency_code,
|
||||||
|
logo: dto.logo ? Maybe.some(dto.logo) : Maybe.none(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
const message = errors.map((err) => err.message).toString();
|
||||||
|
return Result.fail(new Error(message));
|
||||||
|
}
|
||||||
|
return Result.ok(validatedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { Result } from "@common/helpers";
|
||||||
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { logger } from "@common/infrastructure/logger";
|
||||||
|
import { Account, IAccountService } from "@contexts/accounts/domain";
|
||||||
|
|
||||||
|
export class GetAccountsUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly accountService: IAccountService,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(accountID: UniqueID): Promise<Result<Account, Error>> {
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
return await this.accountService.findAccountById(accountID, transaction);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
logger.error(error as Error);
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,4 @@
|
|||||||
export * from "./list-accounts";
|
export * from "./create-account.use-case";
|
||||||
|
export * from "./get-account.use-case";
|
||||||
|
export * from "./list-accounts.use-case";
|
||||||
|
export * from "./update-account.use-case";
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { Collection, Result } from "@common/helpers";
|
||||||
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { logger } from "@common/infrastructure/logger";
|
||||||
|
import { Account, IAccountService } from "@contexts/accounts/domain";
|
||||||
|
|
||||||
|
export class ListAccountsUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly accountService: IAccountService,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(): Promise<Result<Collection<Account>, Error>> {
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
return await this.accountService.findAccounts(transaction);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
logger.error(error as Error);
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from "./list-accounts.use-case";
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Collection, Result } from "@common/helpers";
|
|
||||||
import { ITransactionManager } from "@common/infrastructure/database";
|
|
||||||
import { Account } from "@contexts/accounts/domain";
|
|
||||||
import { IAccountService } from "@contexts/accounts/domain/services/account-service.interface";
|
|
||||||
|
|
||||||
export class ListAccountsUseCase {
|
|
||||||
constructor(
|
|
||||||
private readonly accountService: IAccountService,
|
|
||||||
private readonly transactionManager: ITransactionManager
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public execute(): Promise<Result<Collection<Account>, Error>> {
|
|
||||||
return this.transactionManager.complete((transaction) => {
|
|
||||||
return this.accountService.findAccounts(transaction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { Result } from "@common/helpers";
|
||||||
|
import { TransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { AccountService } from "../domain";
|
||||||
|
import { UpdateAccountUseCase } from "./update-account.use-case";
|
||||||
|
|
||||||
|
const mockAccountService: AccountService = {
|
||||||
|
updateAccountById: jest.fn(),
|
||||||
|
} as unknown as AccountService;
|
||||||
|
|
||||||
|
const mockTransactionManager: TransactionManager = {
|
||||||
|
complete(work: (transaction: any) => Promise<any>): void {
|
||||||
|
jest.fn();
|
||||||
|
},
|
||||||
|
} as unknown as TransactionManager;
|
||||||
|
|
||||||
|
const id = UniqueID.create("", true).data;
|
||||||
|
|
||||||
|
describe("UpdateAccountUseCase", () => {
|
||||||
|
let updateAccountUseCase: UpdateAccountUseCase;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
updateAccountUseCase = new UpdateAccountUseCase(mockAccountService, mockTransactionManager);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("debería actualizar una cuenta y retornar un DTO", async () => {
|
||||||
|
const mockUpdatedAccount = { id: "123", name: "Nuevo Nombre" };
|
||||||
|
(mockAccountService.updateAccountById as jest.Mock).mockResolvedValue(
|
||||||
|
Result.ok(mockUpdatedAccount)
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await updateAccountUseCase.execute(id, { name: "Nuevo Nombre" });
|
||||||
|
expect(result.isSuccess).toBe(true);
|
||||||
|
expect(result.data.name).toBe("Nuevo Nombre");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("debería retornar error si la actualización falla", async () => {
|
||||||
|
(mockAccountService.updateAccountById as jest.Mock).mockResolvedValue(
|
||||||
|
Result.fail(new Error("Account not found"))
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await updateAccountUseCase.execute(id, { name: "Nuevo Nombre" });
|
||||||
|
expect(result.isFailure).toBe(true);
|
||||||
|
expect(result.error.message).toBe("Account not found");
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import { EmailAddress, PhoneNumber, PostalAddress, TINNumber, UniqueID } from "@common/domain";
|
||||||
|
|
||||||
|
import { Maybe, Result } from "@common/helpers";
|
||||||
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { logger } from "@common/infrastructure/logger";
|
||||||
|
import { Account, IAccountProps, IAccountService } from "@contexts/accounts/domain";
|
||||||
|
import { IUpdateAccountRequestDTO } from "../presentation";
|
||||||
|
|
||||||
|
export class UpdateAccountUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly accountService: IAccountService,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(
|
||||||
|
accountID: UniqueID,
|
||||||
|
dto: Partial<IUpdateAccountRequestDTO>
|
||||||
|
): Promise<Result<Account, Error>> {
|
||||||
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
|
try {
|
||||||
|
const validOrErrors = this.validateAccountData(dto);
|
||||||
|
if (validOrErrors.isFailure) {
|
||||||
|
return Result.fail(validOrErrors.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = validOrErrors.data;
|
||||||
|
|
||||||
|
// Update account with dto
|
||||||
|
return await this.accountService.updateAccountById(accountID, data, transaction);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
logger.error(error as Error);
|
||||||
|
return Result.fail(error as Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateAccountData(
|
||||||
|
dto: Partial<IUpdateAccountRequestDTO>
|
||||||
|
): Result<Partial<IAccountProps>, Error> {
|
||||||
|
const errors: Error[] = [];
|
||||||
|
const validatedData: Partial<IAccountProps> = {};
|
||||||
|
|
||||||
|
if (dto.is_freelancer) {
|
||||||
|
validatedData.isFreelancer = dto.is_freelancer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.name) {
|
||||||
|
validatedData.name = dto.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.trade_name) {
|
||||||
|
validatedData.tradeName = Maybe.some(dto.trade_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.tin) {
|
||||||
|
const tinOrError = TINNumber.create(dto.tin);
|
||||||
|
if (tinOrError.isFailure) errors.push(tinOrError.error);
|
||||||
|
else validatedData.tin = tinOrError.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.email) {
|
||||||
|
const emailOrError = EmailAddress.create(dto.email);
|
||||||
|
if (emailOrError.isFailure) errors.push(emailOrError.error);
|
||||||
|
else validatedData.email = emailOrError.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.phone) {
|
||||||
|
const phoneOrError = PhoneNumber.create(dto.phone);
|
||||||
|
if (phoneOrError.isFailure) errors.push(phoneOrError.error);
|
||||||
|
else validatedData.phone = phoneOrError.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.fax) {
|
||||||
|
const faxOrError = PhoneNumber.create(dto.fax);
|
||||||
|
if (faxOrError.isFailure) errors.push(faxOrError.error);
|
||||||
|
else validatedData.fax = Maybe.some(faxOrError.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.street || dto.city || dto.state || dto.postal_code || dto.country) {
|
||||||
|
const postalAddressOrError = PostalAddress.create({
|
||||||
|
street: dto.street ?? "",
|
||||||
|
city: dto.city ?? "",
|
||||||
|
state: dto.state ?? "",
|
||||||
|
postalCode: dto.postal_code ?? "",
|
||||||
|
country: dto.country ?? "",
|
||||||
|
});
|
||||||
|
if (postalAddressOrError.isFailure) errors.push(postalAddressOrError.error);
|
||||||
|
else validatedData.address = postalAddressOrError.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.website) {
|
||||||
|
validatedData.website = Maybe.some(dto.website);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.legal_record) {
|
||||||
|
validatedData.legalRecord = dto.legal_record;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.default_tax) {
|
||||||
|
validatedData.defaultTax = dto.default_tax;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.lang_code) {
|
||||||
|
validatedData.langCode = dto.lang_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.currency_code) {
|
||||||
|
validatedData.currencyCode = dto.currency_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.logo) {
|
||||||
|
validatedData.logo = Maybe.some(dto.logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
const message = errors.map((err) => err.message).toString();
|
||||||
|
return Result.fail(new Error(message));
|
||||||
|
}
|
||||||
|
return Result.ok(validatedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,8 +7,11 @@ import {
|
|||||||
UniqueID,
|
UniqueID,
|
||||||
} from "@common/domain";
|
} from "@common/domain";
|
||||||
import { Maybe, Result } from "@common/helpers";
|
import { Maybe, Result } from "@common/helpers";
|
||||||
|
import { AccountStatus } from "../value-objects";
|
||||||
|
|
||||||
export interface IAccountProps {
|
export interface IAccountProps {
|
||||||
|
status: AccountStatus;
|
||||||
|
|
||||||
isFreelancer: boolean;
|
isFreelancer: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
tin: TINNumber;
|
tin: TINNumber;
|
||||||
@ -17,7 +20,7 @@ export interface IAccountProps {
|
|||||||
phone: PhoneNumber;
|
phone: PhoneNumber;
|
||||||
legalRecord: string;
|
legalRecord: string;
|
||||||
defaultTax: number;
|
defaultTax: number;
|
||||||
status: string;
|
|
||||||
langCode: string;
|
langCode: string;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
|
|
||||||
@ -29,6 +32,7 @@ export interface IAccountProps {
|
|||||||
|
|
||||||
export interface IAccount {
|
export interface IAccount {
|
||||||
id: UniqueID;
|
id: UniqueID;
|
||||||
|
status: AccountStatus;
|
||||||
name: string;
|
name: string;
|
||||||
tin: TINNumber;
|
tin: TINNumber;
|
||||||
address: PostalAddress;
|
address: PostalAddress;
|
||||||
@ -36,6 +40,7 @@ export interface IAccount {
|
|||||||
phone: PhoneNumber;
|
phone: PhoneNumber;
|
||||||
legalRecord: string;
|
legalRecord: string;
|
||||||
defaultTax: number;
|
defaultTax: number;
|
||||||
|
|
||||||
langCode: string;
|
langCode: string;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
|
|
||||||
@ -47,6 +52,9 @@ export interface IAccount {
|
|||||||
isAccount: boolean;
|
isAccount: boolean;
|
||||||
isFreelancer: boolean;
|
isFreelancer: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|
||||||
|
activate(): boolean;
|
||||||
|
deactivate(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Account extends AggregateRoot<IAccountProps> implements IAccount {
|
export class Account extends AggregateRoot<IAccountProps> implements IAccount {
|
||||||
@ -64,6 +72,55 @@ export class Account extends AggregateRoot<IAccountProps> implements IAccount {
|
|||||||
return Result.ok(account);
|
return Result.ok(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static update(oldAccount: Account, data: Partial<IAccountProps>): Result<Account, Error> {
|
||||||
|
const updatedPostalAddress = PostalAddress.update(oldAccount.address, data.address ?? {}).data;
|
||||||
|
|
||||||
|
return Account.create(
|
||||||
|
{
|
||||||
|
isFreelancer: data.isFreelancer ?? oldAccount.isFreelancer,
|
||||||
|
|
||||||
|
name: data.name ?? oldAccount.name,
|
||||||
|
tin: data.tin ?? oldAccount.tin,
|
||||||
|
|
||||||
|
address: updatedPostalAddress,
|
||||||
|
email: data.email ?? oldAccount.email,
|
||||||
|
phone: data.phone ?? oldAccount.phone,
|
||||||
|
|
||||||
|
legalRecord: data.legalRecord ?? oldAccount.legalRecord,
|
||||||
|
defaultTax: data.defaultTax ?? oldAccount.defaultTax,
|
||||||
|
status: oldAccount.props.status,
|
||||||
|
langCode: data.langCode ?? oldAccount.langCode,
|
||||||
|
currencyCode: data.currencyCode ?? oldAccount.currencyCode,
|
||||||
|
|
||||||
|
tradeName: data.tradeName ?? oldAccount.tradeName,
|
||||||
|
website: data.website ?? oldAccount.website,
|
||||||
|
fax: data.fax ?? oldAccount.fax,
|
||||||
|
logo: data.logo ?? oldAccount.logo,
|
||||||
|
},
|
||||||
|
oldAccount.id
|
||||||
|
).getOrElse(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
activate() {
|
||||||
|
if (!this.props.status.canTransitionTo("active")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.props.status = AccountStatus.createActive();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate() {
|
||||||
|
if (!this.props.status.canTransitionTo("inactive")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.props.status = AccountStatus.createInactive();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get status() {
|
||||||
|
return this.props.status;
|
||||||
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
return this.props.name;
|
return this.props.name;
|
||||||
}
|
}
|
||||||
@ -125,6 +182,6 @@ export class Account extends AggregateRoot<IAccountProps> implements IAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isActive(): boolean {
|
get isActive(): boolean {
|
||||||
return this.props.status === "active";
|
return this.props.status.equals(AccountStatus.createActive());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export * from "./aggregates";
|
export * from "./aggregates";
|
||||||
|
|
||||||
export * from "./repositories";
|
export * from "./repositories";
|
||||||
|
export * from "./services";
|
||||||
|
export * from "./value-objects";
|
||||||
|
|||||||
@ -3,7 +3,11 @@ import { Collection, Result } from "@common/helpers";
|
|||||||
import { Account } from "../aggregates";
|
import { Account } from "../aggregates";
|
||||||
|
|
||||||
export interface IAccountRepository {
|
export interface IAccountRepository {
|
||||||
|
accountExists(id: UniqueID, transaction?: any): Promise<Result<boolean, Error>>;
|
||||||
findAll(transaction?: any): Promise<Result<Collection<Account>, Error>>;
|
findAll(transaction?: any): Promise<Result<Collection<Account>, Error>>;
|
||||||
findById(id: UniqueID, transaction?: any): Promise<Result<Account, Error>>;
|
findById(id: UniqueID, transaction?: any): Promise<Result<Account, Error>>;
|
||||||
findByEmail(email: EmailAddress, transaction?: any): Promise<Result<Account, Error>>;
|
findByEmail(email: EmailAddress, transaction?: any): Promise<Result<Account, Error>>;
|
||||||
|
|
||||||
|
create(account: Account, transaction?: any): Promise<void>;
|
||||||
|
update(account: Account, transaction?: any): Promise<void>;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { Collection, Result } from "@common/helpers";
|
||||||
|
import { Account, IAccountProps } from "../aggregates";
|
||||||
|
|
||||||
|
export interface IAccountService {
|
||||||
|
findAccounts(transaction?: any): Promise<Result<Collection<Account>, Error>>;
|
||||||
|
findAccountById(accountId: UniqueID, transaction?: any): Promise<Result<Account>>;
|
||||||
|
|
||||||
|
updateAccountById(
|
||||||
|
accountId: UniqueID,
|
||||||
|
data: Partial<IAccountProps>,
|
||||||
|
transaction?: any
|
||||||
|
): Promise<Result<Account, Error>>;
|
||||||
|
|
||||||
|
createAccount(
|
||||||
|
accountId: UniqueID,
|
||||||
|
data: IAccountProps,
|
||||||
|
transaction?: any
|
||||||
|
): Promise<Result<Account, Error>>;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
// Pruebas unitarias: AccountService.test.ts
|
||||||
|
import { Account } from "../domain/Account";
|
||||||
|
import { IAccountRepository } from "../repositories/AccountRepository";
|
||||||
|
import { AccountService } from "../services/AccountService";
|
||||||
|
|
||||||
|
const mockAccountRepository: IAccountRepository = {
|
||||||
|
findById: jest.fn(),
|
||||||
|
save: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("AccountService", () => {
|
||||||
|
let accountService: AccountService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
accountService = new AccountService(mockAccountRepository);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("debería actualizar una cuenta existente", async () => {
|
||||||
|
const existingAccount = new Account(
|
||||||
|
{
|
||||||
|
/* datos simulados */
|
||||||
|
},
|
||||||
|
"123"
|
||||||
|
);
|
||||||
|
(mockAccountRepository.findById as jest.Mock).mockResolvedValue(existingAccount);
|
||||||
|
(mockAccountRepository.save as jest.Mock).mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await accountService.updateAccountById("123", { name: "Nuevo Nombre" });
|
||||||
|
expect(result.isSuccess).toBe(true);
|
||||||
|
expect(result.data.name).toBe("Nuevo Nombre");
|
||||||
|
expect(mockAccountRepository.save).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("debería retornar error si la cuenta no existe", async () => {
|
||||||
|
(mockAccountRepository.findById as jest.Mock).mockResolvedValue(null);
|
||||||
|
|
||||||
|
const result = await accountService.updateAccountById("123", { name: "Nuevo Nombre" });
|
||||||
|
expect(result.isFailure).toBe(true);
|
||||||
|
expect(result.error.message).toBe("Account not found");
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { Collection, Result } from "@common/helpers";
|
||||||
|
import { Transaction } from "sequelize";
|
||||||
|
import { Account, IAccountProps } from "../aggregates";
|
||||||
|
import { IAccountRepository } from "../repositories";
|
||||||
|
import { IAccountService } from "./account-service.interface";
|
||||||
|
|
||||||
|
export class AccountService implements IAccountService {
|
||||||
|
constructor(private readonly repo: IAccountRepository) {}
|
||||||
|
|
||||||
|
async findAccounts(transaction?: Transaction): Promise<Result<Collection<Account>, Error>> {
|
||||||
|
const accountsOrError = await this.repo.findAll(transaction);
|
||||||
|
if (accountsOrError.isFailure) {
|
||||||
|
return Result.fail(accountsOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solo devolver usuarios activos
|
||||||
|
//const allAccounts = accountsOrError.data.filter((account) => account.isActive);
|
||||||
|
//return Result.ok(new Collection(allAccounts));
|
||||||
|
|
||||||
|
return accountsOrError;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAccountById(accountId: UniqueID, transaction?: Transaction): Promise<Result<Account>> {
|
||||||
|
return await this.repo.findById(accountId, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateAccountById(
|
||||||
|
accountId: UniqueID,
|
||||||
|
data: Partial<IAccountProps>,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Account, Error>> {
|
||||||
|
// Verificar si la cuenta existe
|
||||||
|
const accountOrError = await this.repo.findById(accountId, transaction);
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return Result.fail(new Error("Account not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedAccountOrError = Account.update(accountOrError.data, data);
|
||||||
|
if (updatedAccountOrError.isFailure) {
|
||||||
|
return Result.fail(
|
||||||
|
new Error(`Error updating account: ${updatedAccountOrError.error.message}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAccount = updatedAccountOrError.data;
|
||||||
|
|
||||||
|
await this.repo.update(updateAccount, transaction);
|
||||||
|
return Result.ok(updateAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAccount(
|
||||||
|
accountId: UniqueID,
|
||||||
|
data: IAccountProps,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Account, Error>> {
|
||||||
|
// Verificar si la cuenta existe
|
||||||
|
const accountOrError = await this.repo.findById(accountId, transaction);
|
||||||
|
if (accountOrError.isSuccess) {
|
||||||
|
return Result.fail(new Error("Account exists"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAccountOrError = Account.create(data, accountId);
|
||||||
|
if (newAccountOrError.isFailure) {
|
||||||
|
return Result.fail(new Error(`Error creating account: ${newAccountOrError.error.message}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAccount = newAccountOrError.data;
|
||||||
|
|
||||||
|
await this.repo.create(newAccount, transaction);
|
||||||
|
return Result.ok(newAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async activateAccount(id: UniqueID, transaction?: Transaction): Promise<Result<Account, Error>> {
|
||||||
|
const accountOrError = await this.repo.findById(id, transaction);
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return Result.fail(new Error("Account not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = accountOrError.data;
|
||||||
|
if (account.activate()) {
|
||||||
|
await this.repo.update(account, transaction);
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
return Result.fail(new Error("Error activating account"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async deactivateAccount(
|
||||||
|
id: UniqueID,
|
||||||
|
transaction?: Transaction
|
||||||
|
): Promise<Result<Account, Error>> {
|
||||||
|
const accountOrError = await this.repo.findById(id, transaction);
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return Result.fail(new Error("Account not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = accountOrError.data;
|
||||||
|
if (account.deactivate()) {
|
||||||
|
await this.repo.update(account, transaction);
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
return Result.fail(new Error("Error deactivating account"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { UniqueID } from "@common/domain";
|
|
||||||
import { Collection, Result } from "@common/helpers";
|
|
||||||
import { Account } from "../aggregates";
|
|
||||||
|
|
||||||
export interface IAccountService {
|
|
||||||
findAccounts(transaction?: any): Promise<Result<Collection<Account>, Error>>;
|
|
||||||
findAccountById(userId: UniqueID, transaction?: any): Promise<Result<Account>>;
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import { UniqueID } from "@common/domain";
|
|
||||||
import { Collection, Result } from "@common/helpers";
|
|
||||||
import { Account, IAccountRepository } from "..";
|
|
||||||
import { IAccountService } from "./account-service.interface";
|
|
||||||
|
|
||||||
export class AccountService implements IAccountService {
|
|
||||||
constructor(private readonly accountRepository: IAccountRepository) {}
|
|
||||||
|
|
||||||
async findAccounts(transaction?: any): Promise<Result<Collection<Account>, Error>> {
|
|
||||||
const accountsOrError = await this.accountRepository.findAll(transaction);
|
|
||||||
if (accountsOrError.isFailure) {
|
|
||||||
return Result.fail(accountsOrError.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solo devolver usuarios activos
|
|
||||||
const activeAccounts = accountsOrError.data.filter((account) => account.isActive);
|
|
||||||
return Result.ok(new Collection(activeAccounts));
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAccountById(accountId: UniqueID, transaction?: any): Promise<Result<Account>> {
|
|
||||||
return await this.accountRepository.findById(accountId, transaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./account-service.interface";
|
||||||
|
export * from "./account.service";
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import { ValueObject } from "@common/domain";
|
||||||
|
import { Result } from "@common/helpers";
|
||||||
|
|
||||||
|
interface IAccountStatusProps {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ACCOUNT_STATUS {
|
||||||
|
INACTIVE = "inactive",
|
||||||
|
ACTIVE = "active",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccountStatus extends ValueObject<IAccountStatusProps> {
|
||||||
|
private static readonly ALLOWED_STATUSES = ["inactive", "active"];
|
||||||
|
|
||||||
|
private static readonly TRANSITIONS: Record<string, string[]> = {
|
||||||
|
inactive: [ACCOUNT_STATUS.ACTIVE],
|
||||||
|
active: [ACCOUNT_STATUS.INACTIVE],
|
||||||
|
};
|
||||||
|
|
||||||
|
static create(value: string): Result<AccountStatus, Error> {
|
||||||
|
if (!this.ALLOWED_STATUSES.includes(value)) {
|
||||||
|
return Result.fail(new Error(`Estado de la cuenta no válido: ${value}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(
|
||||||
|
value === "active" ? AccountStatus.createActive() : AccountStatus.createInactive()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createInactive(): AccountStatus {
|
||||||
|
return new AccountStatus({ value: ACCOUNT_STATUS.INACTIVE });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createActive(): AccountStatus {
|
||||||
|
return new AccountStatus({ value: ACCOUNT_STATUS.ACTIVE });
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(): string {
|
||||||
|
return this.props.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
canTransitionTo(nextStatus: string): boolean {
|
||||||
|
return AccountStatus.TRANSITIONS[this.props.value].includes(nextStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionTo(nextStatus: string): Result<AccountStatus, Error> {
|
||||||
|
if (!this.canTransitionTo(nextStatus)) {
|
||||||
|
return Result.fail(
|
||||||
|
new Error(`Transición no permitida de ${this.props.value} a ${nextStatus}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return AccountStatus.create(nextStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return this.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./account-status";
|
||||||
@ -5,7 +5,7 @@ import {
|
|||||||
MapperParamsType,
|
MapperParamsType,
|
||||||
SequelizeMapper,
|
SequelizeMapper,
|
||||||
} from "@common/infrastructure/sequelize/sequelize-mapper";
|
} from "@common/infrastructure/sequelize/sequelize-mapper";
|
||||||
import { Account } from "@contexts/accounts/domain/";
|
import { Account, AccountStatus } from "@contexts/accounts/domain/";
|
||||||
import { AccountCreationAttributes, AccountModel } from "../sequelize/account.model";
|
import { AccountCreationAttributes, AccountModel } from "../sequelize/account.model";
|
||||||
|
|
||||||
export interface IAccountMapper
|
export interface IAccountMapper
|
||||||
@ -17,6 +17,7 @@ export class AccountMapper
|
|||||||
{
|
{
|
||||||
public mapToDomain(source: AccountModel, params?: MapperParamsType): Result<Account, Error> {
|
public mapToDomain(source: AccountModel, params?: MapperParamsType): Result<Account, Error> {
|
||||||
const idOrError = UniqueID.create(source.id);
|
const idOrError = UniqueID.create(source.id);
|
||||||
|
const statusOrError = AccountStatus.create(source.status);
|
||||||
const tinOrError = TINNumber.create(source.tin);
|
const tinOrError = TINNumber.create(source.tin);
|
||||||
const emailOrError = EmailAddress.create(source.email);
|
const emailOrError = EmailAddress.create(source.email);
|
||||||
const phoneOrError = PhoneNumber.create(source.phone);
|
const phoneOrError = PhoneNumber.create(source.phone);
|
||||||
@ -31,6 +32,7 @@ export class AccountMapper
|
|||||||
|
|
||||||
const result = Result.combine([
|
const result = Result.combine([
|
||||||
idOrError,
|
idOrError,
|
||||||
|
statusOrError,
|
||||||
tinOrError,
|
tinOrError,
|
||||||
emailOrError,
|
emailOrError,
|
||||||
phoneOrError,
|
phoneOrError,
|
||||||
@ -44,6 +46,7 @@ export class AccountMapper
|
|||||||
|
|
||||||
return Account.create(
|
return Account.create(
|
||||||
{
|
{
|
||||||
|
status: statusOrError.data,
|
||||||
isFreelancer: source.is_freelancer,
|
isFreelancer: source.is_freelancer,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
tradeName: source.trade_name ? Maybe.some(source.trade_name) : Maybe.none(),
|
tradeName: source.trade_name ? Maybe.some(source.trade_name) : Maybe.none(),
|
||||||
@ -55,7 +58,6 @@ export class AccountMapper
|
|||||||
website: source.website ? Maybe.some(source.website) : Maybe.none(),
|
website: source.website ? Maybe.some(source.website) : Maybe.none(),
|
||||||
legalRecord: source.legal_record,
|
legalRecord: source.legal_record,
|
||||||
defaultTax: source.default_tax,
|
defaultTax: source.default_tax,
|
||||||
status: source.status,
|
|
||||||
langCode: source.lang_code,
|
langCode: source.lang_code,
|
||||||
currencyCode: source.currency_code,
|
currencyCode: source.currency_code,
|
||||||
logo: source.logo ? Maybe.some(source.logo) : Maybe.none(),
|
logo: source.logo ? Maybe.some(source.logo) : Maybe.none(),
|
||||||
@ -64,11 +66,8 @@ export class AccountMapper
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapToPersistence(
|
public mapToPersistence(source: Account, params?: MapperParamsType): AccountCreationAttributes {
|
||||||
source: Account,
|
return {
|
||||||
params?: MapperParamsType
|
|
||||||
): Result<AccountCreationAttributes, Error> {
|
|
||||||
return Result.ok({
|
|
||||||
id: source.id.toString(),
|
id: source.id.toString(),
|
||||||
is_freelancer: source.isFreelancer,
|
is_freelancer: source.isFreelancer,
|
||||||
name: source.name,
|
name: source.name,
|
||||||
@ -92,7 +91,7 @@ export class AccountMapper
|
|||||||
lang_code: source.langCode,
|
lang_code: source.langCode,
|
||||||
currency_code: source.currencyCode,
|
currency_code: source.currencyCode,
|
||||||
logo: source.logo.getOrUndefined(),
|
logo: source.logo.getOrUndefined(),
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,6 +63,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
trade_name: {
|
trade_name: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
tin: {
|
tin: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
@ -104,10 +105,12 @@ export default (sequelize: Sequelize) => {
|
|||||||
fax: {
|
fax: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
validate: {
|
validate: {
|
||||||
isUrl: true,
|
isUrl: true,
|
||||||
},
|
},
|
||||||
@ -126,6 +129,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
logo: {
|
logo: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
lang_code: {
|
lang_code: {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { EmailAddress, UniqueID } from "@common/domain";
|
|||||||
import { Collection, Result } from "@common/helpers";
|
import { Collection, Result } from "@common/helpers";
|
||||||
import { SequelizeRepository } from "@common/infrastructure";
|
import { SequelizeRepository } from "@common/infrastructure";
|
||||||
import { Account } from "@contexts/accounts/domain";
|
import { Account } from "@contexts/accounts/domain";
|
||||||
import { IAccountRepository } from "@contexts/accounts/domain/repositories/company-repository.interface";
|
import { IAccountRepository } from "@contexts/accounts/domain/repositories/account-repository.interface";
|
||||||
import { Transaction } from "sequelize";
|
import { Transaction } from "sequelize";
|
||||||
import { accountMapper, IAccountMapper } from "../mappers/account.mapper";
|
import { accountMapper, IAccountMapper } from "../mappers/account.mapper";
|
||||||
import { AccountModel } from "./account.model";
|
import { AccountModel } from "./account.model";
|
||||||
@ -26,6 +26,16 @@ class AccountRepository extends SequelizeRepository<Account> implements IAccount
|
|||||||
this._mapper = mapper;
|
this._mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async accountExists(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
|
||||||
|
try {
|
||||||
|
const _account = await this._getById(AccountModel, id, {}, transaction);
|
||||||
|
|
||||||
|
return Result.ok(Boolean(id.equals(_account.id)));
|
||||||
|
} catch (error: any) {
|
||||||
|
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async findAll(transaction?: Transaction): Promise<Result<Collection<Account>, Error>> {
|
async findAll(transaction?: Transaction): Promise<Result<Collection<Account>, Error>> {
|
||||||
try {
|
try {
|
||||||
const rawAccounts: any = await this._findAll(AccountModel, {}, transaction);
|
const rawAccounts: any = await this._findAll(AccountModel, {}, transaction);
|
||||||
@ -76,6 +86,16 @@ class AccountRepository extends SequelizeRepository<Account> implements IAccount
|
|||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
return this._handleDatabaseError(error, this._customErrorMapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async create(account: Account, transaction?: Transaction): Promise<void> {
|
||||||
|
const accountData = this._mapper.mapToPersistence(account);
|
||||||
|
await this._save(AccountModel, account.id, accountData, {}, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(account: Account, transaction?: Transaction): Promise<void> {
|
||||||
|
const accountData = this._mapper.mapToPersistence(account);
|
||||||
|
await this._save(AccountModel, account.id, accountData, {}, transaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountRepository = new AccountRepository(accountMapper);
|
const accountRepository = new AccountRepository(accountMapper);
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { IAccountRepository } from "@contexts/accounts/domain/repositories/company-repository.interface";
|
import { IAccountRepository } from "@contexts/accounts/domain/repositories/account-repository.interface";
|
||||||
import { accountRepository } from "./account.repository";
|
import { accountRepository } from "./account.repository";
|
||||||
|
|
||||||
export * from "./account.model";
|
export * from "./account.model";
|
||||||
|
|
||||||
|
export * from "./account.repository";
|
||||||
|
|
||||||
export const createAccountRepository = (): IAccountRepository => {
|
export const createAccountRepository = (): IAccountRepository => {
|
||||||
return accountRepository;
|
return accountRepository;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { ExpressController } from "@common/presentation";
|
||||||
|
import { CreateAccountUseCase } from "../../../application";
|
||||||
|
import { ICreateAccountRequestDTO } from "../../dto";
|
||||||
|
import { ICreateAccountPresenter } from "./create-account.presenter";
|
||||||
|
|
||||||
|
export class CreateAccountController extends ExpressController {
|
||||||
|
public constructor(
|
||||||
|
private readonly createAccount: CreateAccountUseCase,
|
||||||
|
private readonly presenter: ICreateAccountPresenter
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeImpl() {
|
||||||
|
const createDTO: ICreateAccountRequestDTO = this.req.body;
|
||||||
|
|
||||||
|
// Validar ID
|
||||||
|
const accountIdOrError = UniqueID.create(createDTO.id);
|
||||||
|
if (accountIdOrError.isFailure) return this.invalidInputError("Account ID not valid");
|
||||||
|
|
||||||
|
const accountOrError = await this.createAccount.execute(accountIdOrError.data, createDTO);
|
||||||
|
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return this.handleError(accountOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ok(this.presenter.toDTO(accountOrError.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { ensureBoolean, ensureNumber, ensureString } from "@common/helpers";
|
||||||
|
import { Account } from "@contexts/accounts/domain";
|
||||||
|
import { ICreateAccountResponseDTO } from "../../dto";
|
||||||
|
|
||||||
|
export interface ICreateAccountPresenter {
|
||||||
|
toDTO: (account: Account) => ICreateAccountResponseDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createAccountPresenter: ICreateAccountPresenter = {
|
||||||
|
toDTO: (account: Account): ICreateAccountResponseDTO => ({
|
||||||
|
id: ensureString(account.id.toString()),
|
||||||
|
|
||||||
|
is_freelancer: ensureBoolean(account.isFreelancer),
|
||||||
|
name: ensureString(account.name),
|
||||||
|
trade_name: ensureString(account.tradeName.getOrUndefined()),
|
||||||
|
tin: ensureString(account.tin.toString()),
|
||||||
|
|
||||||
|
street: ensureString(account.address.street),
|
||||||
|
city: ensureString(account.address.city),
|
||||||
|
state: ensureString(account.address.state),
|
||||||
|
postal_code: ensureString(account.address.postalCode),
|
||||||
|
country: ensureString(account.address.country),
|
||||||
|
|
||||||
|
email: ensureString(account.email.toString()),
|
||||||
|
phone: ensureString(account.phone.toString()),
|
||||||
|
fax: ensureString(account.fax.getOrUndefined()?.toString()),
|
||||||
|
website: ensureString(account.website.getOrUndefined()),
|
||||||
|
|
||||||
|
legal_record: ensureString(account.legalRecord),
|
||||||
|
|
||||||
|
default_tax: ensureNumber(account.defaultTax),
|
||||||
|
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||||
|
lang_code: ensureString(account.langCode),
|
||||||
|
currency_code: ensureString(account.currencyCode),
|
||||||
|
logo: ensureString(account.logo.getOrUndefined()),
|
||||||
|
}),
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { SequelizeTransactionManager } from "@common/infrastructure";
|
||||||
|
import { CreateAccountUseCase } from "@contexts/accounts/application/create-account.use-case";
|
||||||
|
import { AccountService } from "@contexts/accounts/domain";
|
||||||
|
import { accountRepository } from "@contexts/accounts/infraestructure";
|
||||||
|
import { CreateAccountController } from "./create-account.controller";
|
||||||
|
import { createAccountPresenter } from "./create-account.presenter";
|
||||||
|
|
||||||
|
export const createAccountController = () => {
|
||||||
|
const transactionManager = new SequelizeTransactionManager();
|
||||||
|
const accountService = new AccountService(accountRepository);
|
||||||
|
|
||||||
|
const useCase = new CreateAccountUseCase(accountService, transactionManager);
|
||||||
|
const presenter = createAccountPresenter;
|
||||||
|
|
||||||
|
return new CreateAccountController(useCase, presenter);
|
||||||
|
};
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { ExpressController } from "@common/presentation";
|
||||||
|
import { GetAccountsUseCase } from "@contexts/accounts/application";
|
||||||
|
import { IGetAccountPresenter } from "./get-account.presenter";
|
||||||
|
|
||||||
|
export class GetAccountController extends ExpressController {
|
||||||
|
public constructor(
|
||||||
|
private readonly getAccount: GetAccountsUseCase,
|
||||||
|
private readonly presenter: IGetAccountPresenter
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeImpl() {
|
||||||
|
const { accountId } = this.req.params;
|
||||||
|
|
||||||
|
// Validar ID
|
||||||
|
const accountIdOrError = UniqueID.create(accountId);
|
||||||
|
if (accountIdOrError.isFailure) return this.invalidInputError("Account ID not valid");
|
||||||
|
|
||||||
|
const accountOrError = await this.getAccount.execute(accountIdOrError.data);
|
||||||
|
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return this.handleError(accountOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ok(this.presenter.toDTO(accountOrError.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { ensureBoolean, ensureNumber, ensureString } from "@common/helpers";
|
||||||
|
import { Account } from "@contexts/accounts/domain";
|
||||||
|
import { IGetAccountResponseDTO } from "../../dto";
|
||||||
|
|
||||||
|
export interface IGetAccountPresenter {
|
||||||
|
toDTO: (account: Account) => IGetAccountResponseDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAccountPresenter: IGetAccountPresenter = {
|
||||||
|
toDTO: (account: Account): IGetAccountResponseDTO => ({
|
||||||
|
id: ensureString(account.id.toString()),
|
||||||
|
|
||||||
|
is_freelancer: ensureBoolean(account.isFreelancer),
|
||||||
|
name: ensureString(account.name),
|
||||||
|
trade_name: ensureString(account.tradeName.getOrUndefined()),
|
||||||
|
tin: ensureString(account.tin.toString()),
|
||||||
|
|
||||||
|
street: ensureString(account.address.street),
|
||||||
|
city: ensureString(account.address.city),
|
||||||
|
state: ensureString(account.address.state),
|
||||||
|
postal_code: ensureString(account.address.postalCode),
|
||||||
|
country: ensureString(account.address.country),
|
||||||
|
|
||||||
|
email: ensureString(account.email.toString()),
|
||||||
|
phone: ensureString(account.phone.toString()),
|
||||||
|
fax: ensureString(account.fax.getOrUndefined()?.toString()),
|
||||||
|
website: ensureString(account.website.getOrUndefined()),
|
||||||
|
|
||||||
|
legal_record: ensureString(account.legalRecord),
|
||||||
|
|
||||||
|
default_tax: ensureNumber(account.defaultTax),
|
||||||
|
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||||
|
lang_code: ensureString(account.langCode),
|
||||||
|
currency_code: ensureString(account.currencyCode),
|
||||||
|
logo: ensureString(account.logo.getOrUndefined()),
|
||||||
|
}),
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { SequelizeTransactionManager } from "@common/infrastructure";
|
||||||
|
import { GetAccountsUseCase } from "@contexts/accounts/application";
|
||||||
|
import { AccountService } from "@contexts/accounts/domain";
|
||||||
|
import { accountRepository } from "@contexts/accounts/infraestructure";
|
||||||
|
import { GetAccountController } from "./get-account.controller";
|
||||||
|
import { getAccountPresenter } from "./get-account.presenter";
|
||||||
|
|
||||||
|
export const getAccountController = () => {
|
||||||
|
const transactionManager = new SequelizeTransactionManager();
|
||||||
|
const accountService = new AccountService(accountRepository);
|
||||||
|
|
||||||
|
const useCase = new GetAccountsUseCase(accountService, transactionManager);
|
||||||
|
const presenter = getAccountPresenter;
|
||||||
|
|
||||||
|
return new GetAccountController(useCase, presenter);
|
||||||
|
};
|
||||||
@ -1 +1,4 @@
|
|||||||
|
export * from "./create-account";
|
||||||
|
export * from "./get-account";
|
||||||
export * from "./list-accounts";
|
export * from "./list-accounts";
|
||||||
|
export * from "./update-account";
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { SequelizeTransactionManager } from "@common/infrastructure";
|
import { SequelizeTransactionManager } from "@common/infrastructure";
|
||||||
import { ListAccountsUseCase } from "@contexts/accounts/application/list-accounts/list-accounts.use-case";
|
import { ListAccountsUseCase } from "@contexts/accounts/application";
|
||||||
import { AccountService } from "@contexts/accounts/domain/services/company.service";
|
import { AccountService } from "@contexts/accounts/domain";
|
||||||
import { accountRepository } from "@contexts/accounts/infraestructure/sequelize/account.repository";
|
import { accountRepository } from "@contexts/accounts/infraestructure";
|
||||||
import { ListAccountsController } from "./list-accounts.controller";
|
import { ListAccountsController } from "./list-accounts.controller";
|
||||||
import { listAccountsPresenter } from "./list-accounts.presenter";
|
import { listAccountsPresenter } from "./list-accounts.presenter";
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ExpressController } from "@common/presentation";
|
import { ExpressController } from "@common/presentation";
|
||||||
import { ListAccountsUseCase } from "@contexts/accounts/application/list-accounts/list-accounts.use-case";
|
import { ListAccountsUseCase } from "@contexts/accounts/application";
|
||||||
import { IListAccountsPresenter } from "./list-accounts.presenter";
|
import { IListAccountsPresenter } from "./list-accounts.presenter";
|
||||||
|
|
||||||
export class ListAccountsController extends ExpressController {
|
export class ListAccountsController extends ExpressController {
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { SequelizeTransactionManager } from "@common/infrastructure";
|
||||||
|
import { UpdateAccountUseCase } from "@contexts/accounts/application";
|
||||||
|
import { AccountService } from "@contexts/accounts/domain";
|
||||||
|
import { accountRepository } from "@contexts/accounts/infraestructure";
|
||||||
|
import { UpdateAccountController } from "./update-account.controller";
|
||||||
|
import { updateAccountPresenter } from "./update-account.presenter";
|
||||||
|
|
||||||
|
export const updateAccountController = () => {
|
||||||
|
const transactionManager = new SequelizeTransactionManager();
|
||||||
|
const accountService = new AccountService(accountRepository);
|
||||||
|
|
||||||
|
const useCase = new UpdateAccountUseCase(accountService, transactionManager);
|
||||||
|
const presenter = updateAccountPresenter;
|
||||||
|
|
||||||
|
return new UpdateAccountController(useCase, presenter);
|
||||||
|
};
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { UniqueID } from "@common/domain";
|
||||||
|
import { ExpressController } from "@common/presentation";
|
||||||
|
import { UpdateAccountUseCase } from "@contexts/accounts/application/update-account.use-case";
|
||||||
|
import { IUpdateAccountRequestDTO } from "../../dto";
|
||||||
|
import { IUpdateAccountPresenter } from "./update-account.presenter";
|
||||||
|
|
||||||
|
export class UpdateAccountController extends ExpressController {
|
||||||
|
public constructor(
|
||||||
|
private readonly updateAccount: UpdateAccountUseCase,
|
||||||
|
private readonly presenter: IUpdateAccountPresenter
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeImpl() {
|
||||||
|
const { accountId } = this.req.params;
|
||||||
|
const updateDTO: IUpdateAccountRequestDTO = this.req.body;
|
||||||
|
|
||||||
|
// Validar ID
|
||||||
|
const accountIdOrError = UniqueID.create(accountId);
|
||||||
|
if (accountIdOrError.isFailure) return this.invalidInputError("Account ID not valid");
|
||||||
|
|
||||||
|
const accountOrError = await this.updateAccount.execute(accountIdOrError.data, updateDTO);
|
||||||
|
|
||||||
|
if (accountOrError.isFailure) {
|
||||||
|
return this.handleError(accountOrError.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ok(this.presenter.toDTO(accountOrError.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { ensureBoolean, ensureNumber, ensureString } from "@common/helpers";
|
||||||
|
import { Account } from "@contexts/accounts/domain";
|
||||||
|
import { IUpdateAccountResponseDTO } from "../../dto";
|
||||||
|
|
||||||
|
export interface IUpdateAccountPresenter {
|
||||||
|
toDTO: (account: Account) => IUpdateAccountResponseDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateAccountPresenter: IUpdateAccountPresenter = {
|
||||||
|
toDTO: (account: Account): IUpdateAccountResponseDTO => ({
|
||||||
|
id: ensureString(account.id.toString()),
|
||||||
|
|
||||||
|
is_freelancer: ensureBoolean(account.isFreelancer),
|
||||||
|
name: ensureString(account.name),
|
||||||
|
trade_name: ensureString(account.tradeName.getOrUndefined()),
|
||||||
|
tin: ensureString(account.tin.toString()),
|
||||||
|
|
||||||
|
street: ensureString(account.address.street),
|
||||||
|
city: ensureString(account.address.city),
|
||||||
|
state: ensureString(account.address.state),
|
||||||
|
postal_code: ensureString(account.address.postalCode),
|
||||||
|
country: ensureString(account.address.country),
|
||||||
|
|
||||||
|
email: ensureString(account.email.toString()),
|
||||||
|
phone: ensureString(account.phone.toString()),
|
||||||
|
fax: ensureString(account.fax.getOrUndefined()?.toString()),
|
||||||
|
website: ensureString(account.website.getOrUndefined()),
|
||||||
|
|
||||||
|
legal_record: ensureString(account.legalRecord),
|
||||||
|
|
||||||
|
default_tax: ensureNumber(account.defaultTax),
|
||||||
|
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||||
|
lang_code: ensureString(account.langCode),
|
||||||
|
currency_code: ensureString(account.currencyCode),
|
||||||
|
logo: ensureString(account.logo.getOrUndefined()),
|
||||||
|
}),
|
||||||
|
};
|
||||||
@ -1 +1,52 @@
|
|||||||
export interface IListAccountsRequestDTO {}
|
export interface IListAccountsRequestDTO {}
|
||||||
|
|
||||||
|
export interface ICreateAccountRequestDTO {
|
||||||
|
id: string;
|
||||||
|
is_freelancer: boolean;
|
||||||
|
name: string;
|
||||||
|
trade_name: string;
|
||||||
|
tin: string;
|
||||||
|
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
fax: string;
|
||||||
|
website: string;
|
||||||
|
|
||||||
|
legal_record: string;
|
||||||
|
|
||||||
|
default_tax: number;
|
||||||
|
lang_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUpdateAccountRequestDTO {
|
||||||
|
is_freelancer: boolean;
|
||||||
|
name: string;
|
||||||
|
trade_name: string;
|
||||||
|
tin: string;
|
||||||
|
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
fax: string;
|
||||||
|
website: string;
|
||||||
|
|
||||||
|
legal_record: string;
|
||||||
|
|
||||||
|
default_tax: number;
|
||||||
|
lang_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -25,3 +25,90 @@ export interface IListAccountsResponseDTO {
|
|||||||
currency_code: string;
|
currency_code: string;
|
||||||
logo: string;
|
logo: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IGetAccountResponseDTO {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
is_freelancer: boolean;
|
||||||
|
name: string;
|
||||||
|
trade_name: string;
|
||||||
|
tin: string;
|
||||||
|
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
fax: string;
|
||||||
|
website: string;
|
||||||
|
|
||||||
|
legal_record: string;
|
||||||
|
|
||||||
|
default_tax: number;
|
||||||
|
status: string;
|
||||||
|
lang_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreateAccountResponseDTO {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
is_freelancer: boolean;
|
||||||
|
name: string;
|
||||||
|
trade_name: string;
|
||||||
|
tin: string;
|
||||||
|
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
fax: string;
|
||||||
|
website: string;
|
||||||
|
|
||||||
|
legal_record: string;
|
||||||
|
|
||||||
|
default_tax: number;
|
||||||
|
status: string;
|
||||||
|
lang_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inferir el tipo en TypeScript desde el esquema Zod
|
||||||
|
//export type IUpdateAcccountResponseDTO = z.infer<typeof IUpdateAcccountResponseDTOSchema>;
|
||||||
|
|
||||||
|
export interface IUpdateAccountResponseDTO {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
is_freelancer: boolean;
|
||||||
|
name: string;
|
||||||
|
trade_name: string;
|
||||||
|
tin: string;
|
||||||
|
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
fax: string;
|
||||||
|
website: string;
|
||||||
|
|
||||||
|
legal_record: string;
|
||||||
|
|
||||||
|
default_tax: number;
|
||||||
|
status: string;
|
||||||
|
lang_code: string;
|
||||||
|
currency_code: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,87 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const ListAccountsSchema = z.object({});
|
export const ListAccountsSchema = z.object({});
|
||||||
|
|
||||||
|
export const IGetAcccountResponseDTOSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
|
||||||
|
is_freelancer: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
trade_name: z.string(),
|
||||||
|
tin: z.string(),
|
||||||
|
|
||||||
|
street: z.string(),
|
||||||
|
city: z.string(),
|
||||||
|
state: z.string(),
|
||||||
|
postal_code: z.string(),
|
||||||
|
country: z.string(),
|
||||||
|
|
||||||
|
email: z.string().email(), // Validación específica para email
|
||||||
|
phone: z.string(),
|
||||||
|
fax: z.string(),
|
||||||
|
website: z.string().url(), // Validación específica para URL
|
||||||
|
|
||||||
|
legal_record: z.string(),
|
||||||
|
|
||||||
|
default_tax: z.number(),
|
||||||
|
status: z.string(),
|
||||||
|
lang_code: z.string(),
|
||||||
|
currency_code: z.string(),
|
||||||
|
logo: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ICreateAcccountResponseDTOSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
|
||||||
|
is_freelancer: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
trade_name: z.string(),
|
||||||
|
tin: z.string(),
|
||||||
|
|
||||||
|
street: z.string(),
|
||||||
|
city: z.string(),
|
||||||
|
state: z.string(),
|
||||||
|
postal_code: z.string(),
|
||||||
|
country: z.string(),
|
||||||
|
|
||||||
|
email: z.string().email(), // Validación específica para email
|
||||||
|
phone: z.string(),
|
||||||
|
fax: z.string(),
|
||||||
|
website: z.string().url(), // Validación específica para URL
|
||||||
|
|
||||||
|
legal_record: z.string(),
|
||||||
|
|
||||||
|
default_tax: z.number(),
|
||||||
|
status: z.string(),
|
||||||
|
lang_code: z.string(),
|
||||||
|
currency_code: z.string(),
|
||||||
|
logo: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IUpdateAcccountResponseDTOSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
|
||||||
|
is_freelancer: z.boolean(),
|
||||||
|
name: z.string(),
|
||||||
|
trade_name: z.string(),
|
||||||
|
tin: z.string(),
|
||||||
|
|
||||||
|
street: z.string(),
|
||||||
|
city: z.string(),
|
||||||
|
state: z.string(),
|
||||||
|
postal_code: z.string(),
|
||||||
|
country: z.string(),
|
||||||
|
|
||||||
|
email: z.string().email(), // Validación específica para email
|
||||||
|
phone: z.string(),
|
||||||
|
fax: z.string(),
|
||||||
|
website: z.string().url(), // Validación específica para URL
|
||||||
|
|
||||||
|
legal_record: z.string(),
|
||||||
|
|
||||||
|
default_tax: z.number(),
|
||||||
|
status: z.string(),
|
||||||
|
lang_code: z.string(),
|
||||||
|
currency_code: z.string(),
|
||||||
|
logo: z.string(),
|
||||||
|
});
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
trade_name: {
|
trade_name: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
tin: {
|
tin: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
@ -108,10 +109,12 @@ export default (sequelize: Sequelize) => {
|
|||||||
fax: {
|
fax: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
validate: {
|
validate: {
|
||||||
isUrl: true,
|
isUrl: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export * from "./get-customer-invoice.use-case";
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from "./list-customers";
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from "./list-customers.use-case";
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Collection, Result } from "@common/helpers";
|
|
||||||
import { ITransactionManager } from "@common/infrastructure/database";
|
|
||||||
import { Customer } from "@contexts/customer-billing/domain/aggregates";
|
|
||||||
import { ICustomerService } from "@contexts/customer-billing/domain/services";
|
|
||||||
|
|
||||||
export class ListCustomersUseCase {
|
|
||||||
constructor(
|
|
||||||
private readonly customerService: ICustomerService,
|
|
||||||
private readonly transactionManager: ITransactionManager
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public execute(): Promise<Result<Collection<Customer>, Error>> {
|
|
||||||
return this.transactionManager.complete((transaction) => {
|
|
||||||
return this.customerService.findCustomer(transaction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./customer-invoices";
|
export * from "./get-customer-invoice.use-case";
|
||||||
export * from "./customers";
|
export * from "./list-customer-invoices-use-case";
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { Collection, Result } from "@common/helpers";
|
||||||
|
import { ITransactionManager } from "@common/infrastructure/database";
|
||||||
|
import { CustomerInvoice, ICustomerInvoiceService } from "../domain";
|
||||||
|
|
||||||
|
export class ListCustomerInvoicesUseCase {
|
||||||
|
constructor(
|
||||||
|
private readonly invoiceService: ICustomerInvoiceService,
|
||||||
|
private readonly transactionManager: ITransactionManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public execute(): Promise<Result<Collection<CustomerInvoice>, Error>> {
|
||||||
|
return this.transactionManager.complete((transaction) => {
|
||||||
|
return this.invoiceService.findCustomerInvoices(transaction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { AggregateRoot, UniqueID, UtcDate } from "@common/domain";
|
import { AggregateRoot, UniqueID, UtcDate } from "@common/domain";
|
||||||
import { Result } from "@common/helpers";
|
import { Maybe, Result } from "@common/helpers";
|
||||||
import { Customer, CustomerInvoiceItem } from "../entities";
|
import { Customer, CustomerInvoiceItem } from "../entities";
|
||||||
import { InvoiceStatus } from "../value-objetcs";
|
import { InvoiceStatus } from "../value-objetcs";
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ export interface ICustomerInvoiceProps {
|
|||||||
issueDate: UtcDate;
|
issueDate: UtcDate;
|
||||||
invoiceNumber: string;
|
invoiceNumber: string;
|
||||||
invoiceType: string;
|
invoiceType: string;
|
||||||
invoiceCustomerReference: string;
|
invoiceCustomerReference: Maybe<string>;
|
||||||
|
|
||||||
customer: Customer;
|
customer: Customer;
|
||||||
items: CustomerInvoiceItem[];
|
items: CustomerInvoiceItem[];
|
||||||
@ -20,7 +20,7 @@ export interface ICustomerInvoice {
|
|||||||
issueDate: UtcDate;
|
issueDate: UtcDate;
|
||||||
invoiceNumber: string;
|
invoiceNumber: string;
|
||||||
invoiceType: string;
|
invoiceType: string;
|
||||||
invoiceCustomerReference: string;
|
invoiceCustomerReference: Maybe<string>;
|
||||||
|
|
||||||
customer: Customer;
|
customer: Customer;
|
||||||
items: CustomerInvoiceItem[];
|
items: CustomerInvoiceItem[];
|
||||||
@ -63,7 +63,7 @@ export class CustomerInvoice
|
|||||||
return this.props.invoiceType;
|
return this.props.invoiceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
get invoiceCustomerReference(): string {
|
get invoiceCustomerReference(): Maybe<string> {
|
||||||
return this.props.invoiceCustomerReference;
|
return this.props.invoiceCustomerReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
export * from "./customer-invoice-repository.interface";
|
export * from "./customer-invoice-repository.interface";
|
||||||
export * from "./customer-repository.interface";
|
|
||||||
|
|||||||
@ -1,4 +1,2 @@
|
|||||||
export * from "./customer-invoice-service.interface";
|
export * from "./customer-invoice-service.interface";
|
||||||
export * from "./customer-invoice.service";
|
export * from "./customer-invoice.service";
|
||||||
export * from "./customer-service.interface";
|
|
||||||
export * from "./customer.service";
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { PostalAddress, TINNumber, UniqueID, UtcDate } from "@common/domain";
|
import { PostalAddress, TINNumber, UniqueID, UtcDate } from "@common/domain";
|
||||||
import { Result } from "@common/helpers";
|
import { Maybe, Result } from "@common/helpers";
|
||||||
import {
|
import {
|
||||||
ISequelizeMapper,
|
ISequelizeMapper,
|
||||||
MapperParamsType,
|
MapperParamsType,
|
||||||
@ -70,7 +70,7 @@ export class CustomerInvoiceMapper
|
|||||||
issueDate: issueDateOrError.data,
|
issueDate: issueDateOrError.data,
|
||||||
invoiceNumber: source.invoice_number,
|
invoiceNumber: source.invoice_number,
|
||||||
invoiceType: source.invoice_type,
|
invoiceType: source.invoice_type,
|
||||||
invoiceCustomerReference: source.invoice_customer_reference,
|
invoiceCustomerReference: Maybe.fromNullable(source.invoice_customer_reference),
|
||||||
customer: customerOrError.data,
|
customer: customerOrError.data,
|
||||||
items: [],
|
items: [],
|
||||||
},
|
},
|
||||||
@ -89,7 +89,7 @@ export class CustomerInvoiceMapper
|
|||||||
issue_date: source.issueDate.toDateString(),
|
issue_date: source.issueDate.toDateString(),
|
||||||
invoice_number: source.invoiceNumber,
|
invoice_number: source.invoiceNumber,
|
||||||
invoice_type: source.invoiceType,
|
invoice_type: source.invoiceType,
|
||||||
invoice_customer_reference: source.invoiceCustomerReference,
|
invoice_customer_reference: source.invoiceCustomerReference.getOrUndefined(),
|
||||||
|
|
||||||
lang_code: "es",
|
lang_code: "es",
|
||||||
currency_code: "EUR",
|
currency_code: "EUR",
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
id_article: {
|
id_article: {
|
||||||
type: DataTypes.BIGINT().UNSIGNED,
|
type: DataTypes.BIGINT().UNSIGNED,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
type: new DataTypes.MEDIUMINT(),
|
type: new DataTypes.MEDIUMINT(),
|
||||||
@ -65,26 +66,32 @@ export default (sequelize: Sequelize) => {
|
|||||||
description: {
|
description: {
|
||||||
type: new DataTypes.TEXT(),
|
type: new DataTypes.TEXT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
quantity: {
|
quantity: {
|
||||||
type: DataTypes.BIGINT(),
|
type: DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
unit_price: {
|
unit_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
subtotal_price: {
|
subtotal_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
discount: {
|
discount: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
total_price: {
|
total_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -126,6 +126,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
|
|
||||||
customer_id: {
|
customer_id: {
|
||||||
type: new DataTypes.UUID(),
|
type: new DataTypes.UUID(),
|
||||||
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
customer_name: {
|
customer_name: {
|
||||||
@ -146,6 +147,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
customer_street2: {
|
customer_street2: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
customer_city: {
|
customer_city: {
|
||||||
@ -168,56 +170,67 @@ export default (sequelize: Sequelize) => {
|
|||||||
subtotal_price: {
|
subtotal_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
discount: {
|
discount: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
discount_price: {
|
discount_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
before_tax_price: {
|
before_tax_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
tax: {
|
tax: {
|
||||||
type: new DataTypes.SMALLINT(),
|
type: new DataTypes.SMALLINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
tax_price: {
|
tax_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
total_price: {
|
total_price: {
|
||||||
type: new DataTypes.BIGINT(),
|
type: new DataTypes.BIGINT(),
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
notes: {
|
notes: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
integrity_hash: {
|
integrity_hash: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
comment: "Hash criptográfico para asegurar integridad",
|
comment: "Hash criptográfico para asegurar integridad",
|
||||||
},
|
},
|
||||||
previous_invoice_id: {
|
previous_invoice_id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
comment: "Referencia a la factura anterior (si aplica)",
|
comment: "Referencia a la factura anterior (si aplica)",
|
||||||
},
|
},
|
||||||
signed_at: {
|
signed_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
comment: "Fecha en que la factura fue firmada digitalmente",
|
comment: "Fecha en que la factura fue firmada digitalmente",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export default (sequelize: Sequelize) => {
|
|||||||
trade_name: {
|
trade_name: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
tin: {
|
tin: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
@ -108,10 +109,12 @@ export default (sequelize: Sequelize) => {
|
|||||||
fax: {
|
fax: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
defaultValue: null,
|
||||||
validate: {
|
validate: {
|
||||||
isUrl: true,
|
isUrl: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,81 +0,0 @@
|
|||||||
import { EmailAddress, UniqueID } from "@common/domain";
|
|
||||||
import { Collection, Result } from "@common/helpers";
|
|
||||||
import { SequelizeRepository } from "@common/infrastructure";
|
|
||||||
import { Customer, ICustomerRepository } from "@contexts/customer-billing/domain";
|
|
||||||
import { Transaction } from "sequelize";
|
|
||||||
import { customerMapper, ICustomerMapper } from "../mappers";
|
|
||||||
import { CustomerModel } from "./customer.model";
|
|
||||||
|
|
||||||
class CustomerRepository extends SequelizeRepository<Customer> implements ICustomerRepository {
|
|
||||||
private readonly _mapper!: ICustomerMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 🔹 Función personalizada para mapear errores de unicidad en autenticación
|
|
||||||
*/
|
|
||||||
private _customErrorMapper(error: Error): string | null {
|
|
||||||
if (error.name === "SequelizeUniqueConstraintError") {
|
|
||||||
return "Customer with this email already exists";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(mapper: ICustomerMapper) {
|
|
||||||
super();
|
|
||||||
this._mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAll(transaction?: Transaction): Promise<Result<Collection<Customer>, Error>> {
|
|
||||||
try {
|
|
||||||
const rawCustomers: any = await this._findAll(CustomerModel, {}, transaction);
|
|
||||||
|
|
||||||
if (!rawCustomers === true) {
|
|
||||||
return Result.fail(new Error("Customer with email not exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._mapper.mapArrayToDomain(rawCustomers);
|
|
||||||
} catch (error: any) {
|
|
||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async findById(id: UniqueID, transaction?: Transaction): Promise<Result<Customer, Error>> {
|
|
||||||
try {
|
|
||||||
const rawCustomer: any = await this._getById(CustomerModel, id, {}, transaction);
|
|
||||||
|
|
||||||
if (!rawCustomer === true) {
|
|
||||||
return Result.fail(new Error(`Customer with id ${id.toString()} not exists`));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._mapper.mapToDomain(rawCustomer);
|
|
||||||
} catch (error: any) {
|
|
||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async findByEmail(
|
|
||||||
email: EmailAddress,
|
|
||||||
transaction?: Transaction
|
|
||||||
): Promise<Result<Customer, Error>> {
|
|
||||||
try {
|
|
||||||
const rawCustomer: any = await this._getBy(
|
|
||||||
CustomerModel,
|
|
||||||
"email",
|
|
||||||
email.toString(),
|
|
||||||
{},
|
|
||||||
transaction
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!rawCustomer === true) {
|
|
||||||
return Result.fail(new Error(`Customer with email ${email.toString()} not exists`));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._mapper.mapToDomain(rawCustomer);
|
|
||||||
} catch (error: any) {
|
|
||||||
return this._handleDatabaseError(error, this._customErrorMapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const customerRepository = new CustomerRepository(customerMapper);
|
|
||||||
export { customerRepository };
|
|
||||||
@ -1,17 +1,15 @@
|
|||||||
import { ICustomerRepository } from "@contexts/customer-billing/domain";
|
|
||||||
import { ICustomerInvoiceRepository } from "@contexts/customer-billing/domain/";
|
import { ICustomerInvoiceRepository } from "@contexts/customer-billing/domain/";
|
||||||
import { customerRepository } from "./customer.repository";
|
import { customerInvoiceRepository } from "./customer-invoice.repository";
|
||||||
|
|
||||||
export * from "./customer.model";
|
|
||||||
export * from "./customer.repository";
|
|
||||||
|
|
||||||
export * from "./customer-invoice.model";
|
export * from "./customer-invoice.model";
|
||||||
|
export * from "./customer.model";
|
||||||
|
|
||||||
export * from "./customer-invoice.repository";
|
export * from "./customer-invoice.repository";
|
||||||
|
|
||||||
export const createCustomerRepository = (): ICustomerRepository => {
|
/*export const createCustomerRepository = (): ICustomerRepository => {
|
||||||
return customerRepository;
|
return customerRepository;
|
||||||
};
|
};*/
|
||||||
|
|
||||||
export const createCustomerInvoiceRepository = (): ICustomerInvoiceRepository => {
|
export const createCustomerInvoiceRepository = (): ICustomerInvoiceRepository => {
|
||||||
return customerRepository;
|
return customerInvoiceRepository;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { SequelizeTransactionManager } from "@common/infrastructure";
|
import { SequelizeTransactionManager } from "@common/infrastructure";
|
||||||
import { CustomerInvoiceService } from "@contexts/customer-billing/domain";
|
import { CustomerInvoiceService } from "@contexts/customer-billing/domain";
|
||||||
import { customerInvoiceRepository } from "@contexts/customer-billing/infraestructure";
|
import { customerInvoiceRepository } from "@contexts/customer-billing/infraestructure";
|
||||||
|
import { ListCustomerInvoicesUseCase } from "../../../../application";
|
||||||
import { ListCustomerInvoicesController } from "./list-customer-invoices.controller";
|
import { ListCustomerInvoicesController } from "./list-customer-invoices.controller";
|
||||||
import { listCustomerInvoicesPresenter } from "./list-customer-invoices.presenter";
|
import { listCustomerInvoicesPresenter } from "./list-customer-invoices.presenter";
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Collection, ensureBoolean, ensureNumber, ensureString } from "@common/helpers";
|
import { Collection, ensureString } from "@common/helpers";
|
||||||
|
|
||||||
import { CustomerInvoice } from "@contexts/customer-billing/domain";
|
import { CustomerInvoice } from "@contexts/customer-billing/domain";
|
||||||
import { IListCustomerInvoicesResponseDTO } from "../../../dto";
|
import { IListCustomerInvoicesResponseDTO } from "../../../dto";
|
||||||
@ -8,10 +8,10 @@ export interface IListCustomerInvoicesPresenter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const listCustomerInvoicesPresenter: IListCustomerInvoicesPresenter = {
|
export const listCustomerInvoicesPresenter: IListCustomerInvoicesPresenter = {
|
||||||
toDTO: (customers: Collection<CustomerInvoice>): IListCustomerInvoicesResponseDTO[] =>
|
toDTO: (invoice: Collection<CustomerInvoice>): IListCustomerInvoicesResponseDTO[] =>
|
||||||
customers.map((customer) => ({
|
invoice.map((customer) => ({
|
||||||
id: ensureString(customer.id.toString()),
|
id: ensureString(customer.id.toString()),
|
||||||
reference: ensureString(customer.reference),
|
/*reference: ensureString(customer.),
|
||||||
|
|
||||||
is_freelancer: ensureBoolean(customer.isFreelancer),
|
is_freelancer: ensureBoolean(customer.isFreelancer),
|
||||||
name: ensureString(customer.name),
|
name: ensureString(customer.name),
|
||||||
@ -34,6 +34,6 @@ export const listCustomerInvoicesPresenter: IListCustomerInvoicesPresenter = {
|
|||||||
default_tax: ensureNumber(customer.defaultTax),
|
default_tax: ensureNumber(customer.defaultTax),
|
||||||
status: ensureString(customer.isActive ? "active" : "inactive"),
|
status: ensureString(customer.isActive ? "active" : "inactive"),
|
||||||
lang_code: ensureString(customer.langCode),
|
lang_code: ensureString(customer.langCode),
|
||||||
currency_code: ensureString(customer.currencyCode),
|
currency_code: ensureString(customer.currencyCode),*/
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export * from "./list";
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { SequelizeTransactionManager } from "@common/infrastructure";
|
|
||||||
import { ListCustomersUseCase } from "@contexts/customer-billing/application/customers/list-customers";
|
|
||||||
import { CustomerService } from "@contexts/customer-billing/domain";
|
|
||||||
import { customerRepository } from "@contexts/customer-billing/infraestructure";
|
|
||||||
import { ListCustomersController } from "./list-customers.controller";
|
|
||||||
import { listCustomersPresenter } from "./list-customers.presenter";
|
|
||||||
|
|
||||||
export const listCustomersController = () => {
|
|
||||||
const transactionManager = new SequelizeTransactionManager();
|
|
||||||
const customerService = new CustomerService(customerRepository);
|
|
||||||
|
|
||||||
const useCase = new ListCustomersUseCase(customerService, transactionManager);
|
|
||||||
const presenter = listCustomersPresenter;
|
|
||||||
|
|
||||||
return new ListCustomersController(useCase, presenter);
|
|
||||||
};
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
import { ExpressController } from "@common/presentation";
|
|
||||||
import { ListCustomersUseCase } from "@contexts/customer-billing/application";
|
|
||||||
import { IListCustomersPresenter } from "./list-customers.presenter";
|
|
||||||
|
|
||||||
export class ListCustomersController extends ExpressController {
|
|
||||||
public constructor(
|
|
||||||
private readonly listCustomers: ListCustomersUseCase,
|
|
||||||
private readonly presenter: IListCustomersPresenter
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async executeImpl() {
|
|
||||||
const customersOrError = await this.listCustomers.execute();
|
|
||||||
|
|
||||||
if (customersOrError.isFailure) {
|
|
||||||
return this.handleError(customersOrError.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ok(this.presenter.toDTO(customersOrError.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { Collection, ensureBoolean, ensureNumber, ensureString } from "@common/helpers";
|
|
||||||
import { Customer } from "@contexts/customer-billing/domain";
|
|
||||||
import { IListCustomersResponseDTO } from "../../../dto";
|
|
||||||
|
|
||||||
export interface IListCustomersPresenter {
|
|
||||||
toDTO: (customers: Collection<Customer>) => IListCustomersResponseDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const listCustomersPresenter: IListCustomersPresenter = {
|
|
||||||
toDTO: (customers: Collection<Customer>): IListCustomersResponseDTO[] =>
|
|
||||||
customers.map((customer) => ({
|
|
||||||
id: ensureString(customer.id.toString()),
|
|
||||||
reference: ensureString(customer.reference),
|
|
||||||
|
|
||||||
is_freelancer: ensureBoolean(customer.isFreelancer),
|
|
||||||
name: ensureString(customer.name),
|
|
||||||
trade_name: ensureString(customer.tradeName.getValue()),
|
|
||||||
tin: ensureString(customer.tin.toString()),
|
|
||||||
|
|
||||||
street: ensureString(customer.address.street),
|
|
||||||
city: ensureString(customer.address.city),
|
|
||||||
state: ensureString(customer.address.state),
|
|
||||||
postal_code: ensureString(customer.address.postalCode),
|
|
||||||
country: ensureString(customer.address.country),
|
|
||||||
|
|
||||||
email: ensureString(customer.email.toString()),
|
|
||||||
phone: ensureString(customer.phone.toString()),
|
|
||||||
fax: ensureString(customer.fax.getValue()?.toString()),
|
|
||||||
website: ensureString(customer.website.getValue()),
|
|
||||||
|
|
||||||
legal_record: ensureString(customer.legalRecord),
|
|
||||||
|
|
||||||
default_tax: ensureNumber(customer.defaultTax),
|
|
||||||
status: ensureString(customer.isActive ? "active" : "inactive"),
|
|
||||||
lang_code: ensureString(customer.langCode),
|
|
||||||
currency_code: ensureString(customer.currencyCode),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
@ -1,2 +1 @@
|
|||||||
export * from "./customer-invoices";
|
export * from "./customer-invoices";
|
||||||
export * from "./customers";
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export interface IListCustomerInvoicesResponseDTO {
|
export interface IListCustomerInvoicesResponseDTO {
|
||||||
id: string;
|
id: string;
|
||||||
reference: string;
|
/*reference: string;
|
||||||
|
|
||||||
is_freelancer: boolean;
|
is_freelancer: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
@ -23,7 +23,7 @@ export interface IListCustomerInvoicesResponseDTO {
|
|||||||
default_tax: number;
|
default_tax: number;
|
||||||
status: string;
|
status: string;
|
||||||
lang_code: string;
|
lang_code: string;
|
||||||
currency_code: string;
|
currency_code: string;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGetCustomerInvoiceResponseDTO {}
|
export interface IGetCustomerInvoiceResponseDTO {}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./controllers/customers";
|
export * from "./controllers";
|
||||||
export * from "./dto";
|
export * from "./dto";
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
import { validateRequestDTO } from "@common/presentation";
|
import { validateRequestDTO } from "@common/presentation";
|
||||||
import { ListAccountsSchema } from "@contexts/accounts/presentation";
|
import {
|
||||||
import { listAccountsController } from "@contexts/accounts/presentation/controllers/list-accounts";
|
ICreateAcccountResponseDTOSchema,
|
||||||
|
IGetAcccountResponseDTOSchema,
|
||||||
|
IUpdateAcccountResponseDTOSchema,
|
||||||
|
ListAccountsSchema,
|
||||||
|
} from "@contexts/accounts/presentation";
|
||||||
|
import {
|
||||||
|
createAccountController,
|
||||||
|
getAccountController,
|
||||||
|
listAccountsController,
|
||||||
|
updateAccountController,
|
||||||
|
} from "@contexts/accounts/presentation/controllers";
|
||||||
import { checkTabContext } from "@contexts/auth/infraestructure";
|
import { checkTabContext } from "@contexts/auth/infraestructure";
|
||||||
import { NextFunction, Request, Response, Router } from "express";
|
import { NextFunction, Request, Response, Router } from "express";
|
||||||
|
|
||||||
@ -17,5 +27,35 @@ export const accountsRouter = (appRouter: Router) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
routes.get(
|
||||||
|
"/:accountId",
|
||||||
|
validateRequestDTO(IGetAcccountResponseDTOSchema),
|
||||||
|
checkTabContext,
|
||||||
|
//checkUser,
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
getAccountController().execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
routes.post(
|
||||||
|
"/",
|
||||||
|
validateRequestDTO(ICreateAcccountResponseDTOSchema),
|
||||||
|
checkTabContext,
|
||||||
|
//checkUser,
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
createAccountController().execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
routes.put(
|
||||||
|
"/:accountId",
|
||||||
|
validateRequestDTO(IUpdateAcccountResponseDTOSchema),
|
||||||
|
checkTabContext,
|
||||||
|
//checkUser,
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
updateAccountController().execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
appRouter.use("/accounts", routes);
|
appRouter.use("/accounts", routes);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -65,9 +65,6 @@ importers:
|
|||||||
luxon:
|
luxon:
|
||||||
specifier: ^3.5.0
|
specifier: ^3.5.0
|
||||||
version: 3.5.0
|
version: 3.5.0
|
||||||
mariadb:
|
|
||||||
specifier: ^3.4.0
|
|
||||||
version: 3.4.0
|
|
||||||
module-alias:
|
module-alias:
|
||||||
specifier: ^2.2.3
|
specifier: ^2.2.3
|
||||||
version: 2.2.3
|
version: 2.2.3
|
||||||
@ -94,16 +91,13 @@ importers:
|
|||||||
version: 2.3.3
|
version: 2.3.3
|
||||||
sequelize:
|
sequelize:
|
||||||
specifier: ^6.37.5
|
specifier: ^6.37.5
|
||||||
version: 6.37.5(mariadb@3.4.0)(mysql2@3.12.0)
|
version: 6.37.5(mysql2@3.12.0)
|
||||||
shallow-equal-object:
|
shallow-equal-object:
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.1
|
specifier: ^10.9.1
|
||||||
version: 10.9.2(@types/node@22.12.0)(typescript@5.7.3)
|
version: 10.9.2(@types/node@22.12.0)(typescript@5.7.3)
|
||||||
tsconfig-paths:
|
|
||||||
specifier: ^4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
uuid:
|
uuid:
|
||||||
specifier: ^11.0.5
|
specifier: ^11.0.5
|
||||||
version: 11.0.5
|
version: 11.0.5
|
||||||
@ -201,6 +195,9 @@ importers:
|
|||||||
ts-node-dev:
|
ts-node-dev:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0(@types/node@22.12.0)(typescript@5.7.3)
|
version: 2.0.0(@types/node@22.12.0)(typescript@5.7.3)
|
||||||
|
tsconfig-paths:
|
||||||
|
specifier: ^4.2.0
|
||||||
|
version: 4.2.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.7.3
|
specifier: ^5.7.3
|
||||||
version: 5.7.3
|
version: 5.7.3
|
||||||
@ -1289,9 +1286,6 @@ packages:
|
|||||||
'@types/express@4.17.21':
|
'@types/express@4.17.21':
|
||||||
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
||||||
|
|
||||||
'@types/geojson@7946.0.16':
|
|
||||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
|
||||||
|
|
||||||
'@types/glob@8.1.0':
|
'@types/glob@8.1.0':
|
||||||
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
|
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
|
||||||
|
|
||||||
@ -3215,10 +3209,6 @@ packages:
|
|||||||
makeerror@1.0.12:
|
makeerror@1.0.12:
|
||||||
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
|
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
|
||||||
|
|
||||||
mariadb@3.4.0:
|
|
||||||
resolution: {integrity: sha512-hdRPcAzs+MTxK5VG1thBW18gGTlw6yWBe9YnLB65GLo7q0fO5DWsgomIevV/pXSaWRmD3qi6ka4oSFRTExRiEQ==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -5538,8 +5528,6 @@ snapshots:
|
|||||||
'@types/qs': 6.9.18
|
'@types/qs': 6.9.18
|
||||||
'@types/serve-static': 1.15.7
|
'@types/serve-static': 1.15.7
|
||||||
|
|
||||||
'@types/geojson@7946.0.16': {}
|
|
||||||
|
|
||||||
'@types/glob@8.1.0':
|
'@types/glob@8.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/minimatch': 5.1.2
|
'@types/minimatch': 5.1.2
|
||||||
@ -8128,14 +8116,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tmpl: 1.0.5
|
tmpl: 1.0.5
|
||||||
|
|
||||||
mariadb@3.4.0:
|
|
||||||
dependencies:
|
|
||||||
'@types/geojson': 7946.0.16
|
|
||||||
'@types/node': 22.12.0
|
|
||||||
denque: 2.1.0
|
|
||||||
iconv-lite: 0.6.3
|
|
||||||
lru-cache: 10.4.3
|
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
media-typer@0.3.0: {}
|
media-typer@0.3.0: {}
|
||||||
@ -8746,7 +8726,7 @@ snapshots:
|
|||||||
|
|
||||||
sequelize-pool@7.1.0: {}
|
sequelize-pool@7.1.0: {}
|
||||||
|
|
||||||
sequelize@6.37.5(mariadb@3.4.0)(mysql2@3.12.0):
|
sequelize@6.37.5(mysql2@3.12.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/debug': 4.1.12
|
'@types/debug': 4.1.12
|
||||||
'@types/validator': 13.12.2
|
'@types/validator': 13.12.2
|
||||||
@ -8765,7 +8745,6 @@ snapshots:
|
|||||||
validator: 13.12.0
|
validator: 13.12.0
|
||||||
wkx: 0.5.0
|
wkx: 0.5.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
mariadb: 3.4.0
|
|
||||||
mysql2: 3.12.0
|
mysql2: 3.12.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user