feat: restructure sales, add PDF quote generation and WebSocket support
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { Socket } from "socket.io";
|
||||
import { attachSocketEventPermissions } from "../middleware/authorization";
|
||||
import { opportunities } from "../../../managers/opportunities";
|
||||
|
||||
const LIVE_QUOTE_PREVIEW_PERMISSION = "sales.opportunity.fetch";
|
||||
|
||||
export const registerLiveQuotePreviewHandlers = (socket: Socket) => {
|
||||
attachSocketEventPermissions(socket, {
|
||||
"opp:live_quote_preview": [LIVE_QUOTE_PREVIEW_PERMISSION],
|
||||
});
|
||||
|
||||
const registeredLivePreviewEvents = new Set<string>();
|
||||
|
||||
socket.on(
|
||||
"opp:live_quote_preview",
|
||||
async (
|
||||
payload: { id?: string | number },
|
||||
ack?: (response: { ok: boolean; event?: string; error?: string }) => void,
|
||||
) => {
|
||||
const oppId = payload?.id;
|
||||
const normalizedId =
|
||||
typeof oppId === "string" || typeof oppId === "number"
|
||||
? `${oppId}`
|
||||
: "";
|
||||
|
||||
if (!normalizedId) {
|
||||
if (ack) return ack({ ok: false, error: "Missing opportunity id" });
|
||||
socket.emit("opp:live_quote_preview:error", {
|
||||
message: "Missing opportunity id",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const dataEvent = `opp:live_quote_preview:${normalizedId}:data`;
|
||||
const previewEvent = `opp:live_quote_preview:${normalizedId}:preview`;
|
||||
const roomName = `opp:live_quote_preview:${normalizedId}`;
|
||||
|
||||
if (!registeredLivePreviewEvents.has(dataEvent)) {
|
||||
registeredLivePreviewEvents.add(dataEvent);
|
||||
socket.join(roomName);
|
||||
|
||||
socket.on(dataEvent, async (data: any) => {
|
||||
socket.to(roomName).emit(dataEvent, data);
|
||||
|
||||
try {
|
||||
const opportunity = await opportunities.fetchRecord(normalizedId);
|
||||
const opts =
|
||||
data?.options && typeof data.options === "object"
|
||||
? data.options
|
||||
: data;
|
||||
|
||||
const previewBuffer = await opportunity.generateQuote({
|
||||
lineItemPricing: opts?.lineItemPricing,
|
||||
includeQuoteNarrative: opts?.includeQuoteNarrative,
|
||||
includeItemNarratives: opts?.includeItemNarratives,
|
||||
logoPath: opts?.logoPath,
|
||||
showPreview: true,
|
||||
});
|
||||
|
||||
const previewBase64 = Buffer.from(previewBuffer).toString("base64");
|
||||
|
||||
socket.to(roomName).emit(previewEvent, {
|
||||
id: normalizedId,
|
||||
mimeType: "application/pdf",
|
||||
contentBase64: previewBase64,
|
||||
});
|
||||
|
||||
socket.to(roomName).emit(dataEvent, {
|
||||
id: normalizedId,
|
||||
mimeType: "application/pdf",
|
||||
contentBase64: previewBase64,
|
||||
});
|
||||
|
||||
socket.emit(previewEvent, {
|
||||
id: normalizedId,
|
||||
mimeType: "application/pdf",
|
||||
contentBase64: previewBase64,
|
||||
});
|
||||
|
||||
socket.emit(dataEvent, {
|
||||
id: normalizedId,
|
||||
mimeType: "application/pdf",
|
||||
contentBase64: previewBase64,
|
||||
});
|
||||
} catch (err: any) {
|
||||
socket.emit("opp:live_quote_preview:error", {
|
||||
message: err?.message ?? "Failed to generate live quote preview",
|
||||
id: normalizedId,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (ack) return ack({ ok: true, event: dataEvent });
|
||||
|
||||
socket.emit("opp:live_quote_preview:ready", {
|
||||
id: normalizedId,
|
||||
event: dataEvent,
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user