erp-retail-backend-v2/src/modules/cash/controllers/cash.controller.ts
rckrdmrd a6186c4022 Migración desde erp-retail/backend - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:11:34 -06:00

331 lines
9.7 KiB
TypeScript

import { Request, Response } from 'express';
import { BaseController } from '../../../shared/controllers/base.controller';
import { CashMovementService, MovementQueryOptions } from '../services/cash-movement.service';
import { CashClosingService, ClosingQueryOptions } from '../services/cash-closing.service';
export class CashController extends BaseController {
constructor(
private readonly movementService: CashMovementService,
private readonly closingService: CashClosingService
) {
super();
}
// ==================== MOVEMENT ENDPOINTS ====================
/**
* Create a cash movement
* POST /cash/movements
*/
createMovement = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const result = await this.movementService.createMovement(tenantId, req.body, userId);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.created(res, result.data);
};
/**
* List movements
* GET /cash/movements
*/
listMovements = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const options: MovementQueryOptions = {
page: Number(req.query.page) || 1,
limit: Number(req.query.limit) || 20,
branchId: req.query.branchId as string,
sessionId: req.query.sessionId as string,
registerId: req.query.registerId as string,
type: req.query.type as any,
reason: req.query.reason as any,
status: req.query.status as any,
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 result = await this.movementService.findMovements(tenantId, options);
this.paginated(res, result.data, result.total, options.page!, options.limit!);
};
/**
* Get movement by ID
* GET /cash/movements/:id
*/
getMovement = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const { id } = req.params;
const movement = await this.movementService.findById(tenantId, id);
if (!movement) {
this.notFound(res, 'Movement');
return;
}
this.success(res, movement);
};
/**
* Approve movement
* POST /cash/movements/:id/approve
*/
approveMovement = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const result = await this.movementService.approveMovement(tenantId, id, userId);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Movement approved');
};
/**
* Reject movement
* POST /cash/movements/:id/reject
*/
rejectMovement = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { reason } = req.body;
const result = await this.movementService.rejectMovement(tenantId, id, userId, reason);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Movement rejected');
};
/**
* Cancel movement
* POST /cash/movements/:id/cancel
*/
cancelMovement = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { reason } = req.body;
const result = await this.movementService.cancelMovement(tenantId, id, userId, reason);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Movement cancelled');
};
/**
* Get session summary
* GET /cash/sessions/:sessionId/summary
*/
getSessionSummary = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const { sessionId } = req.params;
const summary = await this.movementService.getSessionSummary(tenantId, sessionId);
this.success(res, summary);
};
// ==================== CLOSING ENDPOINTS ====================
/**
* Create a cash closing
* POST /cash/closings
*/
createClosing = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const result = await this.closingService.createClosing(tenantId, req.body, userId);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.created(res, result.data);
};
/**
* List closings
* GET /cash/closings
*/
listClosings = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const options: ClosingQueryOptions = {
page: Number(req.query.page) || 1,
limit: Number(req.query.limit) || 20,
branchId: req.query.branchId as string,
sessionId: req.query.sessionId as string,
status: req.query.status as any,
type: req.query.type as any,
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 result = await this.closingService.findClosings(tenantId, options);
this.paginated(res, result.data, result.total, options.page!, options.limit!);
};
/**
* Get closing by ID
* GET /cash/closings/:id
*/
getClosing = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const { id } = req.params;
const closing = await this.closingService.getClosingWithCounts(tenantId, id);
if (!closing) {
this.notFound(res, 'Closing');
return;
}
this.success(res, closing);
};
/**
* Submit cash count
* POST /cash/closings/:id/count
*/
submitCashCount = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { denominations } = req.body;
const result = await this.closingService.submitCashCount(tenantId, id, denominations, userId);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Cash count submitted');
};
/**
* Submit payment counts
* POST /cash/closings/:id/payments
*/
submitPaymentCounts = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const result = await this.closingService.submitPaymentCounts(tenantId, id, req.body, userId);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Payment counts submitted');
};
/**
* Approve closing
* POST /cash/closings/:id/approve
*/
approveClosing = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { notes } = req.body;
const result = await this.closingService.approveClosing(tenantId, id, userId, notes);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Closing approved');
};
/**
* Reject closing
* POST /cash/closings/:id/reject
*/
rejectClosing = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { notes } = req.body;
const result = await this.closingService.rejectClosing(tenantId, id, userId, notes);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Closing rejected');
};
/**
* Reconcile closing
* POST /cash/closings/:id/reconcile
*/
reconcileClosing = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const userId = req.userContext!.userId;
const { id } = req.params;
const { depositAmount, depositReference, depositDate } = req.body;
const result = await this.closingService.reconcileClosing(
tenantId,
id,
{
amount: depositAmount,
reference: depositReference,
date: new Date(depositDate),
},
userId
);
if (!result.success) {
this.error(res, result.error!.message, 400, result.error!.code);
return;
}
this.success(res, result.data, 'Closing reconciled');
};
/**
* Get daily summary
* GET /cash/summary/daily
*/
getDailySummary = async (req: Request, res: Response): Promise<void> => {
const tenantId = req.tenantContext!.tenantId;
const branchId = req.branchContext?.branchId || (req.query.branchId as string);
const date = req.query.date ? new Date(req.query.date as string) : new Date();
if (!branchId) {
this.error(res, 'Branch ID is required', 400);
return;
}
const summary = await this.closingService.getDailySummary(tenantId, branchId, date);
this.success(res, summary);
};
}