feat(F-087): completed feature

This commit is contained in:
chattie
2026-08-20 06:16:38 +02:00
parent 822bc7546c
commit 63fdc775d9
18 changed files with 569 additions and 24 deletions

View File

@@ -11,7 +11,7 @@ import type { PricingServicePort } from '../../pricing/index.js';
import type { PromotionServicePort } from '../../promotions/index.js';
import { CartService } from '../application/cart-service.js';
import type { CartItemView, CartView } from '../domain/cart.js';
import { InvalidCartQuantityError } from '../domain/errors.js';
import { InvalidCartQuantityError, InsufficientCartStockError } from '../domain/errors.js';
import { PgCartRepository } from '../infrastructure/pg-cart-repository.js';
export interface CartRoutesDeps {
@@ -131,6 +131,12 @@ export async function registerCartRoutes(
}
function mapCartError(error: unknown): Error {
if (error instanceof InsufficientCartStockError)
return new AppError(
409,
'INSUFFICIENT_STOCK',
`Only ${error.available} units available; you requested ${error.requested}.`,
);
if (error instanceof InvalidCartQuantityError)
return new AppError(422, 'INVALID_CART_QUANTITY', error.message);
if (error instanceof Error && error.name.includes('Promotion'))

View File

@@ -1,7 +1,7 @@
import type { InventoryServicePort } from '../../inventory/index.js';
import type { PricingServicePort } from '../../pricing/index.js';
import type { PromotionServicePort } from '../../promotions/index.js';
import { InvalidCartQuantityError } from '../domain/errors.js';
import { InvalidCartQuantityError, InsufficientCartStockError } from '../domain/errors.js';
import type { CartItemInput, CartView } from '../domain/cart.js';
import type { CartRepository } from '../domain/ports.js';
@@ -19,14 +19,23 @@ export class CartService {
async addItem(userId: string, input: CartItemInput): Promise<CartView> {
ensurePositiveQuantity(input.quantity);
await this.assertStockAvailable(input.variantId, input.quantity);
return this.toView(await this.carts.addItem(userId, input));
}
async changeQuantity(userId: string, variantId: string, quantity: number): Promise<CartView> {
ensurePositiveQuantity(quantity);
await this.assertStockAvailable(variantId, quantity);
return this.toView(await this.carts.changeQuantity(userId, variantId, quantity));
}
private async assertStockAvailable(variantId: string, quantity: number): Promise<void> {
const av = await this.inventory.checkAvailability(variantId, quantity);
if (!av.available) {
throw new InsufficientCartStockError(variantId, quantity, av.availableQuantity);
}
}
async removeItem(userId: string, variantId: string): Promise<CartView> {
return this.toView(await this.carts.removeItem(userId, variantId));
}

View File

@@ -4,3 +4,16 @@ export class InvalidCartQuantityError extends Error {
this.name = 'InvalidCartQuantityError';
}
}
export class InsufficientCartStockError extends Error {
constructor(
public readonly variantId: string,
public readonly requested: number,
public readonly available: number,
) {
super(
`Insufficient stock for variant ${variantId}: requested ${requested}, available ${available}`,
);
this.name = 'InsufficientCartStockError';
}
}