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 GenericError from "../../../../Errors/GenericError"; import { z } from "zod"; const updateSchema = z .object({ name: z.string().min(1).optional(), notes: z.string().optional(), rating: z.object({ id: z.number() }).optional(), type: z.object({ id: z.number() }).optional(), stage: z.object({ id: z.number() }).optional(), status: z.object({ id: z.number() }).optional(), priority: z.object({ id: z.number() }).optional(), campaign: z.object({ id: z.number() }).optional(), primarySalesRep: z.object({ id: z.number() }).optional(), secondarySalesRep: z.object({ id: z.number() }).nullable().optional(), company: z.object({ id: z.number() }).optional(), contact: z.object({ id: z.number() }).nullable().optional(), site: z.object({ id: z.number() }).nullable().optional(), expectedCloseDate: z .string() .optional() .transform((v) => (v ? new Date(v).toISOString() : v)), customerPO: z.string().nullable().optional(), source: z.string().nullable().optional(), locationId: z.number().optional(), businessUnitId: z.number().optional(), }) .refine((d) => Object.values(d).some((v) => v !== undefined), { message: "At least one field must be provided", }); /* PATCH /v1/sales/opportunities/:identifier */ export default createRoute( "patch", ["/opportunities/:identifier"], async (c) => { const identifier = c.req.param("identifier"); const body = await c.req.json(); const data = updateSchema.parse(body); const item = await opportunities.fetchRecord(identifier); try { const updated = await item.updateOpportunity(data); const response = apiResponse.successful( "Opportunity updated successfully!", updated.toJson(), ); return c.json(response, response.status as ContentfulStatusCode); } catch (err) { const isAxios = err != null && typeof err === "object" && "isAxiosError" in err; if (isAxios) { const axiosErr = err as any; const cwStatus: number = axiosErr.response?.status ?? 502; const cwData = axiosErr.response?.data; const cwMessage: string = cwData?.message ?? "Failed to update the opportunity in ConnectWise"; const cwErrors: unknown[] | undefined = Array.isArray(cwData?.errors) ? cwData.errors : undefined; return c.json( { status: cwStatus, message: cwMessage, error: "ConnectWiseUpdateError", successful: false, errors: cwErrors, meta: { timestamp: Date.now() }, }, cwStatus as ContentfulStatusCode, ); } throw new GenericError({ status: 500, name: "OpportunitySaveError", message: "Failed to save opportunity data", cause: err instanceof Error ? err.message : String(err), }); } }, authMiddleware({ permissions: ["sales.opportunity.update"] }), );