feat(F-087): completed feature
This commit is contained in:
@@ -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'))
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user