diff --git a/src/modules/auth/apiKeys.controller.ts b/src/modules/auth/apiKeys.controller.ts index bb6cb71..a637387 100644 --- a/src/modules/auth/apiKeys.controller.ts +++ b/src/modules/auth/apiKeys.controller.ts @@ -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 diff --git a/src/modules/billing-usage/services/invoices.service.ts b/src/modules/billing-usage/services/invoices.service.ts index 117fc8e..ac56c9d 100644 --- a/src/modules/billing-usage/services/invoices.service.ts +++ b/src/modules/billing-usage/services/invoices.service.ts @@ -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'; diff --git a/src/modules/billing-usage/services/subscriptions.service.ts b/src/modules/billing-usage/services/subscriptions.service.ts index c693f05..25851b1 100644 --- a/src/modules/billing-usage/services/subscriptions.service.ts +++ b/src/modules/billing-usage/services/subscriptions.service.ts @@ -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) { diff --git a/src/modules/branches/services/branches.service.ts b/src/modules/branches/services/branches.service.ts index 51fefa8..e06f010 100644 --- a/src/modules/branches/services/branches.service.ts +++ b/src/modules/branches/services/branches.service.ts @@ -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; } async unassignUser(userId: string, branchId: string): Promise { diff --git a/src/modules/financial/financial.controller.ts b/src/modules/financial/financial.controller.ts index b2d7822..f9b06d7 100644 --- a/src/modules/financial/financial.controller.ts +++ b/src/modules/financial/financial.controller.ts @@ -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), diff --git a/src/modules/inventory/inventory.controller.ts b/src/modules/inventory/inventory.controller.ts index de2891a..96d3223 100644 --- a/src/modules/inventory/inventory.controller.ts +++ b/src/modules/inventory/inventory.controller.ts @@ -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({ diff --git a/src/modules/inventory/valuation.controller.ts b/src/modules/inventory/valuation.controller.ts index 01a9c7d..b72a96e 100644 --- a/src/modules/inventory/valuation.controller.ts +++ b/src/modules/inventory/valuation.controller.ts @@ -147,7 +147,7 @@ class ValuationController { req.user!.tenantId ); - const response: ApiResponse = { + const response = { success: true, data: result, meta: { diff --git a/src/modules/notifications/services/notifications.service.ts b/src/modules/notifications/services/notifications.service.ts index e54d653..4feaa66 100644 --- a/src/modules/notifications/services/notifications.service.ts +++ b/src/modules/notifications/services/notifications.service.ts @@ -50,9 +50,10 @@ export class NotificationsService { channelType: string, tenantId?: string ): Promise { + const channel = channelType as any; const where: FindOptionsWhere[] = 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, diff --git a/src/modules/payment-terminals/services/transactions.service.ts b/src/modules/payment-terminals/services/transactions.service.ts index ed574d0..146fde5 100644 --- a/src/modules/payment-terminals/services/transactions.service.ts +++ b/src/modules/payment-terminals/services/transactions.service.ts @@ -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 }) );