import { createRoute } from "../../../modules/api-utils/createRoute"; import { opportunities } from "../../../managers/opportunities"; import { apiResponse } from "../../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../middleware/authorization"; import { processObjectValuePerms } from "../../../modules/permission-utils/processObjectPermissions"; import { z } from "zod"; const productItemSchema = z .object({ catalogItem: z.object({ id: z.number().int().positive() }).optional(), forecastDescription: z.string().optional(), productDescription: z.string().optional(), quantity: z.number().positive().optional(), status: z.object({ id: z.number().int().positive() }).optional(), productClass: z.string().optional(), forecastType: z.string().optional(), revenue: z.number().optional(), cost: z.number().optional(), includeFlag: z.boolean().optional(), linkFlag: z.boolean().optional(), recurringFlag: z.boolean().optional(), taxableFlag: z.boolean().optional(), recurringRevenue: z.number().optional(), recurringCost: z.number().optional(), cycles: z.number().int().min(0).optional(), sequenceNumber: z.number().int().min(0).optional(), }) .strict(); const addProductSchema = z.union([ productItemSchema, z.array(productItemSchema).min(1, "At least one product is required"), ]); /* POST /v1/sales/opportunities/:identifier/products */ export default createRoute( "post", ["/opportunities/:identifier/products"], async (c) => { const identifier = c.req.param("identifier"); const body = await c.req.json(); const validated = addProductSchema.parse(body); const inputItems = Array.isArray(validated) ? validated : [validated]; // Gate each submitted field against user permissions. // Only fields the user has permission for are forwarded to ConnectWise. const user = c.get("user"); const gatedItems = await Promise.all( inputItems.map((item) => processObjectValuePerms(item, "sales.opportunity.product.field", user), ), ); const item = await opportunities.fetchItem(identifier); const created = await item.addProducts(gatedItems); const isBatch = Array.isArray(body); const response = apiResponse.created( isBatch ? `${created.length} product(s) added to opportunity successfully!` : "Product added to opportunity successfully!", isBatch ? created.map((p) => p.toJson()) : created[0]!.toJson(), ); return c.json(response, response.status as ContentfulStatusCode); }, authMiddleware({ permissions: ["sales.opportunity.product.add"] }), );