refactor: ajustar importaciones y tipos en varios archivos para mejorar la claridad y la consistencia
This commit is contained in:
parent
2f23d75b6f
commit
38fb6299b7
@ -1,8 +1,8 @@
|
|||||||
import type { ISequelizeTransactionContext } from "@erp/core/api";
|
import type { ISequelizeTransactionContext } from "@erp/core/api";
|
||||||
import type { AuditEntry, AuditRecordOptions, IAuditStore } from "@repo/rdx-audit";
|
import type { AuditEntry, IAuditStore } from "@repo/rdx-audit";
|
||||||
import { AuditStoreError } from "@repo/rdx-audit";
|
import { type AuditRecordOptions, AuditStoreError } from "@repo/rdx-audit";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import type { ModelStatic } from "sequelize";
|
import type { ModelStatic, Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { AuditChangeModel } from "./audit-change.model";
|
import type { AuditChangeModel } from "./audit-change.model";
|
||||||
import type { AuditEventModel } from "./audit-event.model";
|
import type { AuditEventModel } from "./audit-event.model";
|
||||||
@ -19,8 +19,12 @@ export class SequelizeAuditStore implements IAuditStore {
|
|||||||
private readonly transactionContext: ISequelizeTransactionContext
|
private readonly transactionContext: ISequelizeTransactionContext
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async append(entry: AuditEntry, options?: AuditRecordOptions): Promise<Result<void, AuditStoreError>> {
|
async append(
|
||||||
const transaction = options?.transaction ?? this.transactionContext.current();
|
entry: AuditEntry,
|
||||||
|
options?: AuditRecordOptions
|
||||||
|
): Promise<Result<void, AuditStoreError>> {
|
||||||
|
const transaction = (options?.transaction as Transaction) ?? this.transactionContext.current();
|
||||||
|
|
||||||
if (entry.kind === "mutation" && !transaction) {
|
if (entry.kind === "mutation" && !transaction) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new AuditStoreError("An active transaction is required for audit mutations.")
|
new AuditStoreError("An active transaction is required for audit mutations.")
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { AuditEntry, IAuditStore } from "@repo/rdx-audit";
|
import type { AuditEntry, AuditRecordOptions, IAuditStore } from "@repo/rdx-audit";
|
||||||
import { AuditStoreError } from "@repo/rdx-audit";
|
import { AuditStoreError } from "@repo/rdx-audit";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import type { ModelStatic } from "sequelize";
|
import type { ModelStatic, Transaction } from "sequelize";
|
||||||
|
|
||||||
import type { ISequelizeTransactionContext } from "../../persistence/sequelize";
|
import type { ISequelizeTransactionContext } from "../../persistence/sequelize";
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ export class SequelizeAuditStore implements IAuditStore {
|
|||||||
entry: AuditEntry,
|
entry: AuditEntry,
|
||||||
options?: AuditRecordOptions
|
options?: AuditRecordOptions
|
||||||
): Promise<Result<void, AuditStoreError>> {
|
): Promise<Result<void, AuditStoreError>> {
|
||||||
const transaction = options?.transaction ?? this.transactionContext.current();
|
const transaction = (options?.transaction as Transaction) ?? this.transactionContext.current();
|
||||||
if (entry.kind === "mutation" && !transaction)
|
if (entry.kind === "mutation" && !transaction)
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new AuditStoreError("An active transaction is required for audit mutations.")
|
new AuditStoreError("An active transaction is required for audit mutations.")
|
||||||
|
|||||||
@ -1,11 +1,5 @@
|
|||||||
// core/api/infrastructure/documents/certificates/
|
import type { ICompanyCertificateContext, ISigningContextResolver } from "../../../application";
|
||||||
// env-company-signing-context-store.ts
|
import type { ModuleParams } from "../../../modules";
|
||||||
|
|
||||||
import type {
|
|
||||||
ICompanyCertificateContext,
|
|
||||||
ISigningContextResolver,
|
|
||||||
} from "@erp/core/api/application";
|
|
||||||
import type { ModuleParams } from "@erp/core/api/modules";
|
|
||||||
|
|
||||||
import type { ICompanySigningContextRecord } from "./company-signing-context-record.interface";
|
import type { ICompanySigningContextRecord } from "./company-signing-context-record.interface";
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import type { IDocumentSigningService } from "@erp/core/api/application";
|
import type { ICompanyCertificateContext, IDocumentSigningService } from "../../../application";
|
||||||
import type { ICompanyCertificateContext } from "@erp/core/api/application/documents/application-models/company-certificate-context.interface";
|
|
||||||
|
|
||||||
import type { RestDocumentSigningConfig } from "./rest-document-signing-config";
|
import type { RestDocumentSigningConfig } from "./rest-document-signing-config";
|
||||||
import { SimpleCircuitBreaker } from "./simple-circuit-breaker";
|
import { SimpleCircuitBreaker } from "./simple-circuit-breaker";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { DomainMapperWithBulk, IQueryMapperWithBulk } from "@erp/core/api/application";
|
import type { DomainMapperWithBulk, IQueryMapperWithBulk } from "../../../../application";
|
||||||
|
|
||||||
export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
||||||
extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {}
|
extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export * from "./module-server.interface";
|
|
||||||
export * from "./module-runtime";
|
export * from "./module-runtime";
|
||||||
|
export * from "./module-server.interface";
|
||||||
export * from "./modules-runtime";
|
export * from "./modules-runtime";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import type { ILogger } from "@repo/rdx-logger";
|
import type { ILogger } from "@repo/rdx-logger";
|
||||||
import type { ModelStatic, Sequelize } from "sequelize";
|
import type { Model, ModelStatic, Sequelize } from "sequelize";
|
||||||
|
|
||||||
import type { IModuleServer } from "./module-server.interface";
|
import type { IModuleServer } from "./module-server.interface";
|
||||||
import type { ModuleParams, ModuleSetupResult, StartParams, WarmupParams } from "./types";
|
|
||||||
import type { ModulesRuntime } from "./modules-runtime";
|
import type { ModulesRuntime } from "./modules-runtime";
|
||||||
|
import type { ModuleParams, ModuleSetupResult, StartParams, WarmupParams } from "./types";
|
||||||
|
|
||||||
type ModelInitializer = (database: Sequelize) => ModelStatic;
|
type ModelInitializer = (database: Sequelize) => ModelStatic<Model>;
|
||||||
type ModelEntry = { initializer: ModelInitializer; moduleName: string };
|
type ModelEntry = { initializer: ModelInitializer; moduleName: string };
|
||||||
|
|
||||||
const modules = new Map<string, IModuleServer>();
|
const modules = new Map<string, IModuleServer>();
|
||||||
@ -29,13 +29,28 @@ export function registerModels(initializers: ModelInitializer[], moduleName: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Inicializa únicamente infraestructura backend: setup, modelos y warmups. */
|
/** Inicializa únicamente infraestructura backend: setup, modelos y warmups. */
|
||||||
export async function initModules(params: ModuleParams & { logger: ILogger; database: Sequelize; warmup: { timeoutMs: number; strict: boolean }; databaseSyncMode: string }): Promise<ModulesRuntime> {
|
export async function initModules(
|
||||||
|
params: ModuleParams & {
|
||||||
|
logger: ILogger;
|
||||||
|
database: Sequelize;
|
||||||
|
warmup: { timeoutMs: number; strict: boolean };
|
||||||
|
databaseSyncMode: string;
|
||||||
|
}
|
||||||
|
): Promise<ModulesRuntime> {
|
||||||
for (const name of modules.keys()) await setupModule(name, params, []);
|
for (const name of modules.keys()) await setupModule(name, params, []);
|
||||||
await initializeModels(params.database, params.databaseSyncMode, params.logger);
|
await initializeModels(params.database, params.databaseSyncMode, params.logger);
|
||||||
await warmupModules(params);
|
await warmupModules(params);
|
||||||
validateDependencies();
|
validateDependencies();
|
||||||
const customerInvoices = internals.get("customer-invoices") as { proformas: { recurringProformas: ModulesRuntime["customerInvoices"]["recurringProformas"] } } | undefined;
|
|
||||||
if (!customerInvoices) throw new Error('Module "customer-invoices" did not expose recurring runtime capabilities');
|
const customerInvoices = internals.get("customer-invoices") as
|
||||||
|
| {
|
||||||
|
proformas: { recurringProformas: ModulesRuntime["customerInvoices"]["recurringProformas"] };
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (!customerInvoices)
|
||||||
|
throw new Error('Module "customer-invoices" did not expose recurring runtime capabilities');
|
||||||
|
|
||||||
return { customerInvoices: customerInvoices.proformas.recurringProformas };
|
return { customerInvoices: customerInvoices.proformas.recurringProformas };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,71 +59,228 @@ export async function startModules(params: ModuleParams & { logger: ILogger }):
|
|||||||
for (const name of setupOrder) {
|
for (const name of setupOrder) {
|
||||||
const module = modules.get(name);
|
const module = modules.get(name);
|
||||||
if (!module?.start) continue;
|
if (!module?.start) continue;
|
||||||
await module.start({ ...params, getService: makeGetService(name, module), getInternal: makeGetInternal(name), listServices } as StartParams);
|
await module.start({
|
||||||
|
...params,
|
||||||
|
getService: makeGetService(name, module),
|
||||||
|
getInternal: makeGetInternal(name),
|
||||||
|
listServices,
|
||||||
|
} as StartParams);
|
||||||
}
|
}
|
||||||
validateDependencies();
|
validateDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupModule(name: string, params: ModuleParams & { logger: ILogger }, stack: string[]): Promise<void> {
|
async function setupModule(
|
||||||
if (setupDone.has(name)) return;
|
name: string,
|
||||||
if (visiting.has(name)) throw new Error(`Cyclic dependency detected: ${[...stack, name].join(" -> ")}`);
|
params: ModuleParams & { logger: ILogger },
|
||||||
|
stack: string[]
|
||||||
|
): Promise<void> {
|
||||||
|
if (setupDone.has(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visiting.has(name)) {
|
||||||
|
throw new Error(`Cyclic dependency detected: ${[...stack, name].join(" -> ")}`);
|
||||||
|
}
|
||||||
|
|
||||||
const module = modules.get(name);
|
const module = modules.get(name);
|
||||||
if (!module) throw new Error(`Module "${name}" not found`);
|
|
||||||
|
if (!module) {
|
||||||
|
throw new Error(`Module "${name}" not found`);
|
||||||
|
}
|
||||||
|
|
||||||
visiting.add(name);
|
visiting.add(name);
|
||||||
for (const dependency of module.dependencies ?? []) await setupModule(dependency, params, [...stack, name]);
|
for (const dependency of module.dependencies ?? []) {
|
||||||
const result = (await module.setup?.({ ...params, getService: makeGetService(name, module), listServices })) as ModuleSetupResult | undefined;
|
await setupModule(dependency, params, [...stack, name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = (await module.setup?.({
|
||||||
|
...params,
|
||||||
|
getService: makeGetService(name, module),
|
||||||
|
listServices,
|
||||||
|
})) as ModuleSetupResult | undefined;
|
||||||
|
|
||||||
internals.set(name, result?.internal ?? {});
|
internals.set(name, result?.internal ?? {});
|
||||||
if (result?.models) registerModels(result.models as ModelInitializer[], name);
|
|
||||||
for (const [key, service] of Object.entries(result?.services ?? {})) registerService(`${name}:${key}`, service);
|
if (result?.models) {
|
||||||
|
registerModels(result.models as ModelInitializer[], name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, service] of Object.entries(result?.services ?? {})) {
|
||||||
|
registerService(`${name}:${key}`, service);
|
||||||
|
}
|
||||||
|
|
||||||
setupDone.add(name);
|
setupDone.add(name);
|
||||||
|
|
||||||
setupOrder.push(name);
|
setupOrder.push(name);
|
||||||
|
|
||||||
visiting.delete(name);
|
visiting.delete(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeModels(database: Sequelize, syncMode: string, logger: ILogger): Promise<void> {
|
async function initializeModels(
|
||||||
if (modelsInitialized) return;
|
database: Sequelize,
|
||||||
const registered = new Map<string, { model: ModelStatic; moduleName: string }>();
|
syncMode: string,
|
||||||
|
logger: ILogger
|
||||||
|
): Promise<void> {
|
||||||
|
if (modelsInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const registered = new Map<string, { model: ModelStatic<Model>; moduleName: string }>();
|
||||||
|
|
||||||
for (const entry of models) {
|
for (const entry of models) {
|
||||||
const model = entry.initializer(database);
|
const model = entry.initializer(database);
|
||||||
if (!model?.name) throw new Error(`Invalid model initializer from "${entry.moduleName}"`);
|
if (!model?.name) {
|
||||||
if (registered.has(model.name)) throw new Error(`Model name collision: "${model.name}"`);
|
throw new Error(`Invalid model initializer from "${entry.moduleName}"`);
|
||||||
|
}
|
||||||
|
if (registered.has(model.name)) {
|
||||||
|
throw new Error(`Model name collision: "${model.name}"`);
|
||||||
|
}
|
||||||
registered.set(model.name, { model, moduleName: entry.moduleName });
|
registered.set(model.name, { model, moduleName: entry.moduleName });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { model } of registered.values()) {
|
for (const { model } of registered.values()) {
|
||||||
const associate = model as ModelStatic & { associate?: (sequelize: Sequelize) => void };
|
const associate = model as ModelStatic<Model> & { associate?: (sequelize: Sequelize) => void };
|
||||||
associate.associate?.(database);
|
associate.associate?.(database);
|
||||||
}
|
}
|
||||||
if (syncMode === "alter") await database.sync({ force: false, alter: true });
|
|
||||||
if (syncMode === "force") await database.sync({ force: true, alter: false });
|
if (syncMode === "alter") {
|
||||||
logger.info("Backend module models initialized", { label: "moduleRuntime", modelCount: registered.size });
|
await database.sync({ force: false, alter: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncMode === "force") {
|
||||||
|
await database.sync({ force: true, alter: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Backend module models initialized", {
|
||||||
|
label: "moduleRuntime",
|
||||||
|
modelCount: registered.size,
|
||||||
|
});
|
||||||
modelsInitialized = true;
|
modelsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function warmupModules(params: ModuleParams & { logger: ILogger; warmup: { timeoutMs: number; strict: boolean } }): Promise<void> {
|
async function warmupModules(
|
||||||
|
params: ModuleParams & { logger: ILogger; warmup: { timeoutMs: number; strict: boolean } }
|
||||||
|
): Promise<void> {
|
||||||
for (const name of setupOrder) {
|
for (const name of setupOrder) {
|
||||||
const module = modules.get(name);
|
const module = modules.get(name);
|
||||||
if (!module?.warmup) continue;
|
if (!module?.warmup) continue;
|
||||||
try {
|
try {
|
||||||
await withTimeout(Promise.resolve(module.warmup({ ...params, getService: makeGetService(name, module), getInternal: makeGetInternal(name), listServices } as WarmupParams)), params.warmup.timeoutMs, `${name}.warmup`);
|
await withTimeout(
|
||||||
|
Promise.resolve(
|
||||||
|
module.warmup({
|
||||||
|
...params,
|
||||||
|
getService: makeGetService(name, module),
|
||||||
|
getInternal: makeGetInternal(name),
|
||||||
|
listServices,
|
||||||
|
} as WarmupParams)
|
||||||
|
),
|
||||||
|
params.warmup.timeoutMs,
|
||||||
|
`${name}.warmup`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (params.warmup.strict) throw error;
|
if (params.warmup.strict) throw error;
|
||||||
params.logger.warn("Module warmup failed but continuing", { label: "moduleRuntime", module: name, error });
|
params.logger.warn("Module warmup failed but continuing", {
|
||||||
|
label: "moduleRuntime",
|
||||||
|
module: name,
|
||||||
|
error,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerService(name: string, service: unknown): void { if (services[name]) throw new Error(`Service "${name}" already registered`); services[name] = service; }
|
function registerService(name: string, service: unknown): void {
|
||||||
function makeGetService(moduleName: string, module: IModuleServer) { return <T>(name: string): T => { const [dependency, ...key] = name.split(":"); if (dependency !== "self") { if (!(module.dependencies ?? []).includes(dependency)) throw new Error(`Module "${moduleName}" tried to access service "${name}" without declaring dependency on "${dependency}"`); trackDependency(moduleName, dependency); return getService<T>(name); } return getService<T>(`${moduleName}:${key.join(":")}`); }; }
|
if (services[name]) throw new Error(`Service "${name}" already registered`);
|
||||||
function makeGetInternal(moduleName: string) { return <T>(requestedModule: string, key?: string): T => { if (requestedModule !== moduleName) throw new Error(`Module "${moduleName}" attempted to access private internal state`); const internal = internals.get(moduleName) ?? {}; return (key ? internal[key] : internal) as T; }; }
|
services[name] = service;
|
||||||
function getService<T>(name: string): T { const service = services[name]; if (!service) throw new Error(`Service "${name}" not found`); return service as T; }
|
}
|
||||||
function trackDependency(moduleName: string, dependency: string): void { const used = usedDependencies.get(moduleName) ?? new Set<string>(); used.add(dependency); usedDependencies.set(moduleName, used); }
|
|
||||||
function validateDependencies(): void { for (const [name, module] of modules) { const declared = new Set(module.dependencies ?? []); const used = usedDependencies.get(name) ?? new Set<string>(); const undeclared = [...used].filter((dependency) => !declared.has(dependency)); if (undeclared.length) throw new Error(`Module "${name}" used undeclared dependencies: ${undeclared.join(", ")}`); } }
|
function makeGetService(moduleName: string, module: IModuleServer) {
|
||||||
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> { let timeout: NodeJS.Timeout | undefined; const timeoutPromise = new Promise<T>((_, reject) => { timeout = setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms (${label})`)), timeoutMs); }); return Promise.race([promise, timeoutPromise]).finally(() => { if (timeout) clearTimeout(timeout); }) as Promise<T>; }
|
return <T>(name: string): T => {
|
||||||
export function listServices(): string[] { return Object.keys(services); }
|
const [dependency, ...key] = name.split(":");
|
||||||
export function getServiceScoped<T>(requesterModule: string, allowedDependencies: readonly string[], name: string): T {
|
if (dependency !== "self") {
|
||||||
|
if (!(module.dependencies ?? []).includes(dependency))
|
||||||
|
throw new Error(
|
||||||
|
`Module "${moduleName}" tried to access service "${name}" without declaring dependency on "${dependency}"`
|
||||||
|
);
|
||||||
|
trackDependency(moduleName, dependency);
|
||||||
|
return getService<T>(name);
|
||||||
|
}
|
||||||
|
return getService<T>(`${moduleName}:${key.join(":")}`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeGetInternal(moduleName: string) {
|
||||||
|
return <T>(requestedModule: string, key?: string): T => {
|
||||||
|
if (requestedModule !== moduleName)
|
||||||
|
throw new Error(`Module "${moduleName}" attempted to access private internal state`);
|
||||||
|
const internal = internals.get(moduleName) ?? {};
|
||||||
|
return (key ? internal[key] : internal) as T;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getService<T>(name: string): T {
|
||||||
|
const service = services[name];
|
||||||
|
if (!service) throw new Error(`Service "${name}" not found`);
|
||||||
|
return service as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackDependency(moduleName: string, dependency: string): void {
|
||||||
|
const used = usedDependencies.get(moduleName) ?? new Set<string>();
|
||||||
|
used.add(dependency);
|
||||||
|
usedDependencies.set(moduleName, used);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateDependencies(): void {
|
||||||
|
for (const [name, module] of modules) {
|
||||||
|
const declared = new Set(module.dependencies ?? []);
|
||||||
|
const used = usedDependencies.get(name) ?? new Set<string>();
|
||||||
|
const undeclared = [...used].filter((dependency) => !declared.has(dependency));
|
||||||
|
if (undeclared.length)
|
||||||
|
throw new Error(`Module "${name}" used undeclared dependencies: ${undeclared.join(", ")}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
|
||||||
|
let timeout: NodeJS.Timeout | undefined;
|
||||||
|
const timeoutPromise = new Promise<T>((_, reject) => {
|
||||||
|
timeout = setTimeout(
|
||||||
|
() => reject(new Error(`Timeout after ${timeoutMs}ms (${label})`)),
|
||||||
|
timeoutMs
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return Promise.race([promise, timeoutPromise]).finally(() => {
|
||||||
|
if (timeout) clearTimeout(timeout);
|
||||||
|
}) as Promise<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listServices(): string[] {
|
||||||
|
return Object.keys(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getServiceScoped<T>(
|
||||||
|
requesterModule: string,
|
||||||
|
allowedDependencies: readonly string[],
|
||||||
|
name: string
|
||||||
|
): T {
|
||||||
const [moduleName, ...key] = name.split(":");
|
const [moduleName, ...key] = name.split(":");
|
||||||
if (moduleName === "self") return getService<T>(`${requesterModule}:${key.join(":")}`);
|
if (moduleName === "self") return getService<T>(`${requesterModule}:${key.join(":")}`);
|
||||||
if (!allowedDependencies.includes(moduleName)) throw new Error(`Module "${requesterModule}" tried to access service "${name}" without declaring dependency on "${moduleName}"`);
|
if (!allowedDependencies.includes(moduleName))
|
||||||
|
throw new Error(
|
||||||
|
`Module "${requesterModule}" tried to access service "${name}" without declaring dependency on "${moduleName}"`
|
||||||
|
);
|
||||||
return getService<T>(name);
|
return getService<T>(name);
|
||||||
}
|
}
|
||||||
export function resetModuleRuntimeForTests(): void { modules.clear(); models.length = 0; setupOrder.length = 0; setupDone.clear(); visiting.clear(); internals.clear(); usedDependencies.clear(); for (const key of Object.keys(services)) delete services[key]; modelsInitialized = false; }
|
|
||||||
|
export function resetModuleRuntimeForTests(): void {
|
||||||
|
modules.clear();
|
||||||
|
models.length = 0;
|
||||||
|
setupOrder.length = 0;
|
||||||
|
setupDone.clear();
|
||||||
|
visiting.clear();
|
||||||
|
internals.clear();
|
||||||
|
usedDependencies.clear();
|
||||||
|
|
||||||
|
for (const key of Object.keys(services)) {
|
||||||
|
delete services[key];
|
||||||
|
}
|
||||||
|
modelsInitialized = false;
|
||||||
|
}
|
||||||
|
|||||||
@ -8,10 +8,17 @@ export interface RecurringProformaOccurrenceProcessorCapability {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RecurringProformaDiscoveryCapability {
|
export interface RecurringProformaDiscoveryCapability {
|
||||||
findDue(dueThrough: Date, limit: number): Promise<{
|
findDue(
|
||||||
|
dueThrough: Date,
|
||||||
|
limit: number
|
||||||
|
): Promise<{
|
||||||
isFailure: boolean;
|
isFailure: boolean;
|
||||||
error: Error;
|
error: Error;
|
||||||
data: readonly { recurringProformaId: UniqueID; companyId: UniqueID; nextOccurrenceDate: Date }[];
|
data: readonly {
|
||||||
|
recurringProformaId: UniqueID;
|
||||||
|
companyId: UniqueID;
|
||||||
|
nextOccurrenceDate: Date;
|
||||||
|
}[];
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import Dinero, { type Currency } from "dinero.js";
|
|
||||||
|
|
||||||
import type { MoneyDTO } from "../dto";
|
import type { MoneyDTO } from "../dto";
|
||||||
|
|
||||||
type DineroPlain = { amount: number; precision: number; currency: string };
|
//type DineroPlain = { amount: number; precision: number; currency: string };
|
||||||
|
|
||||||
const isEmptyMoneyDTO = (dto?: MoneyDTO | null) =>
|
const isEmptyMoneyDTO = (dto?: MoneyDTO | null) =>
|
||||||
!dto || dto.value?.trim?.() === "" || dto.scale?.trim?.() === "";
|
!dto || dto.value?.trim?.() === "" || dto.scale?.trim?.() === "";
|
||||||
@ -149,40 +147,40 @@ export const MoneyDTOHelper = {
|
|||||||
/**
|
/**
|
||||||
* Convierte un DTO a una instancia Dinero.js.
|
* Convierte un DTO a una instancia Dinero.js.
|
||||||
*/
|
*/
|
||||||
function dineroFromDTO(dto: MoneyDTO, fallbackCurrency = "EUR"): Dinero.Dinero {
|
/*function dineroFromDTO(dto: MoneyDTO, fallbackCurrency = "EUR"): Dinero.Dinero {
|
||||||
const n = normalizeDTO(dto, fallbackCurrency);
|
const n = normalizeDTO(dto, fallbackCurrency);
|
||||||
return Dinero({
|
return Dinero({
|
||||||
amount: Number.parseInt(n.value, 10),
|
amount: Number.parseInt(n.value, 10),
|
||||||
precision: Number.parseInt(n.scale, 10),
|
precision: Number.parseInt(n.scale, 10),
|
||||||
currency: n.currency_code as Currency,
|
currency: n.currency_code as Currency,
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convierte una instancia Dinero a un DTO.
|
* Convierte una instancia Dinero a un DTO.
|
||||||
*/
|
*/
|
||||||
function dtoFromDinero(d: Dinero.Dinero): MoneyDTO {
|
/*function dtoFromDinero(d: Dinero.Dinero): MoneyDTO {
|
||||||
const { amount, precision, currency } = d.toObject() as DineroPlain;
|
const { amount, precision, currency } = d.toObject() as DineroPlain;
|
||||||
return {
|
return {
|
||||||
value: amount.toString(),
|
value: amount.toString(),
|
||||||
scale: precision.toString(),
|
scale: precision.toString(),
|
||||||
currency_code: currency,
|
currency_code: currency,
|
||||||
};
|
};
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Suma una lista de MoneyDTO.
|
* Suma una lista de MoneyDTO.
|
||||||
*/
|
*/
|
||||||
function sumDTO(list: MoneyDTO[], fallbackCurrency = "EUR"): MoneyDTO {
|
/*function sumDTO(list: MoneyDTO[], fallbackCurrency = "EUR"): MoneyDTO {
|
||||||
if (list.length === 0) return { value: "0", scale: "2", currency_code: fallbackCurrency };
|
if (list.length === 0) return { value: "0", scale: "2", currency_code: fallbackCurrency };
|
||||||
const sum = list.map((x) => dineroFromDTO(x, fallbackCurrency)).reduce((a, b) => a.add(b));
|
const sum = list.map((x) => dineroFromDTO(x, fallbackCurrency)).reduce((a, b) => a.add(b));
|
||||||
return dtoFromDinero(sum);
|
return dtoFromDinero(sum);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiplica un MoneyDTO por un número.
|
* Multiplica un MoneyDTO por un número.
|
||||||
*/
|
*/
|
||||||
function multiplyDTO(
|
/*function multiplyDTO(
|
||||||
dto: MoneyDTO,
|
dto: MoneyDTO,
|
||||||
multiplier: number,
|
multiplier: number,
|
||||||
rounding: Dinero.RoundingMode = "HALF_EVEN",
|
rounding: Dinero.RoundingMode = "HALF_EVEN",
|
||||||
@ -190,12 +188,12 @@ function multiplyDTO(
|
|||||||
): MoneyDTO {
|
): MoneyDTO {
|
||||||
const d = dineroFromDTO(dto, fallbackCurrency).multiply(multiplier, rounding);
|
const d = dineroFromDTO(dto, fallbackCurrency).multiply(multiplier, rounding);
|
||||||
return dtoFromDinero(d);
|
return dtoFromDinero(d);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcula un porcentaje de un MoneyDTO.
|
* Calcula un porcentaje de un MoneyDTO.
|
||||||
*/
|
*/
|
||||||
function percentageDTO(
|
/*function percentageDTO(
|
||||||
dto: MoneyDTO,
|
dto: MoneyDTO,
|
||||||
percent: number,
|
percent: number,
|
||||||
rounding: Dinero.RoundingMode = "HALF_EVEN",
|
rounding: Dinero.RoundingMode = "HALF_EVEN",
|
||||||
@ -203,4 +201,4 @@ function percentageDTO(
|
|||||||
): MoneyDTO {
|
): MoneyDTO {
|
||||||
const d = dineroFromDTO(dto, fallbackCurrency).percentage(percent, rounding);
|
const d = dineroFromDTO(dto, fallbackCurrency).percentage(percent, rounding);
|
||||||
return dtoFromDinero(d);
|
return dtoFromDinero(d);
|
||||||
}
|
}*/
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export { Audit, type AuditDependencies } from "./audit";
|
export { Audit, type AuditDependencies } from "./audit";
|
||||||
export type { AuditArray, AuditObject, AuditScalar, AuditState, AuditValue } from "./audit-state.types";
|
|
||||||
export type {
|
export type {
|
||||||
AuditActor,
|
AuditActor,
|
||||||
AuditChange,
|
AuditChange,
|
||||||
@ -11,6 +10,7 @@ export type {
|
|||||||
AuditMutationEntry,
|
AuditMutationEntry,
|
||||||
AuditMutationTarget,
|
AuditMutationTarget,
|
||||||
AuditOperation,
|
AuditOperation,
|
||||||
|
AuditRecordOptions,
|
||||||
AuditScope,
|
AuditScope,
|
||||||
AuditSource,
|
AuditSource,
|
||||||
IAudit,
|
IAudit,
|
||||||
@ -22,6 +22,13 @@ export type {
|
|||||||
RecordAuditMutationInput,
|
RecordAuditMutationInput,
|
||||||
RecordAuditMutationShortcutInput,
|
RecordAuditMutationShortcutInput,
|
||||||
} from "./audit.types";
|
} from "./audit.types";
|
||||||
|
export type {
|
||||||
|
AuditArray,
|
||||||
|
AuditObject,
|
||||||
|
AuditScalar,
|
||||||
|
AuditState,
|
||||||
|
AuditValue,
|
||||||
|
} from "./audit-state.types";
|
||||||
export {
|
export {
|
||||||
AuditContextUnavailableError,
|
AuditContextUnavailableError,
|
||||||
AuditError,
|
AuditError,
|
||||||
|
|||||||
@ -75,7 +75,7 @@ export class CriteriaFromUrlConverter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return Object.values(tempFilters).filter(
|
return Object.values(tempFilters).filter(
|
||||||
(filter) =>
|
(filter): filter is FiltersPrimitives =>
|
||||||
filter.field !== undefined &&
|
filter.field !== undefined &&
|
||||||
filter.operator !== undefined &&
|
filter.operator !== undefined &&
|
||||||
(filter.value !== undefined ||
|
(filter.value !== undefined ||
|
||||||
|
|||||||
@ -1 +1,4 @@
|
|||||||
export type IAggregateRootRepository = {};
|
// biome-ignore lint/correctness/noUnusedVariables: <se necesita para definir la interfaz, aunque no se use directamente>
|
||||||
|
export type IAggregateRootRepository<T> = {
|
||||||
|
// Define the methods for the aggregate root repository
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user