import { Injectable } from '@nestjs/common'; import { AccessStore } from '../identity/access.store'; import { recordAudit } from '../identity/audit'; import type { Principal } from '../identity/identity.types'; import { AppError } from '../common/errors/app-error'; import { editableProduct } from './catalog.helpers'; import type { VariantInput } from './catalog.schemas'; @Injectable() export class VariantStore { constructor(private readonly access: AccessStore) {} save(actor: Principal, productId: string, input: VariantInput, id?: string) { return this.access.mutate(actor, 'catalog.manage', async (tx) => { const product = await editableProduct( tx, actor.organizationId, productId, ); if ( id && !(await tx.productVariant.findFirst({ where: { id, productId, organizationId: actor.organizationId }, })) ) { throw new AppError('VARIANT_NOT_FOUND'); } if ( !id && (await tx.productVariant.count({ where: { productId } })) >= 100 ) throw new AppError('VARIANT_LIMIT'); if ( id && !input.active && product.status === 'PUBLISHED' && !(await tx.productVariant.count({ where: { productId, active: true, id: { not: id } }, })) ) { throw new AppError('VARIANT_REQUIRED'); } const variant = id ? await tx.productVariant.update({ where: { id }, data: input }) : await tx.productVariant.create({ data: { ...input, productId, organizationId: actor.organizationId }, }); await recordAudit( tx, actor.organizationId, actor.userId, id ? 'variant.updated' : 'variant.created', variant.id, ); return variant; }); } }