feat(POS-003): completed feature

This commit is contained in:
chattie
2026-08-22 13:19:27 +02:00
parent 926add3c97
commit 7ce6465054
26 changed files with 880 additions and 19 deletions

View File

@@ -0,0 +1,22 @@
import type { PosCashSessionRepository } from '../domain/ports.js';
import type { PosCashSession } from '../domain/cash-session.js';
import { CashSessionNotOpenError } from '../domain/errors.js';
export interface CloseCashSessionInput {
sessionId: string;
closingCashCents: number;
actualCashCents: number;
notes?: string;
}
export class CloseCashSessionUseCase {
constructor(private readonly sessionRepo: PosCashSessionRepository) {}
async execute(input: CloseCashSessionInput): Promise<PosCashSession> {
const existing = await this.sessionRepo.findById(input.sessionId);
if (!existing) throw new CashSessionNotOpenError(input.sessionId);
if (existing.status === 'CLOSED') throw new CashSessionNotOpenError(input.sessionId);
return this.sessionRepo.close(input);
}
}

View File

@@ -0,0 +1,36 @@
import type { PosStoreRepository } from '../domain/ports.js';
import type { PosTerminalRepository } from '../domain/ports.js';
import type { PosPaymentMethodRepository } from '../infrastructure/pg-payment-method-repository.js';
import type { PosCashSessionRepository } from '../domain/ports.js';
import type { PosStore } from '../domain/store.js';
import type { PosTerminal } from '../domain/terminal.js';
import type { PosPaymentMethod } from '../infrastructure/pg-payment-method-repository.js';
export interface PosConfig {
store: PosStore;
terminal: PosTerminal;
paymentMethods: PosPaymentMethod[];
sessionOpen: boolean;
}
export class GetPosConfigUseCase {
constructor(
private readonly storeRepo: PosStoreRepository,
private readonly terminalRepo: PosTerminalRepository,
private readonly paymentMethodRepo: PosPaymentMethodRepository,
private readonly sessionRepo: PosCashSessionRepository,
) {}
async execute(terminalId: string): Promise<PosConfig> {
const terminal = await this.terminalRepo.findById(terminalId);
if (!terminal) throw new Error(`Terminal ${terminalId} not found`);
const store = await this.storeRepo.findById(terminal.storeId);
if (!store) throw new Error(`Store ${terminal.storeId} not found`);
const [paymentMethods, openSession] = await Promise.all([
this.paymentMethodRepo.listByStore(terminal.storeId),
this.sessionRepo.findOpenByTerminal(terminalId),
]);
await this.terminalRepo.updateLastSeen(terminalId);
return { store, terminal, paymentMethods, sessionOpen: !!openSession };
}
}

View File

@@ -0,0 +1,10 @@
import type { PosStoreRepository } from '../domain/ports.js';
import type { PosStore, ListStoresOptions } from '../domain/store.js';
export class ListStoresUseCase {
constructor(private readonly storeRepo: PosStoreRepository) {}
async execute(options?: ListStoresOptions): Promise<{ stores: PosStore[]; total: number }> {
return this.storeRepo.list(options);
}
}

View File

@@ -0,0 +1,10 @@
import type { PosTerminalRepository } from '../domain/ports.js';
import type { PosTerminal, ListTerminalsOptions } from '../domain/terminal.js';
export class ListTerminalsUseCase {
constructor(private readonly terminalRepo: PosTerminalRepository) {}
async execute(options?: ListTerminalsOptions): Promise<{ terminals: PosTerminal[]; total: number }> {
return this.terminalRepo.list(options);
}
}

View File

@@ -0,0 +1,28 @@
import type { PosCashSessionRepository, PosTerminalRepository } from '../domain/ports.js';
import type { PosCashSession } from '../domain/cash-session.js';
import { CashSessionAlreadyOpenError, TerminalNotBoundError, TerminalDisabledError } from '../domain/errors.js';
export interface OpenCashSessionInput {
terminalId: string;
userId: string;
openingCashCents: number;
}
export class OpenCashSessionUseCase {
constructor(
private readonly sessionRepo: PosCashSessionRepository,
private readonly terminalRepo: PosTerminalRepository,
) {}
async execute(input: OpenCashSessionInput): Promise<PosCashSession> {
const terminal = await this.terminalRepo.findById(input.terminalId);
if (!terminal) throw new Error(`Terminal ${input.terminalId} not found`);
if (terminal.status === 'disabled') throw new TerminalDisabledError(input.terminalId);
if (!terminal.bindingCode) throw new TerminalNotBoundError(input.terminalId);
const existing = await this.sessionRepo.findOpenByTerminal(input.terminalId);
if (existing) throw new CashSessionAlreadyOpenError(input.terminalId);
return this.sessionRepo.open(input);
}
}