erp-transportistas-backend-v2/src/modules/payment-terminals/controllers/transactions.controller.ts
Adrian Flores Cortes 95c6b58449 feat: Add base modules from erp-core following SIMCO-REUSE directive
Phase 0 - Base modules (100% copy):
- shared/ (errors, middleware, services, utils, types)
- auth, users, tenants (multi-tenancy)
- ai, audit, notifications, mcp, payment-terminals
- billing-usage, branches, companies, core

Phase 1 - Modules to adapt (70-95%):
- partners (for shippers/consignees)
- inventory (for refacciones)
- financial (for transport costing)

Phase 2 - Pattern modules (50-70%):
- ordenes-transporte (from sales)
- gestion-flota (from products)
- viajes (from projects)

Phase 3 - New transport-specific modules:
- tracking (GPS, events, alerts)
- tarifas-transporte (pricing, surcharges)
- combustible-gastos (fuel, tolls, expenses)
- carta-porte (CFDI complement 3.1)

Estimated token savings: ~65% (~10,675 lines)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 10:10:19 -06:00

164 lines
4.8 KiB
TypeScript

/**
* Transactions Controller
*
* REST API endpoints for payment transactions
*/
import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { TransactionsService } from '../services';
import { ProcessPaymentDto, ProcessRefundDto, SendReceiptDto, TransactionFilterDto } from '../dto';
// Extend Request to include tenant info
interface AuthenticatedRequest extends Request {
tenantId?: string;
userId?: string;
}
export class TransactionsController {
public router: Router;
private service: TransactionsService;
constructor(dataSource: DataSource) {
this.router = Router();
this.service = new TransactionsService(dataSource);
this.initializeRoutes();
}
private initializeRoutes(): void {
// Stats
this.router.get('/stats', this.getStats.bind(this));
// Payment processing
this.router.post('/charge', this.processPayment.bind(this));
this.router.post('/refund', this.processRefund.bind(this));
// Transaction queries
this.router.get('/', this.getAll.bind(this));
this.router.get('/:id', this.getById.bind(this));
// Actions
this.router.post('/:id/receipt', this.sendReceipt.bind(this));
}
/**
* GET /payment-transactions/stats
* Get transaction statistics
*/
private async getStats(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const filter: TransactionFilterDto = {
branchId: req.query.branchId as string,
startDate: req.query.startDate ? new Date(req.query.startDate as string) : undefined,
endDate: req.query.endDate ? new Date(req.query.endDate as string) : undefined,
};
const stats = await this.service.getStats(req.tenantId!, filter);
res.json({ data: stats });
} catch (error) {
next(error);
}
}
/**
* POST /payment-transactions/charge
* Process a payment
*/
private async processPayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const dto: ProcessPaymentDto = req.body;
const result = await this.service.processPayment(req.tenantId!, req.userId!, dto);
if (result.success) {
res.status(201).json({ data: result });
} else {
res.status(400).json({ data: result });
}
} catch (error) {
next(error);
}
}
/**
* POST /payment-transactions/refund
* Process a refund
*/
private async processRefund(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const dto: ProcessRefundDto = req.body;
const result = await this.service.processRefund(req.tenantId!, req.userId!, dto);
if (result.success) {
res.json({ data: result });
} else {
res.status(400).json({ data: result });
}
} catch (error) {
next(error);
}
}
/**
* GET /payment-transactions
* Get transactions with filters
*/
private async getAll(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const filter: TransactionFilterDto = {
branchId: req.query.branchId as string,
userId: req.query.userId as string,
status: req.query.status as any,
sourceType: req.query.sourceType as any,
terminalProvider: req.query.terminalProvider as string,
startDate: req.query.startDate ? new Date(req.query.startDate as string) : undefined,
endDate: req.query.endDate ? new Date(req.query.endDate as string) : undefined,
limit: req.query.limit ? parseInt(req.query.limit as string) : undefined,
offset: req.query.offset ? parseInt(req.query.offset as string) : undefined,
};
const result = await this.service.findAll(req.tenantId!, filter);
res.json(result);
} catch (error) {
next(error);
}
}
/**
* GET /payment-transactions/:id
* Get transaction by ID
*/
private async getById(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const transaction = await this.service.findById(req.params.id, req.tenantId!);
if (!transaction) {
res.status(404).json({ error: 'Transaction not found' });
return;
}
res.json({ data: transaction });
} catch (error) {
next(error);
}
}
/**
* POST /payment-transactions/:id/receipt
* Send receipt for transaction
*/
private async sendReceipt(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
try {
const dto: SendReceiptDto = req.body;
const result = await this.service.sendReceipt(req.params.id, req.tenantId!, dto);
if (result.success) {
res.json({ success: true });
} else {
res.status(400).json({ success: false, error: result.error });
}
} catch (error) {
next(error);
}
}
}