Files
mercadodevida/project/src/modules/pos/infrastructure/pg-cash-session-repository.ts
2026-08-22 13:19:27 +02:00

115 lines
3.9 KiB
TypeScript

import type pg from 'pg';
import type { PosCashSessionRepository } from '../domain/ports.js';
import type { PosCashSession, OpenCashSessionInput, CloseCashSessionInput } from '../domain/cash-session.js';
interface SessionRow {
id: string;
terminal_id: string;
store_id: string;
user_id: string;
status: 'OPEN' | 'CLOSED';
opened_at: Date;
closed_at: Date | null;
opening_cash_cents: number;
closing_cash_cents: number | null;
expected_cash_cents: number | null;
actual_cash_cents: number | null;
difference_cents: number | null;
notes: string | null;
created_at: Date;
updated_at: Date;
}
function toSession(row: SessionRow): PosCashSession {
return {
id: row.id,
terminalId: row.terminal_id,
storeId: row.store_id,
userId: row.user_id,
status: row.status,
openedAt: row.opened_at,
closedAt: row.closed_at,
openingCashCents: row.opening_cash_cents,
closingCashCents: row.closing_cash_cents,
expectedCashCents: row.expected_cash_cents,
actualCashCents: row.actual_cash_cents,
differenceCents: row.difference_cents,
notes: row.notes,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
export class PgCashSessionRepository implements PosCashSessionRepository {
constructor(private readonly pool: pg.Pool) {}
async findById(id: string): Promise<PosCashSession | undefined> {
const result = await this.pool.query<SessionRow>(
'SELECT * FROM pos_cash_sessions WHERE id = $1',
[id],
);
return result.rows[0] ? toSession(result.rows[0]) : undefined;
}
async findOpenByTerminal(terminalId: string): Promise<PosCashSession | undefined> {
const result = await this.pool.query<SessionRow>(
"SELECT * FROM pos_cash_sessions WHERE terminal_id = $1 AND status = 'OPEN'",
[terminalId],
);
return result.rows[0] ? toSession(result.rows[0]) : undefined;
}
async open(input: OpenCashSessionInput): Promise<PosCashSession> {
// Get store_id from terminal
const terminal = await this.pool.query<{ store_id: string }>(
'SELECT store_id FROM pos_terminals WHERE id = $1',
[input.terminalId],
);
if (!terminal.rows[0]) throw new Error(`Terminal ${input.terminalId} not found`);
const storeId = terminal.rows[0].store_id;
const result = await this.pool.query<SessionRow>(
`INSERT INTO pos_cash_sessions (terminal_id, store_id, user_id, opening_cash_cents)
VALUES ($1, $2, $3, $4) RETURNING *`,
[input.terminalId, storeId, input.userId, input.openingCashCents],
);
return toSession(result.rows[0]);
}
async close(input: CloseCashSessionInput): Promise<PosCashSession> {
const difference = input.actualCashCents - input.closingCashCents;
const result = await this.pool.query<SessionRow>(
`UPDATE pos_cash_sessions
SET status = 'CLOSED', closed_at = now(),
closing_cash_cents = $2, actual_cash_cents = $3,
difference_cents = $4, notes = $5, updated_at = now()
WHERE id = $1 RETURNING *`,
[input.sessionId, input.closingCashCents, input.actualCashCents, difference, input.notes ?? null],
);
if (!result.rows[0]) throw new Error(`Session ${input.sessionId} not found`);
return toSession(result.rows[0]);
}
async listByStore(
storeId: string,
limit = 50,
offset = 0,
): Promise<{ sessions: PosCashSession[]; total: number }> {
const [countResult, listResult] = await Promise.all([
this.pool.query<{ count: string }>(
'SELECT COUNT(*) as count FROM pos_cash_sessions WHERE store_id = $1',
[storeId],
),
this.pool.query<SessionRow>(
`SELECT * FROM pos_cash_sessions WHERE store_id = $1
ORDER BY opened_at DESC LIMIT $2 OFFSET $3`,
[storeId, limit, offset],
),
]);
return {
sessions: listResult.rows.map(toSession),
total: parseInt(countResult.rows[0].count, 10),
};
}
}