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 = {
|
||||
...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
|
||||
? new Date(validation.data.expiration_date)
|
||||
: validation.data.expiration_date === null
|
||||
|
||||
@ -76,15 +76,14 @@ export class InvoicesService {
|
||||
const discount = itemTotal * ((itemDto.discountPercent || 0) / 100);
|
||||
|
||||
const item = this.itemRepository.create({
|
||||
invoiceId: savedInvoice.id,
|
||||
itemType: itemDto.itemType,
|
||||
description: itemDto.description,
|
||||
quantity: itemDto.quantity,
|
||||
unitPrice: itemDto.unitPrice,
|
||||
discountPercent: itemDto.discountPercent || 0,
|
||||
subtotal: itemTotal - discount,
|
||||
metadata: itemDto.metadata || {},
|
||||
});
|
||||
item.invoiceId = savedInvoice.id;
|
||||
|
||||
await this.itemRepository.save(item);
|
||||
}
|
||||
@ -310,7 +309,7 @@ export class InvoicesService {
|
||||
|
||||
invoice.paidAmount = newPaidAmount;
|
||||
invoice.paymentMethod = dto.paymentMethod;
|
||||
invoice.paymentReference = dto.paymentReference;
|
||||
invoice.paymentReference = dto.paymentReference || '';
|
||||
|
||||
if (newPaidAmount >= total) {
|
||||
invoice.status = 'paid';
|
||||
|
||||
@ -135,7 +135,7 @@ export class SubscriptionsService {
|
||||
throw new Error('Subscription is already cancelled');
|
||||
}
|
||||
|
||||
subscription.cancellationReason = dto.reason;
|
||||
subscription.cancellationReason = dto.reason || '';
|
||||
subscription.cancelledAt = new Date();
|
||||
|
||||
if (dto.cancelImmediately) {
|
||||
|
||||
@ -258,13 +258,17 @@ export class BranchesService {
|
||||
}
|
||||
|
||||
const assignment = this.assignmentRepository.create({
|
||||
...dto,
|
||||
userId: dto.userId,
|
||||
branchId: dto.branchId,
|
||||
tenantId,
|
||||
assignmentType: (dto.assignmentType || 'primary') as any,
|
||||
branchRole: dto.branchRole as any,
|
||||
permissions: dto.permissions || [],
|
||||
validUntil: dto.validUntil ? new Date(dto.validUntil) : undefined,
|
||||
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> {
|
||||
|
||||
@ -9,37 +9,39 @@ import { taxesService, CreateTaxDto, UpdateTaxDto, TaxFilters } from './taxes.se
|
||||
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
|
||||
import { ValidationError } from '../../shared/errors/index.js';
|
||||
|
||||
// Schemas
|
||||
// Schemas - Accounts use camelCase DTOs
|
||||
const createAccountSchema = z.object({
|
||||
company_id: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
code: z.string().min(1).max(50),
|
||||
name: z.string().min(1).max(255),
|
||||
account_type_id: z.string().uuid(),
|
||||
parent_id: z.string().uuid().optional(),
|
||||
currency_id: z.string().uuid().optional(),
|
||||
is_reconcilable: z.boolean().default(false),
|
||||
accountTypeId: z.string().uuid(),
|
||||
parentId: z.string().uuid().optional(),
|
||||
currencyId: z.string().uuid().optional(),
|
||||
isReconcilable: z.boolean().default(false),
|
||||
notes: z.string().optional(),
|
||||
});
|
||||
|
||||
const updateAccountSchema = z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
parent_id: z.string().uuid().optional().nullable(),
|
||||
currency_id: z.string().uuid().optional().nullable(),
|
||||
is_reconcilable: z.boolean().optional(),
|
||||
is_deprecated: z.boolean().optional(),
|
||||
parentId: z.string().uuid().optional().nullable(),
|
||||
currencyId: z.string().uuid().optional().nullable(),
|
||||
isReconcilable: z.boolean().optional(),
|
||||
isDeprecated: z.boolean().optional(),
|
||||
notes: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
const accountQuerySchema = z.object({
|
||||
company_id: z.string().uuid().optional(),
|
||||
account_type_id: z.string().uuid().optional(),
|
||||
parent_id: z.string().optional(),
|
||||
is_deprecated: z.coerce.boolean().optional(),
|
||||
companyId: z.string().uuid().optional(),
|
||||
accountTypeId: z.string().uuid().optional(),
|
||||
parentId: z.string().optional(),
|
||||
isDeprecated: z.coerce.boolean().optional(),
|
||||
search: z.string().optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().positive().max(100).default(50),
|
||||
});
|
||||
|
||||
// Journals and Journal Entries use snake_case DTOs
|
||||
|
||||
const createJournalSchema = z.object({
|
||||
company_id: z.string().uuid(),
|
||||
name: z.string().min(1).max(255),
|
||||
|
||||
@ -15,19 +15,19 @@ const createProductSchema = z.object({
|
||||
code: z.string().max(100).optional(),
|
||||
barcode: z.string().max(100).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'),
|
||||
category_id: z.string().uuid().optional(),
|
||||
uom_id: z.string().uuid({ message: 'La unidad de medida es requerida' }),
|
||||
purchase_uom_id: z.string().uuid().optional(),
|
||||
cost_price: z.number().min(0).default(0),
|
||||
list_price: z.number().min(0).default(0),
|
||||
valuation_method: z.enum(['standard', 'fifo', 'average']).default('fifo'),
|
||||
categoryId: z.string().uuid().optional(),
|
||||
uomId: z.string().uuid({ message: 'La unidad de medida es requerida' }),
|
||||
purchaseUomId: z.string().uuid().optional(),
|
||||
costPrice: z.number().min(0).default(0),
|
||||
listPrice: z.number().min(0).default(0),
|
||||
valuationMethod: z.enum(['standard', 'fifo', 'average']).default('fifo'),
|
||||
weight: z.number().min(0).optional(),
|
||||
volume: z.number().min(0).optional(),
|
||||
can_be_sold: z.boolean().default(true),
|
||||
can_be_purchased: z.boolean().default(true),
|
||||
image_url: z.string().url().max(500).optional(),
|
||||
canBeSold: z.boolean().default(true),
|
||||
canBePurchased: z.boolean().default(true),
|
||||
imageUrl: z.string().url().max(500).optional(),
|
||||
});
|
||||
|
||||
const updateProductSchema = z.object({
|
||||
@ -35,26 +35,26 @@ const updateProductSchema = z.object({
|
||||
barcode: z.string().max(100).optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
tracking: z.enum(['none', 'lot', 'serial']).optional(),
|
||||
category_id: z.string().uuid().optional().nullable(),
|
||||
uom_id: z.string().uuid().optional(),
|
||||
purchase_uom_id: z.string().uuid().optional().nullable(),
|
||||
cost_price: z.number().min(0).optional(),
|
||||
list_price: z.number().min(0).optional(),
|
||||
valuation_method: z.enum(['standard', 'fifo', 'average']).optional(),
|
||||
categoryId: z.string().uuid().optional().nullable(),
|
||||
uomId: z.string().uuid().optional(),
|
||||
purchaseUomId: z.string().uuid().optional().nullable(),
|
||||
costPrice: z.number().min(0).optional(),
|
||||
listPrice: z.number().min(0).optional(),
|
||||
valuationMethod: z.enum(['standard', 'fifo', 'average']).optional(),
|
||||
weight: z.number().min(0).optional().nullable(),
|
||||
volume: z.number().min(0).optional().nullable(),
|
||||
can_be_sold: z.boolean().optional(),
|
||||
can_be_purchased: z.boolean().optional(),
|
||||
image_url: z.string().url().max(500).optional().nullable(),
|
||||
canBeSold: z.boolean().optional(),
|
||||
canBePurchased: z.boolean().optional(),
|
||||
imageUrl: z.string().url().max(500).optional().nullable(),
|
||||
active: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const productQuerySchema = z.object({
|
||||
search: z.string().optional(),
|
||||
category_id: z.string().uuid().optional(),
|
||||
product_type: z.enum(['storable', 'consumable', 'service']).optional(),
|
||||
can_be_sold: z.coerce.boolean().optional(),
|
||||
can_be_purchased: z.coerce.boolean().optional(),
|
||||
categoryId: z.string().uuid().optional(),
|
||||
productType: z.enum(['storable', 'consumable', 'service']).optional(),
|
||||
canBeSold: z.coerce.boolean().optional(),
|
||||
canBePurchased: z.coerce.boolean().optional(),
|
||||
active: z.coerce.boolean().optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().positive().max(100).default(20),
|
||||
@ -62,22 +62,22 @@ const productQuerySchema = z.object({
|
||||
|
||||
// Warehouse schemas
|
||||
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),
|
||||
code: z.string().min(1).max(20),
|
||||
address_id: z.string().uuid().optional(),
|
||||
is_default: z.boolean().default(false),
|
||||
addressId: z.string().uuid().optional(),
|
||||
isDefault: z.boolean().default(false),
|
||||
});
|
||||
|
||||
const updateWarehouseSchema = z.object({
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
address_id: z.string().uuid().optional().nullable(),
|
||||
is_default: z.boolean().optional(),
|
||||
addressId: z.string().uuid().optional().nullable(),
|
||||
isDefault: z.boolean().optional(),
|
||||
active: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const warehouseQuerySchema = z.object({
|
||||
company_id: z.string().uuid().optional(),
|
||||
companyId: z.string().uuid().optional(),
|
||||
active: z.coerce.boolean().optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
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);
|
||||
}
|
||||
|
||||
const filters: ProductFilters = queryResult.data;
|
||||
const filters = queryResult.data as ProductFilters;
|
||||
const result = await productsService.findAll(req.tenantId!, filters);
|
||||
|
||||
res.json({
|
||||
@ -266,7 +266,7 @@ class InventoryController {
|
||||
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);
|
||||
|
||||
res.status(201).json({
|
||||
@ -286,7 +286,7 @@ class InventoryController {
|
||||
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);
|
||||
|
||||
res.json({
|
||||
|
||||
@ -147,7 +147,7 @@ class ValuationController {
|
||||
req.user!.tenantId
|
||||
);
|
||||
|
||||
const response: ApiResponse = {
|
||||
const response = {
|
||||
success: true,
|
||||
data: result,
|
||||
meta: {
|
||||
|
||||
@ -50,9 +50,10 @@ export class NotificationsService {
|
||||
channelType: string,
|
||||
tenantId?: string
|
||||
): Promise<NotificationTemplate | null> {
|
||||
const channel = channelType as any;
|
||||
const where: FindOptionsWhere<NotificationTemplate>[] = tenantId
|
||||
? [{ code, channelType, tenantId }, { code, channelType, tenantId: undefined }]
|
||||
: [{ code, channelType }];
|
||||
? [{ code, channelType: channel, tenantId }, { code, channelType: channel, tenantId: undefined as any }]
|
||||
: [{ code, channelType: channel }];
|
||||
|
||||
return this.templateRepository.findOne({
|
||||
where,
|
||||
|
||||
@ -90,12 +90,12 @@ export class TransactionsService {
|
||||
|
||||
// Update transaction with result
|
||||
transaction.status = providerResult.status;
|
||||
transaction.externalTransactionId = providerResult.externalTransactionId;
|
||||
transaction.paymentMethod = providerResult.paymentMethod;
|
||||
transaction.cardBrand = providerResult.cardBrand;
|
||||
transaction.cardLastFour = providerResult.cardLastFour;
|
||||
transaction.receiptUrl = providerResult.receiptUrl;
|
||||
transaction.providerResponse = providerResult.rawResponse;
|
||||
transaction.externalTransactionId = providerResult.externalTransactionId || '';
|
||||
transaction.paymentMethod = providerResult.paymentMethod || transaction.paymentMethod;
|
||||
transaction.cardBrand = providerResult.cardBrand || '';
|
||||
transaction.cardLastFour = providerResult.cardLastFour || '';
|
||||
transaction.receiptUrl = providerResult.receiptUrl || '';
|
||||
transaction.providerResponse = providerResult.rawResponse || {};
|
||||
|
||||
if (providerResult.status === 'completed') {
|
||||
transaction.completedAt = new Date();
|
||||
@ -105,7 +105,7 @@ export class TransactionsService {
|
||||
healthStatus: 'healthy',
|
||||
});
|
||||
} else if (providerResult.status === 'failed') {
|
||||
transaction.failureReason = providerResult.error;
|
||||
transaction.failureReason = providerResult.error || '';
|
||||
}
|
||||
|
||||
await this.transactionRepository.save(transaction);
|
||||
@ -390,10 +390,9 @@ export class TransactionsService {
|
||||
if (!this.circuitBreakers.has(terminalId)) {
|
||||
this.circuitBreakers.set(
|
||||
terminalId,
|
||||
new CircuitBreaker({
|
||||
name: `terminal-${terminalId}`,
|
||||
new CircuitBreaker(`terminal-${terminalId}`, {
|
||||
failureThreshold: 3,
|
||||
successThreshold: 2,
|
||||
halfOpenRequests: 2,
|
||||
resetTimeout: 30000, // 30 seconds
|
||||
})
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user