realtimex-crm 0.9.10 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,163 @@
1
+ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
2
+ import { createClient } from "jsr:@supabase/supabase-js@2";
3
+ import type { Selectable } from "https://esm.sh/kysely@0.27.2";
4
+ import { db, type CompaniesTable, CompiledQuery } from "../_shared/db.ts";
5
+ import { corsHeaders, createErrorResponse } from "../_shared/utils.ts";
6
+
7
+ type Company = Selectable<CompaniesTable>;
8
+
9
+ // Helper function to merge arrays and remove duplicates
10
+ function mergeArraysUnique<T>(arr1: T[], arr2: T[]): T[] {
11
+ return [...new Set([...arr1, ...arr2])];
12
+ }
13
+
14
+ function mergeCompanyData(winner: Company, loser: Company) {
15
+ // Merge context_links arrays
16
+ const mergedContextLinks = mergeArraysUnique(
17
+ winner.context_links || [],
18
+ loser.context_links || [],
19
+ );
20
+
21
+ const selectedLogo =
22
+ winner.logo && winner.logo.src ? winner.logo : loser.logo;
23
+
24
+ return {
25
+ logo: selectedLogo ? (JSON.stringify(selectedLogo) as any) : null,
26
+ sector: winner.sector ?? loser.sector,
27
+ size: winner.size ?? loser.size,
28
+ linkedin_url: winner.linkedin_url || loser.linkedin_url,
29
+ website: winner.website || loser.website,
30
+ phone_number: winner.phone_number ?? loser.phone_number,
31
+ address: winner.address ?? loser.address,
32
+ zipcode: winner.zipcode ?? loser.zipcode,
33
+ city: winner.city ?? loser.city,
34
+ stateAbbr: winner.stateAbbr ?? loser.stateAbbr,
35
+ country: winner.country ?? loser.country,
36
+ description: winner.description ?? loser.description,
37
+ revenue: winner.revenue ?? loser.revenue,
38
+ tax_identifier: winner.tax_identifier ?? loser.tax_identifier,
39
+ sales_id: winner.sales_id ?? loser.sales_id,
40
+ context_links: mergedContextLinks.length > 0
41
+ ? (JSON.stringify(mergedContextLinks) as any)
42
+ : null,
43
+ };
44
+ }
45
+
46
+ async function mergeCompanies(
47
+ loserId: number,
48
+ winnerId: number,
49
+ userId: string,
50
+ ) {
51
+ try {
52
+ return await db.transaction().execute(async (trx) => {
53
+ // Enable RLS by switching to authenticated role and setting user context
54
+ await trx.executeQuery(CompiledQuery.raw("SET LOCAL ROLE authenticated"));
55
+ await trx.executeQuery(
56
+ CompiledQuery.raw(
57
+ `SELECT set_config('request.jwt.claim.sub', '${userId}', true)`,
58
+ ),
59
+ );
60
+
61
+ // 1. Fetch both companies
62
+ const [winner, loser] = await Promise.all([
63
+ trx
64
+ .selectFrom("companies")
65
+ .selectAll()
66
+ .where("id", "=", winnerId)
67
+ .executeTakeFirstOrThrow(),
68
+ trx
69
+ .selectFrom("companies")
70
+ .selectAll()
71
+ .where("id", "=", loserId)
72
+ .executeTakeFirstOrThrow(),
73
+ ]);
74
+
75
+ // 2. Reassign contacts from loser to winner
76
+ await trx
77
+ .updateTable("contacts")
78
+ .set({ company_id: winnerId })
79
+ .where("company_id", "=", loserId)
80
+ .execute();
81
+
82
+ // 3. Reassign deals from loser to winner
83
+ await trx
84
+ .updateTable("deals")
85
+ .set({ company_id: winnerId })
86
+ .where("company_id", "=", loserId)
87
+ .execute();
88
+
89
+ // 4. Merge and update winner company
90
+ const mergedData = mergeCompanyData(winner as Company, loser as Company);
91
+ await trx
92
+ .updateTable("companies")
93
+ .set(mergedData)
94
+ .where("id", "=", winnerId)
95
+ .execute();
96
+
97
+ // 5. Delete loser company
98
+ await trx.deleteFrom("companies").where("id", "=", loserId).execute();
99
+
100
+ return { success: true, winnerId };
101
+ });
102
+ } catch (error) {
103
+ console.error("Transaction failed:", error);
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ Deno.serve(async (req: Request) => {
109
+ // Handle CORS preflight
110
+ if (req.method === "OPTIONS") {
111
+ return new Response(null, {
112
+ status: 204,
113
+ headers: corsHeaders,
114
+ });
115
+ }
116
+
117
+ // Authenticate user via Supabase client
118
+ const authHeader = req.headers.get("Authorization");
119
+ if (!authHeader) {
120
+ return createErrorResponse(401, "Missing Authorization header");
121
+ }
122
+
123
+ const supabaseClient = createClient(
124
+ Deno.env.get("SUPABASE_URL") ?? "",
125
+ Deno.env.get("SUPABASE_ANON_KEY") ?? "",
126
+ { global: { headers: { Authorization: authHeader } } },
127
+ );
128
+
129
+ const {
130
+ data: { user },
131
+ error: authError,
132
+ } = await supabaseClient.auth.getUser();
133
+ if (!user || authError) {
134
+ return createErrorResponse(401, "Unauthorized");
135
+ }
136
+
137
+ // Handle POST request
138
+ if (req.method === "POST") {
139
+ try {
140
+ const { loserId, winnerId } = await req.json();
141
+
142
+ if (!loserId || !winnerId) {
143
+ return createErrorResponse(400, "Missing loserId or winnerId");
144
+ }
145
+
146
+ const result = await mergeCompanies(loserId, winnerId, user.id);
147
+
148
+ return new Response(JSON.stringify(result), {
149
+ headers: { "Content-Type": "application/json", ...corsHeaders },
150
+ });
151
+ } catch (error) {
152
+ console.error("Merge failed:", error);
153
+ return createErrorResponse(
154
+ 500,
155
+ `Failed to merge companies: ${
156
+ error instanceof Error ? error.message : "Unknown error"
157
+ }`,
158
+ );
159
+ }
160
+ }
161
+
162
+ return createErrorResponse(405, "Method Not Allowed");
163
+ });