feat(ADM-018): completed feature

This commit is contained in:
chattie
2026-08-17 22:23:10 +02:00
parent cf1c69fc8b
commit d595b4871f
871 changed files with 47411 additions and 281 deletions

View File

@@ -0,0 +1,181 @@
import type pg from 'pg';
import type { OrderRepository } from '../domain/ports.js';
import type { Order, OrderItem, OrderItemInput, OrderState, OrderView } from '../domain/order.js';
interface OrderRow {
id: string;
user_id: string;
idempotency_key: string | null;
state: OrderState;
currency: 'EUR';
subtotal_cents: number;
discount_cents: number;
tax_cents: number;
total_cents: number;
created_at: Date;
updated_at: Date;
}
interface ItemRow {
id: string;
order_id: string;
product_id: string;
variant_id: string;
sku: string;
ean: string | null;
name: string;
unit_price_cents: number;
discount_cents: number;
tax_cents: number;
quantity: number;
created_at: Date;
}
interface CreateInput {
userId: string;
idempotencyKey: string | null;
items: OrderItemInput[];
totals: { subtotalCents: number; discountCents: number; taxCents: number; totalCents: number };
}
export class PgOrderRepository implements OrderRepository {
constructor(private readonly pool: pg.Pool) {}
async create(input: CreateInput): Promise<OrderView> {
const client = await this.pool.connect();
try {
await client.query('BEGIN');
const orderResult = await client.query<OrderRow>(
`INSERT INTO orders_orders (user_id, idempotency_key, subtotal_cents, discount_cents, tax_cents, total_cents)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
[
input.userId,
input.idempotencyKey,
input.totals.subtotalCents,
input.totals.discountCents,
input.totals.taxCents,
input.totals.totalCents,
],
);
const order = orderResult.rows[0];
if (!order) throw new Error('orders_orders INSERT returned no row');
for (const item of input.items) {
await client.query<ItemRow>(
`INSERT INTO orders_items (order_id, product_id, variant_id, sku, ean, name, unit_price_cents, discount_cents, tax_cents, quantity)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *`,
[
order.id,
item.productId,
item.variantId,
item.sku,
item.ean,
item.name,
item.unitPriceCents,
item.discountCents,
item.taxCents,
item.quantity,
],
);
}
await client.query('COMMIT');
const loaded = await this.findById(order.id);
if (!loaded) throw new Error('orders_orders load after create returned no row');
return loaded;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
async findAll(): Promise<OrderView[]> {
const result = await this.pool.query<OrderRow>(
'SELECT * FROM orders_orders ORDER BY created_at DESC',
);
return Promise.all(
result.rows.map(async (order) => {
const items = await this.pool.query<ItemRow>(
'SELECT * FROM orders_items WHERE order_id = $1 ORDER BY id',
[order.id],
);
return { ...toOrder(order), items: items.rows.map(toItem) };
}),
);
}
async findById(id: string): Promise<OrderView | undefined> {
const orderResult = await this.pool.query<OrderRow>(
'SELECT * FROM orders_orders WHERE id = $1',
[id],
);
const order = orderResult.rows[0];
if (!order) return undefined;
const items = await this.pool.query<ItemRow>(
'SELECT * FROM orders_items WHERE order_id = $1 ORDER BY created_at, id',
[id],
);
return { ...toOrder(order), items: items.rows.map(toItem) };
}
async findByIdAndUserId(id: string, userId: string): Promise<OrderView | undefined> {
const orderResult = await this.pool.query<OrderRow>(
'SELECT * FROM orders_orders WHERE id = $1 AND user_id = $2',
[id, userId],
);
const order = orderResult.rows[0];
if (!order) return undefined;
const items = await this.pool.query<ItemRow>(
'SELECT * FROM orders_items WHERE order_id = $1 ORDER BY created_at, id',
[id],
);
return { ...toOrder(order), items: items.rows.map(toItem) };
}
async updateState(id: string, state: OrderState): Promise<OrderView | undefined> {
const result = await this.pool.query<OrderRow>(
`UPDATE orders_orders SET state = $2, updated_at = now()
WHERE id = $1 RETURNING *`,
[id, state],
);
const order = result.rows[0];
if (!order) return undefined;
const items = await this.pool.query<ItemRow>(
'SELECT * FROM orders_items WHERE order_id = $1 ORDER BY created_at, id',
[id],
);
return { ...toOrder(order), items: items.rows.map(toItem) };
}
}
function toOrder(row: OrderRow): Order {
return {
id: row.id,
userId: row.user_id,
idempotencyKey: row.idempotency_key,
state: row.state,
currency: row.currency,
subtotalCents: row.subtotal_cents,
discountCents: row.discount_cents,
taxCents: row.tax_cents,
totalCents: row.total_cents,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
function toItem(row: ItemRow): OrderItem {
return {
id: row.id,
orderId: row.order_id,
productId: row.product_id,
variantId: row.variant_id,
sku: row.sku,
ean: row.ean,
name: row.name,
unitPriceCents: row.unit_price_cents,
discountCents: row.discount_cents,
taxCents: row.tax_cents,
quantity: row.quantity,
createdAt: row.created_at,
};
}