import { Injectable } from '@nestjs/common'; import { AccessStore } from '../identity/access.store'; import type { Principal } from '../identity/identity.types'; import { AppError } from '../common/errors/app-error'; @Injectable() export class CartStore { constructor(private readonly access: AccessStore) {} get(actor: Principal) { return this.access.mutate(actor, null, async (tx) => { const cart = await tx.cart.upsert({ where: { userId_organizationId: { userId: actor.userId, organizationId: actor.organizationId, }, }, create: { userId: actor.userId, organizationId: actor.organizationId }, update: {}, }); const lines = await tx.cartLine.findMany({ where: { cartId: cart.id }, orderBy: { id: 'asc' }, select: { variantId: true, quantity: true, variant: { select: { name: true, sku: true, price: true, currency: true, active: true, product: { select: { name: true, status: true } }, }, }, }, }); return { version: cart.version, lines }; }); } set(actor: Principal, variantId: string, version: number, quantity: number) { return this.access.mutate(actor, null, async (tx) => { const cart = await tx.cart.upsert({ where: { userId_organizationId: { userId: actor.userId, organizationId: actor.organizationId, }, }, create: { userId: actor.userId, organizationId: actor.organizationId }, update: {}, }); if (cart.version !== version) throw new AppError('CART_CHANGED'); if (quantity === 0) { await tx.cartLine.deleteMany({ where: { cartId: cart.id, variantId } }); } else { const variant = await tx.productVariant.findFirst({ where: { id: variantId, organizationId: actor.organizationId, active: true, product: { status: 'PUBLISHED' }, }, }); if (!variant) throw new AppError('CART_ITEM_UNAVAILABLE'); const lines = await tx.cartLine.findMany({ where: { cartId: cart.id }, include: { variant: true }, }); if (lines.some((line) => line.variant.currency !== variant.currency)) throw new AppError('CART_CURRENCY'); if ( lines.length >= 20 && !lines.some((line) => line.variantId === variantId) ) throw new AppError('CART_LIMIT'); await tx.cartLine.upsert({ where: { cartId_variantId: { cartId: cart.id, variantId } }, create: { cartId: cart.id, organizationId: actor.organizationId, variantId, quantity, }, update: { quantity }, }); } await tx.cart.update({ where: { id: cart.id }, data: { version: { increment: 1 } }, }); return { version: version + 1 }; }); } }