eggi-ai-db-schema-2 0.1.1
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.
- package/CHANGELOG.md +750 -0
- package/README.md +660 -0
- package/dist/config/database.d.ts +28 -0
- package/dist/config/database.d.ts.map +1 -0
- package/dist/config/database.js +72 -0
- package/dist/config/database.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/database-service.d.ts +714 -0
- package/dist/lib/database-service.d.ts.map +1 -0
- package/dist/lib/database-service.js +1394 -0
- package/dist/lib/database-service.js.map +1 -0
- package/dist/lib/db-types.d.ts +167 -0
- package/dist/lib/db-types.d.ts.map +1 -0
- package/dist/lib/db-types.js +28 -0
- package/dist/lib/db-types.js.map +1 -0
- package/dist/lib/db.d.ts +58 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +292 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +26 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/pg-client.d.ts +50 -0
- package/dist/lib/pg-client.d.ts.map +1 -0
- package/dist/lib/pg-client.js +106 -0
- package/dist/lib/pg-client.js.map +1 -0
- package/dist/lib/schema.d.ts +298 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +12 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/migration-manager.d.ts +49 -0
- package/dist/migration-manager.d.ts.map +1 -0
- package/dist/migration-manager.js +282 -0
- package/dist/migration-manager.js.map +1 -0
- package/dist/queries/minimal-connections.d.ts +31 -0
- package/dist/queries/minimal-connections.d.ts.map +1 -0
- package/dist/queries/minimal-connections.js +143 -0
- package/dist/queries/minimal-connections.js.map +1 -0
- package/dist/schema.ts +340 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +40 -0
- package/dist/seed.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/types.d.ts +77 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +3 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/authenticated-user-operations.d.ts +110 -0
- package/dist/utils/authenticated-user-operations.d.ts.map +1 -0
- package/dist/utils/authenticated-user-operations.js +292 -0
- package/dist/utils/authenticated-user-operations.js.map +1 -0
- package/dist/utils/authentication-operations.d.ts +48 -0
- package/dist/utils/authentication-operations.d.ts.map +1 -0
- package/dist/utils/authentication-operations.js +172 -0
- package/dist/utils/authentication-operations.js.map +1 -0
- package/dist/utils/company-mapping-job-operations.d.ts +103 -0
- package/dist/utils/company-mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/company-mapping-job-operations.js +413 -0
- package/dist/utils/company-mapping-job-operations.js.map +1 -0
- package/dist/utils/company-sheet-upload-operations.d.ts +53 -0
- package/dist/utils/company-sheet-upload-operations.d.ts.map +1 -0
- package/dist/utils/company-sheet-upload-operations.js +135 -0
- package/dist/utils/company-sheet-upload-operations.js.map +1 -0
- package/dist/utils/contact-operations.d.ts +70 -0
- package/dist/utils/contact-operations.d.ts.map +1 -0
- package/dist/utils/contact-operations.js +294 -0
- package/dist/utils/contact-operations.js.map +1 -0
- package/dist/utils/forager-linkedin-operations.d.ts +74 -0
- package/dist/utils/forager-linkedin-operations.d.ts.map +1 -0
- package/dist/utils/forager-linkedin-operations.js +778 -0
- package/dist/utils/forager-linkedin-operations.js.map +1 -0
- package/dist/utils/ghost-genius-linkedin-operations.d.ts +23 -0
- package/dist/utils/ghost-genius-linkedin-operations.d.ts.map +1 -0
- package/dist/utils/ghost-genius-linkedin-operations.js +282 -0
- package/dist/utils/ghost-genius-linkedin-operations.js.map +1 -0
- package/dist/utils/index.d.ts +29 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +77 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/introduction-request-operations.d.ts +160 -0
- package/dist/utils/introduction-request-operations.d.ts.map +1 -0
- package/dist/utils/introduction-request-operations.js +492 -0
- package/dist/utils/introduction-request-operations.js.map +1 -0
- package/dist/utils/invitation-operations.d.ts +141 -0
- package/dist/utils/invitation-operations.d.ts.map +1 -0
- package/dist/utils/invitation-operations.js +749 -0
- package/dist/utils/invitation-operations.js.map +1 -0
- package/dist/utils/linkedin-account-operations.d.ts +45 -0
- package/dist/utils/linkedin-account-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-account-operations.js +279 -0
- package/dist/utils/linkedin-account-operations.js.map +1 -0
- package/dist/utils/linkedin-account-relationship-operations.d.ts +77 -0
- package/dist/utils/linkedin-account-relationship-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-account-relationship-operations.js +274 -0
- package/dist/utils/linkedin-account-relationship-operations.js.map +1 -0
- package/dist/utils/linkedin-data-operations.d.ts +102 -0
- package/dist/utils/linkedin-data-operations.d.ts.map +1 -0
- package/dist/utils/linkedin-data-operations.js +613 -0
- package/dist/utils/linkedin-data-operations.js.map +1 -0
- package/dist/utils/linkedin-identifier-utils.d.ts +31 -0
- package/dist/utils/linkedin-identifier-utils.d.ts.map +1 -0
- package/dist/utils/linkedin-identifier-utils.js +63 -0
- package/dist/utils/linkedin-identifier-utils.js.map +1 -0
- package/dist/utils/linkedin-profile-cache.d.ts +131 -0
- package/dist/utils/linkedin-profile-cache.d.ts.map +1 -0
- package/dist/utils/linkedin-profile-cache.js +418 -0
- package/dist/utils/linkedin-profile-cache.js.map +1 -0
- package/dist/utils/llm-inference-job-operations.d.ts +116 -0
- package/dist/utils/llm-inference-job-operations.d.ts.map +1 -0
- package/dist/utils/llm-inference-job-operations.js +267 -0
- package/dist/utils/llm-inference-job-operations.js.map +1 -0
- package/dist/utils/mapping-job-operations.d.ts +272 -0
- package/dist/utils/mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/mapping-job-operations.js +833 -0
- package/dist/utils/mapping-job-operations.js.map +1 -0
- package/dist/utils/mapping-operations.d.ts +80 -0
- package/dist/utils/mapping-operations.d.ts.map +1 -0
- package/dist/utils/mapping-operations.js +318 -0
- package/dist/utils/mapping-operations.js.map +1 -0
- package/dist/utils/on-demand-mapping-operations.d.ts +199 -0
- package/dist/utils/on-demand-mapping-operations.d.ts.map +1 -0
- package/dist/utils/on-demand-mapping-operations.js +728 -0
- package/dist/utils/on-demand-mapping-operations.js.map +1 -0
- package/dist/utils/onboarding-operations.d.ts +53 -0
- package/dist/utils/onboarding-operations.d.ts.map +1 -0
- package/dist/utils/onboarding-operations.js +223 -0
- package/dist/utils/onboarding-operations.js.map +1 -0
- package/dist/utils/organization-assignment-job-operations.d.ts +258 -0
- package/dist/utils/organization-assignment-job-operations.d.ts.map +1 -0
- package/dist/utils/organization-assignment-job-operations.js +881 -0
- package/dist/utils/organization-assignment-job-operations.js.map +1 -0
- package/dist/utils/organization-assignment-operations.d.ts +59 -0
- package/dist/utils/organization-assignment-operations.d.ts.map +1 -0
- package/dist/utils/organization-assignment-operations.js +130 -0
- package/dist/utils/organization-assignment-operations.js.map +1 -0
- package/dist/utils/organization-operations.d.ts +284 -0
- package/dist/utils/organization-operations.d.ts.map +1 -0
- package/dist/utils/organization-operations.js +1030 -0
- package/dist/utils/organization-operations.js.map +1 -0
- package/dist/utils/organization-relationship-operations.d.ts +79 -0
- package/dist/utils/organization-relationship-operations.d.ts.map +1 -0
- package/dist/utils/organization-relationship-operations.js +294 -0
- package/dist/utils/organization-relationship-operations.js.map +1 -0
- package/dist/utils/quota-operations.d.ts +107 -0
- package/dist/utils/quota-operations.d.ts.map +1 -0
- package/dist/utils/quota-operations.js +692 -0
- package/dist/utils/quota-operations.js.map +1 -0
- package/dist/utils/recursive-mapping-job-operations.d.ts +42 -0
- package/dist/utils/recursive-mapping-job-operations.d.ts.map +1 -0
- package/dist/utils/recursive-mapping-job-operations.js +169 -0
- package/dist/utils/recursive-mapping-job-operations.js.map +1 -0
- package/dist/utils/relationship-operations.d.ts +130 -0
- package/dist/utils/relationship-operations.d.ts.map +1 -0
- package/dist/utils/relationship-operations.js +329 -0
- package/dist/utils/relationship-operations.js.map +1 -0
- package/dist/utils/sales-pipeline-operations.d.ts +163 -0
- package/dist/utils/sales-pipeline-operations.d.ts.map +1 -0
- package/dist/utils/sales-pipeline-operations.js +725 -0
- package/dist/utils/sales-pipeline-operations.js.map +1 -0
- package/dist/utils/skills-operations.d.ts +117 -0
- package/dist/utils/skills-operations.d.ts.map +1 -0
- package/dist/utils/skills-operations.js +487 -0
- package/dist/utils/skills-operations.js.map +1 -0
- package/dist/utils/subscription-operations.d.ts +123 -0
- package/dist/utils/subscription-operations.d.ts.map +1 -0
- package/dist/utils/subscription-operations.js +391 -0
- package/dist/utils/subscription-operations.js.map +1 -0
- package/dist/utils/unipile-account-operations.d.ts +96 -0
- package/dist/utils/unipile-account-operations.d.ts.map +1 -0
- package/dist/utils/unipile-account-operations.js +255 -0
- package/dist/utils/unipile-account-operations.js.map +1 -0
- package/dist/utils/user-industry-operations.d.ts +80 -0
- package/dist/utils/user-industry-operations.d.ts.map +1 -0
- package/dist/utils/user-industry-operations.js +237 -0
- package/dist/utils/user-industry-operations.js.map +1 -0
- package/dist/utils/user-operations.d.ts +87 -0
- package/dist/utils/user-operations.d.ts.map +1 -0
- package/dist/utils/user-operations.js +212 -0
- package/dist/utils/user-operations.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sales Pipeline Operations
|
|
4
|
+
*
|
|
5
|
+
* Database operations for sales pipelines and user assignments.
|
|
6
|
+
* Organization-scoped pipelines shared by all users in the organization.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createOrGetSalesPipeline = createOrGetSalesPipeline;
|
|
10
|
+
exports.getOrCreateOrganizationRelationshipAssignment = getOrCreateOrganizationRelationshipAssignment;
|
|
11
|
+
exports.createOrUpdateSalesPipelineAssignment = createOrUpdateSalesPipelineAssignment;
|
|
12
|
+
exports.getSalesPipelineAssignmentWithValidation = getSalesPipelineAssignmentWithValidation;
|
|
13
|
+
exports.addToSalesPipeline = addToSalesPipeline;
|
|
14
|
+
exports.addToSalesPipelineByAssignmentId = addToSalesPipelineByAssignmentId;
|
|
15
|
+
exports.getSalesPipelineById = getSalesPipelineById;
|
|
16
|
+
exports.getSalesPipelineForOrganization = getSalesPipelineForOrganization;
|
|
17
|
+
exports.updateSalesPipelineAssignmentStage = updateSalesPipelineAssignmentStage;
|
|
18
|
+
const pg_client_1 = require("../lib/pg-client");
|
|
19
|
+
const db_1 = require("../lib/db");
|
|
20
|
+
/**
|
|
21
|
+
* Create or get a sales pipeline entry for an organization
|
|
22
|
+
* Checks for existing entry via organization_user_relationship_assignment_id
|
|
23
|
+
* If the pipeline entry already exists, returns the existing one
|
|
24
|
+
*
|
|
25
|
+
* @param db - Database instance
|
|
26
|
+
* @param organizationId - Organization ID
|
|
27
|
+
* @param organizationUserRelationshipAssignmentId - Organization relationship assignment ID
|
|
28
|
+
* @param addedByOrganizationMemberId - Organization member who added this to the pipeline
|
|
29
|
+
* @returns Sales pipeline entry
|
|
30
|
+
*/
|
|
31
|
+
async function createOrGetSalesPipeline(db, organizationId, organizationUserRelationshipAssignmentId, addedByOrganizationMemberId // Mandatory - must be provided
|
|
32
|
+
) {
|
|
33
|
+
(0, db_1.debugLogDbOperation)("createOrGetSalesPipeline", "sales_pipelines", {
|
|
34
|
+
organizationId,
|
|
35
|
+
organizationUserRelationshipAssignmentId,
|
|
36
|
+
addedByOrganizationMemberId,
|
|
37
|
+
});
|
|
38
|
+
// Try to get existing pipeline entry via assignment
|
|
39
|
+
const existingSql = `
|
|
40
|
+
SELECT sp.id, sp.organization_id, sp.added_by_organization_member_id, sp.created_at
|
|
41
|
+
FROM public.sales_pipelines sp
|
|
42
|
+
INNER JOIN public.sales_pipeline_assignments spa ON spa.sales_pipeline_id = sp.id
|
|
43
|
+
WHERE sp.organization_id = $1
|
|
44
|
+
AND spa.organization_user_relationship_assignment_id = $2
|
|
45
|
+
LIMIT 1
|
|
46
|
+
`;
|
|
47
|
+
const existing = await (0, pg_client_1.queryOne)(db, existingSql, [
|
|
48
|
+
organizationId,
|
|
49
|
+
organizationUserRelationshipAssignmentId,
|
|
50
|
+
]);
|
|
51
|
+
if (existing) {
|
|
52
|
+
return {
|
|
53
|
+
id: existing.id,
|
|
54
|
+
organizationId: existing.organization_id,
|
|
55
|
+
addedByOrganizationMemberId: existing.added_by_organization_member_id,
|
|
56
|
+
createdAt: existing.created_at,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Create new pipeline entry
|
|
60
|
+
const insertSql = `
|
|
61
|
+
INSERT INTO public.sales_pipelines (organization_id, added_by_organization_member_id)
|
|
62
|
+
VALUES ($1, $2)
|
|
63
|
+
RETURNING id, organization_id, added_by_organization_member_id, created_at
|
|
64
|
+
`;
|
|
65
|
+
const result = await (0, pg_client_1.queryOne)(db, insertSql, [organizationId, addedByOrganizationMemberId]);
|
|
66
|
+
if (!result) {
|
|
67
|
+
throw new Error("Failed to create sales pipeline entry");
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
id: result.id,
|
|
71
|
+
organizationId: result.organization_id,
|
|
72
|
+
addedByOrganizationMemberId: result.added_by_organization_member_id,
|
|
73
|
+
createdAt: result.created_at,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get or create an organization_user_relationships_assignments entry
|
|
78
|
+
*
|
|
79
|
+
* @param db - Database instance
|
|
80
|
+
* @param organizationId - Organization ID
|
|
81
|
+
* @param linkedinRelationshipId - LinkedIn relationship ID
|
|
82
|
+
* @param organizationMemberId - Organization member ID (the member whose LinkedIn account is part of the relationship)
|
|
83
|
+
* @returns Organization relationship assignment ID
|
|
84
|
+
*/
|
|
85
|
+
async function getOrCreateOrganizationRelationshipAssignment(db, organizationId, linkedinRelationshipId, organizationMemberId) {
|
|
86
|
+
// Try to get existing assignment for this specific organization member
|
|
87
|
+
const existingSql = `
|
|
88
|
+
SELECT id
|
|
89
|
+
FROM public.organization_user_relationships_assignments
|
|
90
|
+
WHERE organization_id = $1
|
|
91
|
+
AND linkedin_relationship_id = $2
|
|
92
|
+
AND organization_member_id = $3
|
|
93
|
+
LIMIT 1
|
|
94
|
+
`;
|
|
95
|
+
const existing = await (0, pg_client_1.queryOne)(db, existingSql, [
|
|
96
|
+
organizationId,
|
|
97
|
+
linkedinRelationshipId,
|
|
98
|
+
organizationMemberId,
|
|
99
|
+
]);
|
|
100
|
+
if (existing) {
|
|
101
|
+
return existing.id;
|
|
102
|
+
}
|
|
103
|
+
// Create new assignment
|
|
104
|
+
const insertSql = `
|
|
105
|
+
INSERT INTO public.organization_user_relationships_assignments (organization_id, linkedin_relationship_id, organization_member_id, created_at)
|
|
106
|
+
VALUES ($1, $2, $3, NOW())
|
|
107
|
+
RETURNING id
|
|
108
|
+
`;
|
|
109
|
+
const result = await (0, pg_client_1.queryOne)(db, insertSql, [
|
|
110
|
+
organizationId,
|
|
111
|
+
linkedinRelationshipId,
|
|
112
|
+
organizationMemberId,
|
|
113
|
+
]);
|
|
114
|
+
if (!result) {
|
|
115
|
+
throw new Error("Failed to create organization relationship assignment");
|
|
116
|
+
}
|
|
117
|
+
return result.id;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create or update a sales pipeline assignment
|
|
121
|
+
* If assignment exists, updates the stage; otherwise creates new assignment with default stage 'prospect'
|
|
122
|
+
*
|
|
123
|
+
* @param db - Database instance
|
|
124
|
+
* @param salesPipelineId - Sales pipeline ID
|
|
125
|
+
* @param organizationUserRelationshipAssignmentId - Organization relationship assignment ID
|
|
126
|
+
* @param stage - Sales stage (default: 'prospect')
|
|
127
|
+
* @returns Sales pipeline assignment
|
|
128
|
+
*/
|
|
129
|
+
async function createOrUpdateSalesPipelineAssignment(db, salesPipelineId, organizationUserRelationshipAssignmentId, stage = "prospect") {
|
|
130
|
+
(0, db_1.debugLogDbOperation)("createOrUpdateSalesPipelineAssignment", "sales_pipeline_assignments", {
|
|
131
|
+
salesPipelineId,
|
|
132
|
+
organizationUserRelationshipAssignmentId,
|
|
133
|
+
stage,
|
|
134
|
+
});
|
|
135
|
+
// Try to get existing assignment
|
|
136
|
+
const existingSql = `
|
|
137
|
+
SELECT id, sales_pipeline_id, organization_user_relationship_assignment_id, stage, created_at, updated_at
|
|
138
|
+
FROM public.sales_pipeline_assignments
|
|
139
|
+
WHERE sales_pipeline_id = $1 AND organization_user_relationship_assignment_id = $2
|
|
140
|
+
LIMIT 1
|
|
141
|
+
`;
|
|
142
|
+
const existing = await (0, pg_client_1.queryOne)(db, existingSql, [
|
|
143
|
+
salesPipelineId,
|
|
144
|
+
organizationUserRelationshipAssignmentId,
|
|
145
|
+
]);
|
|
146
|
+
if (existing) {
|
|
147
|
+
// Update existing assignment if stage changed
|
|
148
|
+
if (existing.stage !== stage) {
|
|
149
|
+
const updateSql = `
|
|
150
|
+
UPDATE public.sales_pipeline_assignments
|
|
151
|
+
SET stage = $1, updated_at = now()
|
|
152
|
+
WHERE id = $2
|
|
153
|
+
RETURNING id, sales_pipeline_id, organization_user_relationship_assignment_id, stage, created_at, updated_at
|
|
154
|
+
`;
|
|
155
|
+
const updated = await (0, pg_client_1.queryOne)(db, updateSql, [stage, existing.id]);
|
|
156
|
+
if (!updated) {
|
|
157
|
+
throw new Error("Failed to update sales pipeline assignment");
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
id: updated.id,
|
|
161
|
+
salesPipelineId: updated.sales_pipeline_id,
|
|
162
|
+
organizationUserRelationshipAssignmentId: updated.organization_user_relationship_assignment_id,
|
|
163
|
+
stage: updated.stage,
|
|
164
|
+
createdAt: updated.created_at,
|
|
165
|
+
updatedAt: updated.updated_at,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Return existing assignment if stage unchanged
|
|
169
|
+
return {
|
|
170
|
+
id: existing.id,
|
|
171
|
+
salesPipelineId: existing.sales_pipeline_id,
|
|
172
|
+
organizationUserRelationshipAssignmentId: existing.organization_user_relationship_assignment_id,
|
|
173
|
+
stage: existing.stage,
|
|
174
|
+
createdAt: existing.created_at,
|
|
175
|
+
updatedAt: existing.updated_at,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// Create new assignment
|
|
179
|
+
const insertSql = `
|
|
180
|
+
INSERT INTO public.sales_pipeline_assignments (sales_pipeline_id, organization_user_relationship_assignment_id, stage)
|
|
181
|
+
VALUES ($1, $2, $3)
|
|
182
|
+
RETURNING id, sales_pipeline_id, organization_user_relationship_assignment_id, stage, created_at, updated_at
|
|
183
|
+
`;
|
|
184
|
+
const result = await (0, pg_client_1.queryOne)(db, insertSql, [
|
|
185
|
+
salesPipelineId,
|
|
186
|
+
organizationUserRelationshipAssignmentId,
|
|
187
|
+
stage,
|
|
188
|
+
]);
|
|
189
|
+
if (!result) {
|
|
190
|
+
throw new Error("Failed to create sales pipeline assignment");
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
id: result.id,
|
|
194
|
+
salesPipelineId: result.sales_pipeline_id,
|
|
195
|
+
organizationUserRelationshipAssignmentId: result.organization_user_relationship_assignment_id,
|
|
196
|
+
stage: result.stage,
|
|
197
|
+
createdAt: result.created_at,
|
|
198
|
+
updatedAt: result.updated_at,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get a sales pipeline assignment by ID and validate it belongs to an organization
|
|
203
|
+
*
|
|
204
|
+
* @param db - Database instance
|
|
205
|
+
* @param assignmentId - Sales pipeline assignment ID
|
|
206
|
+
* @param organizationId - Organization ID to validate against
|
|
207
|
+
* @returns Sales pipeline assignment with pipeline info, or null if not found or doesn't belong to org
|
|
208
|
+
*/
|
|
209
|
+
async function getSalesPipelineAssignmentWithValidation(db, assignmentId, organizationId) {
|
|
210
|
+
(0, db_1.debugLogDbOperation)("getSalesPipelineAssignmentWithValidation", "sales_pipeline_assignments", {
|
|
211
|
+
assignmentId,
|
|
212
|
+
organizationId,
|
|
213
|
+
});
|
|
214
|
+
const sql = `
|
|
215
|
+
SELECT
|
|
216
|
+
spa.id,
|
|
217
|
+
spa.sales_pipeline_id,
|
|
218
|
+
spa.organization_user_relationship_assignment_id,
|
|
219
|
+
spa.stage,
|
|
220
|
+
spa.created_at as assignment_created_at,
|
|
221
|
+
spa.updated_at as assignment_updated_at,
|
|
222
|
+
sp.id as pipeline_id,
|
|
223
|
+
sp.organization_id,
|
|
224
|
+
sp.added_by_organization_member_id,
|
|
225
|
+
sp.created_at as pipeline_created_at
|
|
226
|
+
FROM public.sales_pipeline_assignments spa
|
|
227
|
+
INNER JOIN public.sales_pipelines sp ON sp.id = spa.sales_pipeline_id
|
|
228
|
+
INNER JOIN public.organization_user_relationships_assignments oura ON oura.id = spa.organization_user_relationship_assignment_id
|
|
229
|
+
WHERE spa.id = $1 AND sp.organization_id = $2 AND oura.organization_id = $2
|
|
230
|
+
LIMIT 1
|
|
231
|
+
`;
|
|
232
|
+
const result = await (0, pg_client_1.queryOne)(db, sql, [assignmentId, organizationId]);
|
|
233
|
+
if (!result) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
assignment: {
|
|
238
|
+
id: result.id,
|
|
239
|
+
salesPipelineId: result.sales_pipeline_id,
|
|
240
|
+
organizationUserRelationshipAssignmentId: result.organization_user_relationship_assignment_id,
|
|
241
|
+
stage: result.stage,
|
|
242
|
+
createdAt: result.assignment_created_at,
|
|
243
|
+
updatedAt: result.assignment_updated_at,
|
|
244
|
+
},
|
|
245
|
+
pipeline: {
|
|
246
|
+
id: result.pipeline_id,
|
|
247
|
+
organizationId: result.organization_id,
|
|
248
|
+
addedByOrganizationMemberId: result.added_by_organization_member_id,
|
|
249
|
+
createdAt: result.pipeline_created_at,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Add a relationship to the sales pipeline
|
|
255
|
+
* Creates or gets the pipeline entry and organization relationship assignment, then creates/updates the pipeline assignment with default stage 'prospect'
|
|
256
|
+
*
|
|
257
|
+
* @param db - Database instance
|
|
258
|
+
* @param organizationId - Organization ID
|
|
259
|
+
* @param linkedinRelationshipId - LinkedIn relationship ID
|
|
260
|
+
* @returns Sales pipeline assignment
|
|
261
|
+
*/
|
|
262
|
+
async function addToSalesPipeline(db, organizationId, linkedinRelationshipId, addedByOrganizationMemberId // Mandatory - must be provided
|
|
263
|
+
) {
|
|
264
|
+
(0, db_1.debugLogDbOperation)("addToSalesPipeline", "sales_pipeline_assignments", {
|
|
265
|
+
organizationId,
|
|
266
|
+
linkedinRelationshipId,
|
|
267
|
+
addedByOrganizationMemberId,
|
|
268
|
+
});
|
|
269
|
+
// Find the organization member whose LinkedIn account is part of this relationship
|
|
270
|
+
// The relationship assignment should belong to the member who owns the relationship
|
|
271
|
+
const findOrgMemberSql = `
|
|
272
|
+
SELECT om.id
|
|
273
|
+
FROM linkedin.relationships r
|
|
274
|
+
INNER JOIN linkedin.accounts la ON (la.id = r.linkedin_account_id_a OR la.id = r.linkedin_account_id_b)
|
|
275
|
+
INNER JOIN public.organization_members om ON om.user_id = la.user_id
|
|
276
|
+
WHERE r.id = $1
|
|
277
|
+
AND om.organization_id = $2
|
|
278
|
+
AND om.id = $3
|
|
279
|
+
LIMIT 1
|
|
280
|
+
`;
|
|
281
|
+
const relationshipOwner = await (0, pg_client_1.queryOne)(db, findOrgMemberSql, [linkedinRelationshipId, organizationId, addedByOrganizationMemberId]);
|
|
282
|
+
// If the person adding to pipeline is not the relationship owner, try to find the actual owner
|
|
283
|
+
let organizationMemberId = addedByOrganizationMemberId;
|
|
284
|
+
if (!relationshipOwner) {
|
|
285
|
+
const findAnyOwnerSql = `
|
|
286
|
+
SELECT om.id
|
|
287
|
+
FROM linkedin.relationships r
|
|
288
|
+
INNER JOIN linkedin.accounts la ON (la.id = r.linkedin_account_id_a OR la.id = r.linkedin_account_id_b)
|
|
289
|
+
INNER JOIN public.organization_members om ON om.user_id = la.user_id
|
|
290
|
+
WHERE r.id = $1
|
|
291
|
+
AND om.organization_id = $2
|
|
292
|
+
LIMIT 1
|
|
293
|
+
`;
|
|
294
|
+
const actualOwner = await (0, pg_client_1.queryOne)(db, findAnyOwnerSql, [
|
|
295
|
+
linkedinRelationshipId,
|
|
296
|
+
organizationId,
|
|
297
|
+
]);
|
|
298
|
+
if (actualOwner) {
|
|
299
|
+
organizationMemberId = actualOwner.id;
|
|
300
|
+
}
|
|
301
|
+
// If no owner found, fall back to addedByOrganizationMemberId
|
|
302
|
+
}
|
|
303
|
+
// Get or create the organization relationship assignment first
|
|
304
|
+
const orgRelAssignmentId = await getOrCreateOrganizationRelationshipAssignment(db, organizationId, linkedinRelationshipId, organizationMemberId);
|
|
305
|
+
// Create or get the pipeline entry (checking via assignment)
|
|
306
|
+
const pipeline = await createOrGetSalesPipeline(db, organizationId, orgRelAssignmentId, addedByOrganizationMemberId);
|
|
307
|
+
// Create or update the pipeline assignment with default stage 'prospect'
|
|
308
|
+
const assignment = await createOrUpdateSalesPipelineAssignment(db, pipeline.id, orgRelAssignmentId, "prospect");
|
|
309
|
+
return assignment;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Add a relationship to the sales pipeline using organization relationship assignment ID
|
|
313
|
+
* This is more efficient as it already contains the organization and relationship context
|
|
314
|
+
*
|
|
315
|
+
* @param db - Database instance
|
|
316
|
+
* @param organizationUserRelationshipAssignmentId - Organization relationship assignment ID
|
|
317
|
+
* @param addedByOrganizationMemberId - Organization member who added this entry
|
|
318
|
+
* @returns Sales pipeline assignment
|
|
319
|
+
*/
|
|
320
|
+
async function addToSalesPipelineByAssignmentId(db, organizationUserRelationshipAssignmentId, addedByOrganizationMemberId) {
|
|
321
|
+
(0, db_1.debugLogDbOperation)("addToSalesPipelineByAssignmentId", "sales_pipeline_assignments", {
|
|
322
|
+
organizationUserRelationshipAssignmentId,
|
|
323
|
+
addedByOrganizationMemberId,
|
|
324
|
+
});
|
|
325
|
+
// First, get the organization relationship assignment to extract org_id
|
|
326
|
+
const getAssignmentSql = `
|
|
327
|
+
SELECT organization_id, linkedin_relationship_id, organization_member_id
|
|
328
|
+
FROM public.organization_user_relationships_assignments
|
|
329
|
+
WHERE id = $1
|
|
330
|
+
LIMIT 1
|
|
331
|
+
`;
|
|
332
|
+
const assignment = await (0, pg_client_1.queryOne)(db, getAssignmentSql, [organizationUserRelationshipAssignmentId]);
|
|
333
|
+
if (!assignment) {
|
|
334
|
+
throw new Error(`Organization relationship assignment not found: ${organizationUserRelationshipAssignmentId}`);
|
|
335
|
+
}
|
|
336
|
+
const organizationId = assignment.organization_id;
|
|
337
|
+
// Create or get the pipeline entry (checking via assignment)
|
|
338
|
+
const pipeline = await createOrGetSalesPipeline(db, organizationId, organizationUserRelationshipAssignmentId, addedByOrganizationMemberId);
|
|
339
|
+
// Create or update the pipeline assignment with default stage 'prospect'
|
|
340
|
+
const pipelineAssignment = await createOrUpdateSalesPipelineAssignment(db, pipeline.id, organizationUserRelationshipAssignmentId, "prospect");
|
|
341
|
+
return pipelineAssignment;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Get a sales pipeline entry by ID
|
|
345
|
+
*
|
|
346
|
+
* @param db - Database instance
|
|
347
|
+
* @param salesPipelineId - Sales pipeline ID
|
|
348
|
+
* @returns Sales pipeline entry or null if not found
|
|
349
|
+
*/
|
|
350
|
+
async function getSalesPipelineById(db, salesPipelineId) {
|
|
351
|
+
(0, db_1.debugLogDbOperation)("getSalesPipelineById", "sales_pipelines", {
|
|
352
|
+
salesPipelineId,
|
|
353
|
+
});
|
|
354
|
+
const sql = `
|
|
355
|
+
SELECT id, organization_id, added_by_organization_member_id, created_at
|
|
356
|
+
FROM public.sales_pipelines
|
|
357
|
+
WHERE id = $1
|
|
358
|
+
LIMIT 1
|
|
359
|
+
`;
|
|
360
|
+
const result = await (0, pg_client_1.queryOne)(db, sql, [salesPipelineId]);
|
|
361
|
+
if (!result) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
id: result.id,
|
|
366
|
+
organizationId: result.organization_id,
|
|
367
|
+
addedByOrganizationMemberId: result.added_by_organization_member_id,
|
|
368
|
+
createdAt: result.created_at,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get all sales pipeline entries for an organization with profile data
|
|
373
|
+
* Returns all pipeline assignments, with full relationship and profile information
|
|
374
|
+
*
|
|
375
|
+
* @param db - Database instance
|
|
376
|
+
* @param organizationId - Organization ID
|
|
377
|
+
* @param linkedinAccountId - Optional: filter by specific LinkedIn account's relationships
|
|
378
|
+
* @returns Array of sales pipeline entries with profile data
|
|
379
|
+
*/
|
|
380
|
+
async function getSalesPipelineForOrganization(db, organizationId, linkedinAccountId) {
|
|
381
|
+
(0, db_1.debugLogDbOperation)("getSalesPipelineForOrganization", "sales_pipeline_assignments", {
|
|
382
|
+
organizationId,
|
|
383
|
+
linkedinAccountId,
|
|
384
|
+
});
|
|
385
|
+
const sql = `
|
|
386
|
+
WITH org_member_accounts AS (
|
|
387
|
+
-- Get all LinkedIn account IDs for organization members
|
|
388
|
+
SELECT DISTINCT la.id as linkedin_account_id
|
|
389
|
+
FROM public.organization_members om
|
|
390
|
+
INNER JOIN linkedin.accounts la ON la.user_id = om.user_id
|
|
391
|
+
WHERE om.organization_id = $1
|
|
392
|
+
),
|
|
393
|
+
pipeline_assignments AS (
|
|
394
|
+
-- Get all assignments for pipelines in this organization
|
|
395
|
+
SELECT
|
|
396
|
+
spa.id as assignment_id,
|
|
397
|
+
spa.sales_pipeline_id,
|
|
398
|
+
spa.organization_user_relationship_assignment_id,
|
|
399
|
+
spa.stage,
|
|
400
|
+
spa.created_at as assignment_created_at,
|
|
401
|
+
spa.updated_at as assignment_updated_at,
|
|
402
|
+
oura.linkedin_relationship_id,
|
|
403
|
+
sp.added_by_organization_member_id
|
|
404
|
+
FROM public.sales_pipeline_assignments spa
|
|
405
|
+
INNER JOIN public.sales_pipelines sp ON sp.id = spa.sales_pipeline_id
|
|
406
|
+
INNER JOIN public.organization_user_relationships_assignments oura ON oura.id = spa.organization_user_relationship_assignment_id
|
|
407
|
+
INNER JOIN linkedin.relationships r ON r.id = oura.linkedin_relationship_id
|
|
408
|
+
WHERE sp.organization_id = $1 AND oura.organization_id = $1
|
|
409
|
+
${linkedinAccountId ? "AND (r.linkedin_account_id_a = $2 OR r.linkedin_account_id_b = $2)" : ""}
|
|
410
|
+
)
|
|
411
|
+
SELECT
|
|
412
|
+
pa.assignment_id,
|
|
413
|
+
pa.sales_pipeline_id,
|
|
414
|
+
pa.organization_user_relationship_assignment_id,
|
|
415
|
+
pa.stage,
|
|
416
|
+
pa.assignment_created_at,
|
|
417
|
+
pa.assignment_updated_at,
|
|
418
|
+
pa.linkedin_relationship_id,
|
|
419
|
+
-- Get the contributor/introducer's LinkedIn account (the one whose mapping job created the relationship)
|
|
420
|
+
-- The contributor is the one who can make the intro
|
|
421
|
+
CASE
|
|
422
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN r.linkedin_account_id_a
|
|
423
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN r.linkedin_account_id_b
|
|
424
|
+
-- Fallback: if mapping job info not available, use org member logic
|
|
425
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN r.linkedin_account_id_a
|
|
426
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN r.linkedin_account_id_b
|
|
427
|
+
ELSE NULL
|
|
428
|
+
END as introducer_linkedin_account_id,
|
|
429
|
+
-- Get the connection's LinkedIn account (the "other" side - the person we want to reach)
|
|
430
|
+
CASE
|
|
431
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN r.linkedin_account_id_b
|
|
432
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN r.linkedin_account_id_a
|
|
433
|
+
-- Fallback: if mapping job info not available, use org member logic
|
|
434
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN r.linkedin_account_id_b
|
|
435
|
+
ELSE r.linkedin_account_id_a
|
|
436
|
+
END as connection_id,
|
|
437
|
+
-- Connection profile data (the person we want to reach)
|
|
438
|
+
COALESCE(
|
|
439
|
+
CASE
|
|
440
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.first_name || ' ' || la_b.last_name
|
|
441
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.first_name || ' ' || la_a.last_name
|
|
442
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.first_name || ' ' || la_b.last_name
|
|
443
|
+
ELSE la_a.first_name || ' ' || la_a.last_name
|
|
444
|
+
END,
|
|
445
|
+
CASE
|
|
446
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.first_name
|
|
447
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.first_name
|
|
448
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.first_name
|
|
449
|
+
ELSE la_a.first_name
|
|
450
|
+
END
|
|
451
|
+
) as full_name,
|
|
452
|
+
CASE
|
|
453
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.first_name
|
|
454
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.first_name
|
|
455
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.first_name
|
|
456
|
+
ELSE la_a.first_name
|
|
457
|
+
END as first_name,
|
|
458
|
+
CASE
|
|
459
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.last_name
|
|
460
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.last_name
|
|
461
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.last_name
|
|
462
|
+
ELSE la_a.last_name
|
|
463
|
+
END as last_name,
|
|
464
|
+
CASE
|
|
465
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.headline
|
|
466
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.headline
|
|
467
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.headline
|
|
468
|
+
ELSE la_a.headline
|
|
469
|
+
END as headline,
|
|
470
|
+
CASE
|
|
471
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.summary
|
|
472
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.summary
|
|
473
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.summary
|
|
474
|
+
ELSE la_a.summary
|
|
475
|
+
END as summary,
|
|
476
|
+
CASE
|
|
477
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.location
|
|
478
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.location
|
|
479
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.location
|
|
480
|
+
ELSE la_a.location
|
|
481
|
+
END as location,
|
|
482
|
+
COALESCE(
|
|
483
|
+
CASE
|
|
484
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.profile_image_cloudfront_url
|
|
485
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.profile_image_cloudfront_url
|
|
486
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.profile_image_cloudfront_url
|
|
487
|
+
ELSE la_a.profile_image_cloudfront_url
|
|
488
|
+
END,
|
|
489
|
+
CASE
|
|
490
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.profile_picture_url
|
|
491
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.profile_picture_url
|
|
492
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.profile_picture_url
|
|
493
|
+
ELSE la_a.profile_picture_url
|
|
494
|
+
END
|
|
495
|
+
) as profile_image_url,
|
|
496
|
+
CASE
|
|
497
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.public_identifier
|
|
498
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.public_identifier
|
|
499
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.public_identifier
|
|
500
|
+
ELSE la_a.public_identifier
|
|
501
|
+
END as linkedin_public_identifier,
|
|
502
|
+
CASE
|
|
503
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.linkedin_identifier_acoa
|
|
504
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.linkedin_identifier_acoa
|
|
505
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.linkedin_identifier_acoa
|
|
506
|
+
ELSE la_a.linkedin_identifier_acoa
|
|
507
|
+
END as linkedin_identifier_acoa,
|
|
508
|
+
-- Connection user data (if the connection is a registered user)
|
|
509
|
+
CASE
|
|
510
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_b.user_id
|
|
511
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_a.user_id
|
|
512
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_b.user_id
|
|
513
|
+
ELSE la_a.user_id
|
|
514
|
+
END as connection_user_id,
|
|
515
|
+
CASE
|
|
516
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN u_b.given_name
|
|
517
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN u_a.given_name
|
|
518
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN u_b.given_name
|
|
519
|
+
ELSE u_a.given_name
|
|
520
|
+
END as connection_user_given_name,
|
|
521
|
+
CASE
|
|
522
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN u_b.family_name
|
|
523
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN u_a.family_name
|
|
524
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN u_b.family_name
|
|
525
|
+
ELSE u_a.family_name
|
|
526
|
+
END as connection_user_family_name,
|
|
527
|
+
-- Introducer/Contributor data (the person who can do the intro - the one whose mapping job created the relationship)
|
|
528
|
+
COALESCE(
|
|
529
|
+
CASE
|
|
530
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.first_name || ' ' || la_a.last_name
|
|
531
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.first_name || ' ' || la_b.last_name
|
|
532
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.first_name || ' ' || la_a.last_name
|
|
533
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.first_name || ' ' || la_b.last_name
|
|
534
|
+
ELSE NULL
|
|
535
|
+
END,
|
|
536
|
+
CASE
|
|
537
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.first_name
|
|
538
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.first_name
|
|
539
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.first_name
|
|
540
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.first_name
|
|
541
|
+
ELSE NULL
|
|
542
|
+
END
|
|
543
|
+
) as introducer_full_name,
|
|
544
|
+
CASE
|
|
545
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.first_name
|
|
546
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.first_name
|
|
547
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.first_name
|
|
548
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.first_name
|
|
549
|
+
ELSE NULL
|
|
550
|
+
END as introducer_first_name,
|
|
551
|
+
CASE
|
|
552
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.last_name
|
|
553
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.last_name
|
|
554
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.last_name
|
|
555
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.last_name
|
|
556
|
+
ELSE NULL
|
|
557
|
+
END as introducer_last_name,
|
|
558
|
+
CASE
|
|
559
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.headline
|
|
560
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.headline
|
|
561
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.headline
|
|
562
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.headline
|
|
563
|
+
ELSE NULL
|
|
564
|
+
END as introducer_headline,
|
|
565
|
+
COALESCE(
|
|
566
|
+
CASE
|
|
567
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.profile_image_cloudfront_url
|
|
568
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.profile_image_cloudfront_url
|
|
569
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.profile_image_cloudfront_url
|
|
570
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.profile_image_cloudfront_url
|
|
571
|
+
ELSE NULL
|
|
572
|
+
END,
|
|
573
|
+
CASE
|
|
574
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.profile_picture_url
|
|
575
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.profile_picture_url
|
|
576
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.profile_picture_url
|
|
577
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.profile_picture_url
|
|
578
|
+
ELSE NULL
|
|
579
|
+
END
|
|
580
|
+
) as introducer_profile_image_url,
|
|
581
|
+
CASE
|
|
582
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.public_identifier
|
|
583
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.public_identifier
|
|
584
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.public_identifier
|
|
585
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.public_identifier
|
|
586
|
+
ELSE NULL
|
|
587
|
+
END as introducer_linkedin_public_identifier,
|
|
588
|
+
CASE
|
|
589
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.linkedin_identifier_acoa
|
|
590
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.linkedin_identifier_acoa
|
|
591
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.linkedin_identifier_acoa
|
|
592
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.linkedin_identifier_acoa
|
|
593
|
+
ELSE NULL
|
|
594
|
+
END as introducer_linkedin_identifier_acoa,
|
|
595
|
+
CASE
|
|
596
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN la_a.user_id
|
|
597
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN la_b.user_id
|
|
598
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN la_a.user_id
|
|
599
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN la_b.user_id
|
|
600
|
+
ELSE NULL
|
|
601
|
+
END as introducer_user_id,
|
|
602
|
+
CASE
|
|
603
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN u_a.given_name
|
|
604
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN u_b.given_name
|
|
605
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN u_a.given_name
|
|
606
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN u_b.given_name
|
|
607
|
+
ELSE NULL
|
|
608
|
+
END as introducer_user_given_name,
|
|
609
|
+
CASE
|
|
610
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_a THEN u_a.family_name
|
|
611
|
+
WHEN mj.linkedin_account_id = r.linkedin_account_id_b THEN u_b.family_name
|
|
612
|
+
WHEN oma_a.linkedin_account_id IS NOT NULL THEN u_a.family_name
|
|
613
|
+
WHEN oma_b.linkedin_account_id IS NOT NULL THEN u_b.family_name
|
|
614
|
+
ELSE NULL
|
|
615
|
+
END as introducer_user_family_name,
|
|
616
|
+
-- Added by information (who added this entry to the sales pipeline)
|
|
617
|
+
pa.added_by_organization_member_id,
|
|
618
|
+
added_by_om.user_id as added_by_user_id,
|
|
619
|
+
COALESCE(
|
|
620
|
+
added_by_user.given_name || ' ' || added_by_user.family_name,
|
|
621
|
+
added_by_user.given_name,
|
|
622
|
+
added_by_user.family_name,
|
|
623
|
+
'Unknown'
|
|
624
|
+
) as added_by_full_name,
|
|
625
|
+
added_by_user.given_name as added_by_given_name,
|
|
626
|
+
added_by_user.family_name as added_by_family_name,
|
|
627
|
+
COALESCE(
|
|
628
|
+
added_by_la.profile_image_cloudfront_url,
|
|
629
|
+
added_by_la.profile_picture_url
|
|
630
|
+
) as added_by_profile_image_url
|
|
631
|
+
FROM pipeline_assignments pa
|
|
632
|
+
INNER JOIN linkedin.relationships r ON r.id = pa.linkedin_relationship_id
|
|
633
|
+
-- Get mapping job to determine which account is the contributor (the one whose mapping job created the relationship)
|
|
634
|
+
LEFT JOIN monitoring.mapping_jobs mj ON mj.id = r.mapping_job_id
|
|
635
|
+
-- Check if account A is an org member (fallback if mapping job info not available)
|
|
636
|
+
LEFT JOIN org_member_accounts oma_a ON oma_a.linkedin_account_id = r.linkedin_account_id_a
|
|
637
|
+
-- Check if account B is an org member (fallback if mapping job info not available)
|
|
638
|
+
LEFT JOIN org_member_accounts oma_b ON oma_b.linkedin_account_id = r.linkedin_account_id_b
|
|
639
|
+
-- Get LinkedIn account A data
|
|
640
|
+
INNER JOIN linkedin.accounts la_a ON la_a.id = r.linkedin_account_id_a
|
|
641
|
+
LEFT JOIN public.users u_a ON u_a.id = la_a.user_id
|
|
642
|
+
-- Get LinkedIn account B data
|
|
643
|
+
INNER JOIN linkedin.accounts la_b ON la_b.id = r.linkedin_account_id_b
|
|
644
|
+
LEFT JOIN public.users u_b ON u_b.id = la_b.user_id
|
|
645
|
+
-- Get added_by organization member and user data
|
|
646
|
+
LEFT JOIN public.organization_members added_by_om ON added_by_om.id = pa.added_by_organization_member_id
|
|
647
|
+
LEFT JOIN public.users added_by_user ON added_by_user.id = added_by_om.user_id
|
|
648
|
+
LEFT JOIN linkedin.accounts added_by_la ON added_by_la.user_id = added_by_om.user_id
|
|
649
|
+
ORDER BY pa.assignment_updated_at DESC, pa.assignment_created_at DESC
|
|
650
|
+
`;
|
|
651
|
+
const params = linkedinAccountId ? [organizationId, linkedinAccountId] : [organizationId];
|
|
652
|
+
const results = await (0, pg_client_1.query)(db, sql, params);
|
|
653
|
+
return results.map(r => ({
|
|
654
|
+
assignment_id: r.assignment_id,
|
|
655
|
+
sales_pipeline_id: r.sales_pipeline_id,
|
|
656
|
+
organization_user_relationship_assignment_id: r.organization_user_relationship_assignment_id,
|
|
657
|
+
stage: r.stage,
|
|
658
|
+
assignment_created_at: r.assignment_created_at,
|
|
659
|
+
assignment_updated_at: r.assignment_updated_at,
|
|
660
|
+
linkedin_relationship_id: r.linkedin_relationship_id,
|
|
661
|
+
connection_id: r.connection_id,
|
|
662
|
+
full_name: r.full_name,
|
|
663
|
+
first_name: r.first_name,
|
|
664
|
+
last_name: r.last_name,
|
|
665
|
+
headline: r.headline,
|
|
666
|
+
summary: r.summary,
|
|
667
|
+
location: r.location,
|
|
668
|
+
profile_image_url: r.profile_image_url,
|
|
669
|
+
linkedin_public_identifier: r.linkedin_public_identifier,
|
|
670
|
+
linkedin_identifier_acoa: r.linkedin_identifier_acoa,
|
|
671
|
+
connection_user_id: r.connection_user_id,
|
|
672
|
+
connection_user_given_name: r.connection_user_given_name,
|
|
673
|
+
connection_user_family_name: r.connection_user_family_name,
|
|
674
|
+
introducer_linkedin_account_id: r.introducer_linkedin_account_id,
|
|
675
|
+
introducer_full_name: r.introducer_full_name,
|
|
676
|
+
introducer_first_name: r.introducer_first_name,
|
|
677
|
+
introducer_last_name: r.introducer_last_name,
|
|
678
|
+
introducer_headline: r.introducer_headline,
|
|
679
|
+
introducer_profile_image_url: r.introducer_profile_image_url,
|
|
680
|
+
introducer_linkedin_public_identifier: r.introducer_linkedin_public_identifier,
|
|
681
|
+
introducer_linkedin_identifier_acoa: r.introducer_linkedin_identifier_acoa,
|
|
682
|
+
introducer_user_id: r.introducer_user_id,
|
|
683
|
+
introducer_user_given_name: r.introducer_user_given_name,
|
|
684
|
+
introducer_user_family_name: r.introducer_user_family_name,
|
|
685
|
+
added_by_organization_member_id: r.added_by_organization_member_id,
|
|
686
|
+
added_by_user_id: r.added_by_user_id,
|
|
687
|
+
added_by_full_name: r.added_by_full_name,
|
|
688
|
+
added_by_given_name: r.added_by_given_name,
|
|
689
|
+
added_by_family_name: r.added_by_family_name,
|
|
690
|
+
added_by_profile_image_url: r.added_by_profile_image_url,
|
|
691
|
+
}));
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Update the stage of a sales pipeline assignment
|
|
695
|
+
*
|
|
696
|
+
* @param db - Database instance
|
|
697
|
+
* @param assignmentId - Sales pipeline assignment ID
|
|
698
|
+
* @param newStage - New sales stage
|
|
699
|
+
* @returns Updated sales pipeline assignment
|
|
700
|
+
*/
|
|
701
|
+
async function updateSalesPipelineAssignmentStage(db, assignmentId, newStage) {
|
|
702
|
+
(0, db_1.debugLogDbOperation)("updateSalesPipelineAssignmentStage", "sales_pipeline_assignments", {
|
|
703
|
+
assignmentId,
|
|
704
|
+
newStage,
|
|
705
|
+
});
|
|
706
|
+
const updateSql = `
|
|
707
|
+
UPDATE public.sales_pipeline_assignments
|
|
708
|
+
SET stage = $1, updated_at = now()
|
|
709
|
+
WHERE id = $2
|
|
710
|
+
RETURNING id, sales_pipeline_id, organization_user_relationship_assignment_id, stage, created_at, updated_at
|
|
711
|
+
`;
|
|
712
|
+
const updated = await (0, pg_client_1.queryOne)(db, updateSql, [newStage, assignmentId]);
|
|
713
|
+
if (!updated) {
|
|
714
|
+
throw new Error("Failed to update sales pipeline assignment stage - assignment not found");
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
id: updated.id,
|
|
718
|
+
salesPipelineId: updated.sales_pipeline_id,
|
|
719
|
+
organizationUserRelationshipAssignmentId: updated.organization_user_relationship_assignment_id,
|
|
720
|
+
stage: updated.stage,
|
|
721
|
+
createdAt: updated.created_at,
|
|
722
|
+
updatedAt: updated.updated_at,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
//# sourceMappingURL=sales-pipeline-operations.js.map
|