feat(F-191): completed feature

This commit is contained in:
chattie
2026-08-23 07:51:56 +02:00
parent eb3322e309
commit e542b180ad
10 changed files with 306 additions and 39 deletions

View File

@@ -559,6 +559,46 @@ export async function registerPosRoutes(app: FastifyInstance, deps: PosRouteDeps
},
);
// F-191: get session by ID (includes closed sessions) with summary metrics
app.get<{ Params: { id: string } }>(
'/pos/sessions/:id',
{
schema: {
tags: ['POS Terminal'],
summary: 'Get session details and summary',
params: { type: 'object', properties: { id: { type: 'string', format: 'uuid' } } },
response: { 401: errorSchema, 404: errorSchema },
} as FastifySchema,
},
async (request, reply) => {
const user = await authenticate(request);
requireAnyRole(user, ['admin', 'pos_manager', 'pos_cashier'] as ReadonlyArray<Role>);
const { id } = parseJson(idParamSchema, request.params);
const session = await sessionRepo.findById(id);
if (!session) throw new AppError(404, 'SESSION_NOT_FOUND', 'Sesión no encontrada');
// Summary: sales count, revenue, pending count for this session
const [salesResult, pendingResult] = await Promise.all([
pool.query<{ cnt: string; total: string }>(
`SELECT COUNT(*)::int AS cnt, COALESCE(SUM(total_cents), 0)::bigint AS total
FROM orders_orders WHERE cash_session_id = $1 AND source = 'pos'`,
[id],
),
pool.query<{ cnt: string }>(
`SELECT COUNT(*)::int AS cnt FROM orders_orders
WHERE cash_session_id = $1 AND source = 'pos' AND state = 'PENDING'`,
[id],
),
]);
return reply.send({
...session,
salesCount: parseInt(salesResult.rows[0]?.cnt ?? '0', 10),
salesTotalCents: parseInt(salesResult.rows[0]?.total ?? '0', 10),
pendingCount: parseInt(pendingResult.rows[0]?.cnt ?? '0', 10),
});
},
);
app.get(
'/pos/catalog/touch',
{
@@ -1511,18 +1551,22 @@ export async function registerPosRoutes(app: FastifyInstance, deps: PosRouteDeps
},
);
// F-191: enhanced admin sessions listing with terminalId and date range filters
app.get(
'/pos/sessions',
{
schema: {
tags: ['POS Admin'],
summary: 'List cash sessions',
summary: 'List cash sessions (admin)',
querystring: {
type: 'object',
properties: {
storeId: { type: 'string', format: 'uuid' },
terminalId: { type: 'string', format: 'uuid' },
status: { type: 'string', enum: ['OPEN', 'CLOSED'] },
limit: { type: 'integer', minimum: 1, maximum: 50, default: 20 },
dateFrom: { type: 'string', format: 'date' },
dateTo: { type: 'string', format: 'date' },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
},
},
response: { 401: errorSchema, 403: errorSchema },
@@ -1533,19 +1577,22 @@ export async function registerPosRoutes(app: FastifyInstance, deps: PosRouteDeps
requireRole(user, 'admin');
const {
storeId,
terminalId,
status,
dateFrom,
dateTo,
limit = 20,
} = request.query as { storeId?: string; status?: string; limit?: number };
} = request.query as {
storeId?: string; terminalId?: string; status?: string;
dateFrom?: string; dateTo?: string; limit?: number;
};
const conditions: string[] = [];
const params: unknown[] = [];
if (storeId) {
params.push(storeId);
conditions.push(`store_id = $${params.length}`);
}
if (status) {
params.push(status);
conditions.push(`status = $${params.length}`);
}
if (storeId) { params.push(storeId); conditions.push(`s.store_id = $${params.length}`); }
if (terminalId) { params.push(terminalId); conditions.push(`s.terminal_id = $${params.length}`); }
if (status) { params.push(status); conditions.push(`s.status = $${params.length}`); }
if (dateFrom) { params.push(dateFrom); conditions.push(`DATE(s.created_at) >= $${params.length}`); }
if (dateTo) { params.push(dateTo); conditions.push(`DATE(s.created_at) <= $${params.length}`); }
params.push(limit);
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')} AND ` : 'WHERE ';
const result = await pool.query(