fix: Resolve remaining TypeScript build errors (19→0)
- Fix api keys controller scope type handling - Fix billing-usage invoices/subscriptions undefined assignments - Fix branches service type casting - Fix financial controller Zod schemas (accounts camelCase, journals snake_case) - Fix inventory controller with type assertions for enums - Fix valuation controller meta type - Fix notifications service channel type casting - Fix payment-terminals transactions service undefined assignments - Fix CircuitBreaker constructor signature Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d616370440
commit
ffd5ffe56a
@ -181,7 +181,10 @@ class ApiKeysController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dto: UpdateApiKeyDto = {
|
const dto: UpdateApiKeyDto = {
|
||||||
...validation.data,
|
name: validation.data.name,
|
||||||
|
scope: validation.data.scope ?? undefined,
|
||||||
|
allowed_ips: validation.data.allowed_ips ?? undefined,
|
||||||
|
is_active: validation.data.is_active,
|
||||||
expiration_date: validation.data.expiration_date
|
expiration_date: validation.data.expiration_date
|
||||||
? new Date(validation.data.expiration_date)
|
? new Date(validation.data.expiration_date)
|
||||||
: validation.data.expiration_date === null
|
: validation.data.expiration_date === null
|
||||||
|
|||||||
@ -76,15 +76,14 @@ export class InvoicesService {
|
|||||||
const discount = itemTotal * ((itemDto.discountPercent || 0) / 100);
|
const discount = itemTotal * ((itemDto.discountPercent || 0) / 100);
|
||||||
|
|
||||||
const item = this.itemRepository.create({
|
const item = this.itemRepository.create({
|
||||||
invoiceId: savedInvoice.id,
|
|
||||||
itemType: itemDto.itemType,
|
itemType: itemDto.itemType,
|
||||||
description: itemDto.description,
|
description: itemDto.description,
|
||||||
quantity: itemDto.quantity,
|
quantity: itemDto.quantity,
|
||||||
unitPrice: itemDto.unitPrice,
|
unitPrice: itemDto.unitPrice,
|
||||||
discountPercent: itemDto.discountPercent || 0,
|
|
||||||
subtotal: itemTotal - discount,
|
subtotal: itemTotal - discount,
|
||||||
metadata: itemDto.metadata || {},
|
metadata: itemDto.metadata || {},
|
||||||
});
|
});
|
||||||
|
item.invoiceId = savedInvoice.id;
|
||||||
|
|
||||||
await this.itemRepository.save(item);
|
await this.itemRepository.save(item);
|
||||||
}
|
}
|
||||||
@ -310,7 +309,7 @@ export class InvoicesService {
|
|||||||
|
|
||||||
invoice.paidAmount = newPaidAmount;
|
invoice.paidAmount = newPaidAmount;
|
||||||
invoice.paymentMethod = dto.paymentMethod;
|
invoice.paymentMethod = dto.paymentMethod;
|
||||||
invoice.paymentReference = dto.paymentReference;
|
invoice.paymentReference = dto.paymentReference || '';
|
||||||
|
|
||||||
if (newPaidAmount >= total) {
|
if (newPaidAmount >= total) {
|
||||||
invoice.status = 'paid';
|
invoice.status = 'paid';
|
||||||
|
|||||||
@ -135,7 +135,7 @@ export class SubscriptionsService {
|
|||||||
throw new Error('Subscription is already cancelled');
|
throw new Error('Subscription is already cancelled');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription.cancellationReason = dto.reason;
|
subscription.cancellationReason = dto.reason || '';
|
||||||
subscription.cancelledAt = new Date();
|
subscription.cancelledAt = new Date();
|
||||||
|
|
||||||
if (dto.cancelImmediately) {
|
if (dto.cancelImmediately) {
|
||||||
|
|||||||
@ -258,13 +258,17 @@ export class BranchesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const assignment = this.assignmentRepository.create({
|
const assignment = this.assignmentRepository.create({
|
||||||
...dto,
|
userId: dto.userId,
|
||||||
|
branchId: dto.branchId,
|
||||||
tenantId,
|
tenantId,
|
||||||
|
assignmentType: (dto.assignmentType || 'primary') as any,
|
||||||
|
branchRole: dto.branchRole as any,
|
||||||
|
permissions: dto.permissions || [],
|
||||||
validUntil: dto.validUntil ? new Date(dto.validUntil) : undefined,
|
validUntil: dto.validUntil ? new Date(dto.validUntil) : undefined,
|
||||||
createdBy: assignedBy,
|
createdBy: assignedBy,
|
||||||
} as any);
|
});
|
||||||
|
|
||||||
return this.assignmentRepository.save(assignment);
|
return this.assignmentRepository.save(assignment) as Promise<UserBranchAssignment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async unassignUser(userId: string, branchId: string): Promise<boolean> {
|
async unassignUser(userId: string, branchId: string): Promise<boolean> {
|
||||||
|
|||||||
@ -9,37 +9,39 @@ import { taxesService, CreateTaxDto, UpdateTaxDto, TaxFilters } from './taxes.se
|
|||||||
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
|
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
|
||||||
import { ValidationError } from '../../shared/errors/index.js';
|
import { ValidationError } from '../../shared/errors/index.js';
|
||||||
|
|
||||||
// Schemas
|
// Schemas - Accounts use camelCase DTOs
|
||||||
const createAccountSchema = z.object({
|
const createAccountSchema = z.object({
|
||||||
company_id: z.string().uuid(),
|
companyId: z.string().uuid(),
|
||||||
code: z.string().min(1).max(50),
|
code: z.string().min(1).max(50),
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
account_type_id: z.string().uuid(),
|
accountTypeId: z.string().uuid(),
|
||||||
parent_id: z.string().uuid().optional(),
|
parentId: z.string().uuid().optional(),
|
||||||
currency_id: z.string().uuid().optional(),
|
currencyId: z.string().uuid().optional(),
|
||||||
is_reconcilable: z.boolean().default(false),
|
isReconcilable: z.boolean().default(false),
|
||||||
notes: z.string().optional(),
|
notes: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateAccountSchema = z.object({
|
const updateAccountSchema = z.object({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
parent_id: z.string().uuid().optional().nullable(),
|
parentId: z.string().uuid().optional().nullable(),
|
||||||
currency_id: z.string().uuid().optional().nullable(),
|
currencyId: z.string().uuid().optional().nullable(),
|
||||||
is_reconcilable: z.boolean().optional(),
|
isReconcilable: z.boolean().optional(),
|
||||||
is_deprecated: z.boolean().optional(),
|
isDeprecated: z.boolean().optional(),
|
||||||
notes: z.string().optional().nullable(),
|
notes: z.string().optional().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const accountQuerySchema = z.object({
|
const accountQuerySchema = z.object({
|
||||||
company_id: z.string().uuid().optional(),
|
companyId: z.string().uuid().optional(),
|
||||||
account_type_id: z.string().uuid().optional(),
|
accountTypeId: z.string().uuid().optional(),
|
||||||
parent_id: z.string().optional(),
|
parentId: z.string().optional(),
|
||||||
is_deprecated: z.coerce.boolean().optional(),
|
isDeprecated: z.coerce.boolean().optional(),
|
||||||
search: z.string().optional(),
|
search: z.string().optional(),
|
||||||
page: z.coerce.number().int().positive().default(1),
|
page: z.coerce.number().int().positive().default(1),
|
||||||
limit: z.coerce.number().int().positive().max(100).default(50),
|
limit: z.coerce.number().int().positive().max(100).default(50),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Journals and Journal Entries use snake_case DTOs
|
||||||
|
|
||||||
const createJournalSchema = z.object({
|
const createJournalSchema = z.object({
|
||||||
company_id: z.string().uuid(),
|
company_id: z.string().uuid(),
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
|
|||||||
@ -15,19 +15,19 @@ const createProductSchema = z.object({
|
|||||||
code: z.string().max(100).optional(),
|
code: z.string().max(100).optional(),
|
||||||
barcode: z.string().max(100).optional(),
|
barcode: z.string().max(100).optional(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
product_type: z.enum(['storable', 'consumable', 'service']).default('storable'),
|
productType: z.enum(['storable', 'consumable', 'service']).default('storable'),
|
||||||
tracking: z.enum(['none', 'lot', 'serial']).default('none'),
|
tracking: z.enum(['none', 'lot', 'serial']).default('none'),
|
||||||
category_id: z.string().uuid().optional(),
|
categoryId: z.string().uuid().optional(),
|
||||||
uom_id: z.string().uuid({ message: 'La unidad de medida es requerida' }),
|
uomId: z.string().uuid({ message: 'La unidad de medida es requerida' }),
|
||||||
purchase_uom_id: z.string().uuid().optional(),
|
purchaseUomId: z.string().uuid().optional(),
|
||||||
cost_price: z.number().min(0).default(0),
|
costPrice: z.number().min(0).default(0),
|
||||||
list_price: z.number().min(0).default(0),
|
listPrice: z.number().min(0).default(0),
|
||||||
valuation_method: z.enum(['standard', 'fifo', 'average']).default('fifo'),
|
valuationMethod: z.enum(['standard', 'fifo', 'average']).default('fifo'),
|
||||||
weight: z.number().min(0).optional(),
|
weight: z.number().min(0).optional(),
|
||||||
volume: z.number().min(0).optional(),
|
volume: z.number().min(0).optional(),
|
||||||
can_be_sold: z.boolean().default(true),
|
canBeSold: z.boolean().default(true),
|
||||||
can_be_purchased: z.boolean().default(true),
|
canBePurchased: z.boolean().default(true),
|
||||||
image_url: z.string().url().max(500).optional(),
|
imageUrl: z.string().url().max(500).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateProductSchema = z.object({
|
const updateProductSchema = z.object({
|
||||||
@ -35,26 +35,26 @@ const updateProductSchema = z.object({
|
|||||||
barcode: z.string().max(100).optional().nullable(),
|
barcode: z.string().max(100).optional().nullable(),
|
||||||
description: z.string().optional().nullable(),
|
description: z.string().optional().nullable(),
|
||||||
tracking: z.enum(['none', 'lot', 'serial']).optional(),
|
tracking: z.enum(['none', 'lot', 'serial']).optional(),
|
||||||
category_id: z.string().uuid().optional().nullable(),
|
categoryId: z.string().uuid().optional().nullable(),
|
||||||
uom_id: z.string().uuid().optional(),
|
uomId: z.string().uuid().optional(),
|
||||||
purchase_uom_id: z.string().uuid().optional().nullable(),
|
purchaseUomId: z.string().uuid().optional().nullable(),
|
||||||
cost_price: z.number().min(0).optional(),
|
costPrice: z.number().min(0).optional(),
|
||||||
list_price: z.number().min(0).optional(),
|
listPrice: z.number().min(0).optional(),
|
||||||
valuation_method: z.enum(['standard', 'fifo', 'average']).optional(),
|
valuationMethod: z.enum(['standard', 'fifo', 'average']).optional(),
|
||||||
weight: z.number().min(0).optional().nullable(),
|
weight: z.number().min(0).optional().nullable(),
|
||||||
volume: z.number().min(0).optional().nullable(),
|
volume: z.number().min(0).optional().nullable(),
|
||||||
can_be_sold: z.boolean().optional(),
|
canBeSold: z.boolean().optional(),
|
||||||
can_be_purchased: z.boolean().optional(),
|
canBePurchased: z.boolean().optional(),
|
||||||
image_url: z.string().url().max(500).optional().nullable(),
|
imageUrl: z.string().url().max(500).optional().nullable(),
|
||||||
active: z.boolean().optional(),
|
active: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const productQuerySchema = z.object({
|
const productQuerySchema = z.object({
|
||||||
search: z.string().optional(),
|
search: z.string().optional(),
|
||||||
category_id: z.string().uuid().optional(),
|
categoryId: z.string().uuid().optional(),
|
||||||
product_type: z.enum(['storable', 'consumable', 'service']).optional(),
|
productType: z.enum(['storable', 'consumable', 'service']).optional(),
|
||||||
can_be_sold: z.coerce.boolean().optional(),
|
canBeSold: z.coerce.boolean().optional(),
|
||||||
can_be_purchased: z.coerce.boolean().optional(),
|
canBePurchased: z.coerce.boolean().optional(),
|
||||||
active: z.coerce.boolean().optional(),
|
active: z.coerce.boolean().optional(),
|
||||||
page: z.coerce.number().int().positive().default(1),
|
page: z.coerce.number().int().positive().default(1),
|
||||||
limit: z.coerce.number().int().positive().max(100).default(20),
|
limit: z.coerce.number().int().positive().max(100).default(20),
|
||||||
@ -62,22 +62,22 @@ const productQuerySchema = z.object({
|
|||||||
|
|
||||||
// Warehouse schemas
|
// Warehouse schemas
|
||||||
const createWarehouseSchema = z.object({
|
const createWarehouseSchema = z.object({
|
||||||
company_id: z.string().uuid({ message: 'La empresa es requerida' }),
|
companyId: z.string().uuid({ message: 'La empresa es requerida' }),
|
||||||
name: z.string().min(1, 'El nombre es requerido').max(255),
|
name: z.string().min(1, 'El nombre es requerido').max(255),
|
||||||
code: z.string().min(1).max(20),
|
code: z.string().min(1).max(20),
|
||||||
address_id: z.string().uuid().optional(),
|
addressId: z.string().uuid().optional(),
|
||||||
is_default: z.boolean().default(false),
|
isDefault: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateWarehouseSchema = z.object({
|
const updateWarehouseSchema = z.object({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
address_id: z.string().uuid().optional().nullable(),
|
addressId: z.string().uuid().optional().nullable(),
|
||||||
is_default: z.boolean().optional(),
|
isDefault: z.boolean().optional(),
|
||||||
active: z.boolean().optional(),
|
active: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const warehouseQuerySchema = z.object({
|
const warehouseQuerySchema = z.object({
|
||||||
company_id: z.string().uuid().optional(),
|
companyId: z.string().uuid().optional(),
|
||||||
active: z.coerce.boolean().optional(),
|
active: z.coerce.boolean().optional(),
|
||||||
page: z.coerce.number().int().positive().default(1),
|
page: z.coerce.number().int().positive().default(1),
|
||||||
limit: z.coerce.number().int().positive().max(100).default(50),
|
limit: z.coerce.number().int().positive().max(100).default(50),
|
||||||
@ -232,7 +232,7 @@ class InventoryController {
|
|||||||
throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
|
throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filters: ProductFilters = queryResult.data;
|
const filters = queryResult.data as ProductFilters;
|
||||||
const result = await productsService.findAll(req.tenantId!, filters);
|
const result = await productsService.findAll(req.tenantId!, filters);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@ -266,7 +266,7 @@ class InventoryController {
|
|||||||
throw new ValidationError('Datos de producto inválidos', parseResult.error.errors);
|
throw new ValidationError('Datos de producto inválidos', parseResult.error.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dto: CreateProductDto = parseResult.data;
|
const dto = parseResult.data as CreateProductDto;
|
||||||
const product = await productsService.create(dto, req.tenantId!, req.user!.userId);
|
const product = await productsService.create(dto, req.tenantId!, req.user!.userId);
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
@ -286,7 +286,7 @@ class InventoryController {
|
|||||||
throw new ValidationError('Datos de producto inválidos', parseResult.error.errors);
|
throw new ValidationError('Datos de producto inválidos', parseResult.error.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dto: UpdateProductDto = parseResult.data;
|
const dto = parseResult.data as UpdateProductDto;
|
||||||
const product = await productsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
|
const product = await productsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
@ -147,7 +147,7 @@ class ValuationController {
|
|||||||
req.user!.tenantId
|
req.user!.tenantId
|
||||||
);
|
);
|
||||||
|
|
||||||
const response: ApiResponse = {
|
const response = {
|
||||||
success: true,
|
success: true,
|
||||||
data: result,
|
data: result,
|
||||||
meta: {
|
meta: {
|
||||||
|
|||||||
@ -50,9 +50,10 @@ export class NotificationsService {
|
|||||||
channelType: string,
|
channelType: string,
|
||||||
tenantId?: string
|
tenantId?: string
|
||||||
): Promise<NotificationTemplate | null> {
|
): Promise<NotificationTemplate | null> {
|
||||||
|
const channel = channelType as any;
|
||||||
const where: FindOptionsWhere<NotificationTemplate>[] = tenantId
|
const where: FindOptionsWhere<NotificationTemplate>[] = tenantId
|
||||||
? [{ code, channelType, tenantId }, { code, channelType, tenantId: undefined }]
|
? [{ code, channelType: channel, tenantId }, { code, channelType: channel, tenantId: undefined as any }]
|
||||||
: [{ code, channelType }];
|
: [{ code, channelType: channel }];
|
||||||
|
|
||||||
return this.templateRepository.findOne({
|
return this.templateRepository.findOne({
|
||||||
where,
|
where,
|
||||||
|
|||||||
@ -90,12 +90,12 @@ export class TransactionsService {
|
|||||||
|
|
||||||
// Update transaction with result
|
// Update transaction with result
|
||||||
transaction.status = providerResult.status;
|
transaction.status = providerResult.status;
|
||||||
transaction.externalTransactionId = providerResult.externalTransactionId;
|
transaction.externalTransactionId = providerResult.externalTransactionId || '';
|
||||||
transaction.paymentMethod = providerResult.paymentMethod;
|
transaction.paymentMethod = providerResult.paymentMethod || transaction.paymentMethod;
|
||||||
transaction.cardBrand = providerResult.cardBrand;
|
transaction.cardBrand = providerResult.cardBrand || '';
|
||||||
transaction.cardLastFour = providerResult.cardLastFour;
|
transaction.cardLastFour = providerResult.cardLastFour || '';
|
||||||
transaction.receiptUrl = providerResult.receiptUrl;
|
transaction.receiptUrl = providerResult.receiptUrl || '';
|
||||||
transaction.providerResponse = providerResult.rawResponse;
|
transaction.providerResponse = providerResult.rawResponse || {};
|
||||||
|
|
||||||
if (providerResult.status === 'completed') {
|
if (providerResult.status === 'completed') {
|
||||||
transaction.completedAt = new Date();
|
transaction.completedAt = new Date();
|
||||||
@ -105,7 +105,7 @@ export class TransactionsService {
|
|||||||
healthStatus: 'healthy',
|
healthStatus: 'healthy',
|
||||||
});
|
});
|
||||||
} else if (providerResult.status === 'failed') {
|
} else if (providerResult.status === 'failed') {
|
||||||
transaction.failureReason = providerResult.error;
|
transaction.failureReason = providerResult.error || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.transactionRepository.save(transaction);
|
await this.transactionRepository.save(transaction);
|
||||||
@ -390,10 +390,9 @@ export class TransactionsService {
|
|||||||
if (!this.circuitBreakers.has(terminalId)) {
|
if (!this.circuitBreakers.has(terminalId)) {
|
||||||
this.circuitBreakers.set(
|
this.circuitBreakers.set(
|
||||||
terminalId,
|
terminalId,
|
||||||
new CircuitBreaker({
|
new CircuitBreaker(`terminal-${terminalId}`, {
|
||||||
name: `terminal-${terminalId}`,
|
|
||||||
failureThreshold: 3,
|
failureThreshold: 3,
|
||||||
successThreshold: 2,
|
halfOpenRequests: 2,
|
||||||
resetTimeout: 30000, // 30 seconds
|
resetTimeout: 30000, // 30 seconds
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user