import type { GeneralError, InvalidOperationError, Product, ShoppingList, ShoppingListAddLineItemAction, ShoppingListChangeLineItemQuantityAction, ShoppingListChangeNameAction, ShoppingListLineItem, ShoppingListRemoveLineItemAction, ShoppingListSetAnonymousIdAction, ShoppingListSetCustomerAction, ShoppingListSetCustomFieldAction, ShoppingListSetCustomTypeAction, ShoppingListSetDeleteDaysAfterLastModificationAction, ShoppingListSetDescriptionAction, ShoppingListSetKeyAction, ShoppingListSetSlugAction, ShoppingListSetStoreAction, ShoppingListUpdateAction, } from "@commercetools/platform-sdk"; import { v4 as uuidv4 } from "uuid"; import { CommercetoolsError } from "#src/exceptions.ts"; import type { Writable } from "../../types.ts"; import type { RepositoryContext, UpdateHandlerInterface } from "../abstract.ts"; import { AbstractUpdateHandler } from "../abstract.ts"; export class ShoppingListUpdateHandler extends AbstractUpdateHandler implements Partial> { async addLineItem( context: RepositoryContext, resource: Writable, { productId, variantId, sku, quantity = 1, addedAt, key, }: ShoppingListAddLineItemAction, ) { let product: Product | null = null; if (productId) { // Fetch product and variant by ID product = await this._storage.get( context.projectKey, "product", productId, {}, ); } else if (sku) { // Fetch product and variant by SKU const items = await this._storage.query(context.projectKey, "product", { where: [ `masterData(current(masterVariant(sku="${sku}"))) or masterData(current(variants(sku="${sku}")))`, ], }); if (items.count === 1) { product = items.results[0]; } } if (!product) { // Check if product is found throw new CommercetoolsError({ code: "General", message: sku ? `A product containing a variant with SKU '${sku}' not found.` : `A product with ID '${productId}' not found.`, }); } let varId: number | undefined = variantId; if (sku) { varId = [ product.masterData.current.masterVariant, ...product.masterData.current.variants, ].find((x) => x.sku === sku)?.id; } if (!varId) { varId = product.masterData.current.masterVariant.id; } const alreadyAdded = resource.lineItems.some( (x) => x.productId === product?.id && x.variantId === varId, ); if (alreadyAdded) { // increase quantity and update total price resource.lineItems.forEach((x) => { if (x.productId === product?.id && x.variantId === varId) { x.quantity += quantity; } }); } else { // add line item resource.lineItems.push({ addedAt: addedAt ? addedAt : new Date().toISOString(), id: uuidv4(), key, productId: product.id, productSlug: product.masterData.current.slug, productType: product.productType, name: product.masterData.current.name, variantId: varId, quantity, published: Boolean(product.masterData.current), }); } } changeLineItemQuantity( context: RepositoryContext, resource: Writable, { lineItemId, lineItemKey, quantity, }: ShoppingListChangeLineItemQuantityAction, ) { let lineItem: Writable | undefined; if (lineItemId) { lineItem = resource.lineItems.find((x) => x.id === lineItemId); if (!lineItem) { throw new CommercetoolsError({ code: "General", message: `A line item with ID '${lineItemId}' not found.`, }); } } else if (lineItemKey) { lineItem = resource.lineItems.find((x) => x.id === lineItemId); if (!lineItem) { throw new CommercetoolsError({ code: "General", message: `A line item with Key '${lineItemKey}' not found.`, }); } } else { throw new CommercetoolsError({ code: "General", message: "Either lineItemid or lineItemKey needs to be provided.", }); } if (quantity === 0) { // delete line item resource.lineItems = resource.lineItems.filter( (x) => x.id !== lineItemId, ); } else { resource.lineItems.forEach((x) => { if (x.id === lineItemId && quantity) { x.quantity = quantity; } }); } } changeName( context: RepositoryContext, resource: Writable, { name }: ShoppingListChangeNameAction, ) { resource.name = name; } removeLineItem( context: RepositoryContext, resource: Writable, { lineItemId, quantity }: ShoppingListRemoveLineItemAction, ) { const lineItem = resource.lineItems.find((x) => x.id === lineItemId); if (!lineItem) { // Check if product is found throw new CommercetoolsError({ code: "General", message: `A line item with ID '${lineItemId}' not found.`, }); } const shouldDelete = !quantity || quantity >= lineItem.quantity; if (shouldDelete) { // delete line item resource.lineItems = resource.lineItems.filter( (x) => x.id !== lineItemId, ); } else { // decrease quantity and update total price resource.lineItems.forEach((x) => { if (x.id === lineItemId && quantity) { x.quantity -= quantity; } }); } } setAnonymousId( context: RepositoryContext, resource: Writable, { anonymousId }: ShoppingListSetAnonymousIdAction, ) { resource.anonymousId = anonymousId; } setCustomer( context: RepositoryContext, resource: Writable, { customer }: ShoppingListSetCustomerAction, ) { if (customer?.key) { throw new CommercetoolsError({ code: "InvalidOperation", message: "set customer on shoppinglist by key not implemented", }); } if (customer?.id) { resource.customer = { typeId: "customer", id: customer.id }; } } setCustomField( context: RepositoryContext, resource: ShoppingList, { name, value }: ShoppingListSetCustomFieldAction, ) { this._setCustomFieldValues(resource, { name, value }); } async setCustomType( context: RepositoryContext, resource: Writable, { type, fields }: ShoppingListSetCustomTypeAction, ) { await this._setCustomType(context, resource, { type, fields }); } setDeleteDaysAfterLastModification( context: RepositoryContext, resource: Writable, { deleteDaysAfterLastModification, }: ShoppingListSetDeleteDaysAfterLastModificationAction, ) { resource.deleteDaysAfterLastModification = deleteDaysAfterLastModification; } setDescription( context: RepositoryContext, resource: Writable, { description }: ShoppingListSetDescriptionAction, ) { resource.description = description; } setKey( context: RepositoryContext, resource: Writable, { key }: ShoppingListSetKeyAction, ) { resource.key = key; } setSlug( context: RepositoryContext, resource: Writable, { slug }: ShoppingListSetSlugAction, ) { resource.slug = slug; } setStore( context: RepositoryContext, resource: Writable, { store }: ShoppingListSetStoreAction, ) { if (store?.key) { resource.store = { typeId: "store", key: store.key }; } if (store?.id) { throw new CommercetoolsError({ code: "InvalidOperation", message: "set store on shoppinglist by id not implemented", }); } } }