@hed-hog/contact 0.0.299 → 0.0.300

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.
Files changed (29) hide show
  1. package/dist/person/person.service.js +350 -350
  2. package/hedhog/data/dashboard.yaml +6 -0
  3. package/hedhog/data/dashboard_component.yaml +87 -0
  4. package/hedhog/data/dashboard_component_role.yaml +55 -0
  5. package/hedhog/data/dashboard_item.yaml +95 -0
  6. package/hedhog/data/dashboard_role.yaml +6 -0
  7. package/hedhog/data/route.yaml +68 -68
  8. package/hedhog/frontend/app/dashboard/_components/dashboard-widgets.tsx.ejs +508 -0
  9. package/hedhog/frontend/app/dashboard/_components/use-crm-dashboard-data.ts.ejs +104 -0
  10. package/hedhog/frontend/app/dashboard/page.tsx.ejs +37 -431
  11. package/hedhog/frontend/widgets/next-actions.tsx.ejs +40 -0
  12. package/hedhog/frontend/widgets/overview-kpis.tsx.ejs +47 -0
  13. package/hedhog/frontend/widgets/owner-performance.tsx.ejs +42 -0
  14. package/hedhog/frontend/widgets/quick-access.tsx.ejs +29 -0
  15. package/hedhog/frontend/widgets/source-breakdown.tsx.ejs +40 -0
  16. package/hedhog/frontend/widgets/stage-distribution.tsx.ejs +40 -0
  17. package/hedhog/frontend/widgets/top-owners.tsx.ejs +42 -0
  18. package/hedhog/frontend/widgets/unattended.tsx.ejs +40 -0
  19. package/hedhog/table/crm_activity.yaml +68 -68
  20. package/hedhog/table/crm_stage_history.yaml +34 -34
  21. package/hedhog/table/person_company.yaml +27 -27
  22. package/package.json +4 -4
  23. package/src/person/dto/account.dto.ts +100 -100
  24. package/src/person/dto/activity.dto.ts +54 -54
  25. package/src/person/dto/dashboard-query.dto.ts +25 -25
  26. package/src/person/dto/followup-query.dto.ts +25 -25
  27. package/src/person/dto/reports-query.dto.ts +25 -25
  28. package/src/person/person.controller.ts +176 -176
  29. package/src/person/person.service.ts +4825 -4825
@@ -231,44 +231,44 @@ let PersonService = class PersonService {
231
231
  const visibilityFilter = allowCompanyRegistration
232
232
  ? api_prisma_1.Prisma.empty
233
233
  : api_prisma_1.Prisma.sql `AND p.type = 'individual'`;
234
- const stageRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
235
- SELECT
236
- sh.to_stage,
237
- sh.from_stage,
238
- sh.changed_at,
239
- sh.changed_by_user_id
240
- FROM crm_stage_history sh
241
- INNER JOIN person p ON p.id = sh.person_id
242
- WHERE 1 = 1
243
- ${visibilityFilter}
244
- AND sh.changed_at >= CAST(${startIso} AS TIMESTAMPTZ)
245
- AND sh.changed_at <= CAST(${endIso} AS TIMESTAMPTZ)
234
+ const stageRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
235
+ SELECT
236
+ sh.to_stage,
237
+ sh.from_stage,
238
+ sh.changed_at,
239
+ sh.changed_by_user_id
240
+ FROM crm_stage_history sh
241
+ INNER JOIN person p ON p.id = sh.person_id
242
+ WHERE 1 = 1
243
+ ${visibilityFilter}
244
+ AND sh.changed_at >= CAST(${startIso} AS TIMESTAMPTZ)
245
+ AND sh.changed_at <= CAST(${endIso} AS TIMESTAMPTZ)
246
246
  `);
247
- const activityRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
248
- SELECT
249
- a.type,
250
- a.source_kind,
251
- a.created_at,
252
- a.completed_at,
253
- a.owner_user_id,
254
- a.created_by_user_id,
255
- a.completed_by_user_id
256
- FROM crm_activity a
257
- INNER JOIN person p ON p.id = a.person_id
258
- WHERE 1 = 1
259
- ${visibilityFilter}
260
- AND (
261
- (
262
- a.created_at >= CAST(${startIso} AS TIMESTAMPTZ)
263
- AND a.created_at <= CAST(${endIso} AS TIMESTAMPTZ)
264
- )
265
- OR (
266
- a.source_kind = 'followup'
267
- AND a.completed_at IS NOT NULL
268
- AND a.completed_at >= CAST(${startIso} AS TIMESTAMPTZ)
269
- AND a.completed_at <= CAST(${endIso} AS TIMESTAMPTZ)
270
- )
271
- )
247
+ const activityRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
248
+ SELECT
249
+ a.type,
250
+ a.source_kind,
251
+ a.created_at,
252
+ a.completed_at,
253
+ a.owner_user_id,
254
+ a.created_by_user_id,
255
+ a.completed_by_user_id
256
+ FROM crm_activity a
257
+ INNER JOIN person p ON p.id = a.person_id
258
+ WHERE 1 = 1
259
+ ${visibilityFilter}
260
+ AND (
261
+ (
262
+ a.created_at >= CAST(${startIso} AS TIMESTAMPTZ)
263
+ AND a.created_at <= CAST(${endIso} AS TIMESTAMPTZ)
264
+ )
265
+ OR (
266
+ a.source_kind = 'followup'
267
+ AND a.completed_at IS NOT NULL
268
+ AND a.completed_at >= CAST(${startIso} AS TIMESTAMPTZ)
269
+ AND a.completed_at <= CAST(${endIso} AS TIMESTAMPTZ)
270
+ )
271
+ )
272
272
  `);
273
273
  const summary = {
274
274
  new_leads: enrichedCreatedPeople.length,
@@ -518,13 +518,13 @@ let PersonService = class PersonService {
518
518
  const excludedPersonFilter = excludedPersonId > 0
519
519
  ? api_prisma_1.Prisma.sql ` AND c.person_id <> ${excludedPersonId}`
520
520
  : api_prisma_1.Prisma.empty;
521
- const phoneRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
522
- SELECT DISTINCT c.person_id
523
- FROM contact c
524
- JOIN contact_type ct ON ct.id = c.contact_type_id
525
- WHERE UPPER(ct.code) IN ('PHONE', 'MOBILE', 'WHATSAPP')
526
- AND regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') = ${normalizedPhone}
527
- ${excludedPersonFilter}
521
+ const phoneRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
522
+ SELECT DISTINCT c.person_id
523
+ FROM contact c
524
+ JOIN contact_type ct ON ct.id = c.contact_type_id
525
+ WHERE UPPER(ct.code) IN ('PHONE', 'MOBILE', 'WHATSAPP')
526
+ AND regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') = ${normalizedPhone}
527
+ ${excludedPersonFilter}
528
528
  `);
529
529
  for (const row of phoneRows) {
530
530
  this.addDuplicateReason(reasonsByPersonId, row.person_id, 'phone');
@@ -537,12 +537,12 @@ let PersonService = class PersonService {
537
537
  const documentTypeFilter = documentTypeId > 0
538
538
  ? api_prisma_1.Prisma.sql ` AND d.document_type_id = ${documentTypeId}`
539
539
  : api_prisma_1.Prisma.empty;
540
- const documentRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
541
- SELECT DISTINCT d.person_id
542
- FROM document d
543
- WHERE regexp_replace(COALESCE(d.value, ''), '[^0-9]+', '', 'g') = ${normalizedDocument}
544
- ${excludedPersonFilter}
545
- ${documentTypeFilter}
540
+ const documentRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
541
+ SELECT DISTINCT d.person_id
542
+ FROM document d
543
+ WHERE regexp_replace(COALESCE(d.value, ''), '[^0-9]+', '', 'g') = ${normalizedDocument}
544
+ ${excludedPersonFilter}
545
+ ${documentTypeFilter}
546
546
  `);
547
547
  for (const row of documentRows) {
548
548
  this.addDuplicateReason(reasonsByPersonId, row.person_id, 'document');
@@ -717,26 +717,26 @@ let PersonService = class PersonService {
717
717
  lifecycleStage: paginationParams.lifecycle_stage,
718
718
  });
719
719
  const orderBy = this.getAccountOrderBySql(paginationParams.sortField, paginationParams.sortOrder);
720
- const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
721
- SELECT COUNT(*) AS total
722
- FROM person p
723
- INNER JOIN person_company pc ON pc.id = p.id
724
- WHERE p.type = 'company'
725
- ${filters}
720
+ const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
721
+ SELECT COUNT(*) AS total
722
+ FROM person p
723
+ INNER JOIN person_company pc ON pc.id = p.id
724
+ WHERE p.type = 'company'
725
+ ${filters}
726
726
  `);
727
727
  const total = this.coerceCount((_a = totalRows[0]) === null || _a === void 0 ? void 0 : _a.total);
728
728
  if (total === 0) {
729
729
  return this.createEmptyAccountPagination(page, pageSize);
730
730
  }
731
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
732
- SELECT p.id AS person_id
733
- FROM person p
734
- INNER JOIN person_company pc ON pc.id = p.id
735
- WHERE p.type = 'company'
736
- ${filters}
737
- ORDER BY ${orderBy}, p.id ASC
738
- LIMIT ${pageSize}
739
- OFFSET ${skip}
731
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
732
+ SELECT p.id AS person_id
733
+ FROM person p
734
+ INNER JOIN person_company pc ON pc.id = p.id
735
+ WHERE p.type = 'company'
736
+ ${filters}
737
+ ORDER BY ${orderBy}, p.id ASC
738
+ LIMIT ${pageSize}
739
+ OFFSET ${skip}
740
740
  `);
741
741
  const personIds = rows.map((row) => row.person_id).filter((id) => id > 0);
742
742
  if (personIds.length === 0) {
@@ -784,15 +784,15 @@ let PersonService = class PersonService {
784
784
  prospects: 0,
785
785
  };
786
786
  }
787
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
788
- SELECT
789
- COUNT(*) AS total,
790
- COUNT(*) FILTER (WHERE p.status = 'active') AS active,
791
- COUNT(*) FILTER (WHERE pc.account_lifecycle_stage = 'customer') AS customers,
792
- COUNT(*) FILTER (WHERE pc.account_lifecycle_stage = 'prospect') AS prospects
793
- FROM person p
794
- INNER JOIN person_company pc ON pc.id = p.id
795
- WHERE p.type = 'company'
787
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
788
+ SELECT
789
+ COUNT(*) AS total,
790
+ COUNT(*) FILTER (WHERE p.status = 'active') AS active,
791
+ COUNT(*) FILTER (WHERE pc.account_lifecycle_stage = 'customer') AS customers,
792
+ COUNT(*) FILTER (WHERE pc.account_lifecycle_stage = 'prospect') AS prospects
793
+ FROM person p
794
+ INNER JOIN person_company pc ON pc.id = p.id
795
+ WHERE p.type = 'company'
796
796
  `);
797
797
  return {
798
798
  total: this.coerceCount((_a = rows[0]) === null || _a === void 0 ? void 0 : _a.total),
@@ -902,44 +902,44 @@ let PersonService = class PersonService {
902
902
  type: paginationParams.type,
903
903
  priority: paginationParams.priority,
904
904
  });
905
- const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
906
- SELECT COUNT(*) AS total
907
- FROM crm_activity a
908
- INNER JOIN person p ON p.id = a.person_id
909
- LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
910
- WHERE 1 = 1
911
- ${filters}
905
+ const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
906
+ SELECT COUNT(*) AS total
907
+ FROM crm_activity a
908
+ INNER JOIN person p ON p.id = a.person_id
909
+ LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
910
+ WHERE 1 = 1
911
+ ${filters}
912
912
  `);
913
913
  const total = this.coerceCount((_a = totalRows[0]) === null || _a === void 0 ? void 0 : _a.total);
914
914
  if (total === 0) {
915
915
  return this.createEmptyCrmActivityPagination(page, pageSize);
916
916
  }
917
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
918
- SELECT
919
- a.id,
920
- a.person_id,
921
- a.owner_user_id,
922
- a.type,
923
- a.subject,
924
- a.notes,
925
- a.due_at,
926
- a.completed_at,
927
- a.created_at,
928
- a.priority,
929
- p.name AS person_name,
930
- p.type AS person_type,
931
- p.status AS person_status,
932
- pc.trade_name AS person_trade_name,
933
- owner_user.name AS owner_user_name
934
- FROM crm_activity a
935
- INNER JOIN person p ON p.id = a.person_id
936
- LEFT JOIN person_company pc ON pc.id = p.id
937
- LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
938
- WHERE 1 = 1
939
- ${filters}
940
- ORDER BY a.due_at ASC, a.id ASC
941
- LIMIT ${pageSize}
942
- OFFSET ${skip}
917
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
918
+ SELECT
919
+ a.id,
920
+ a.person_id,
921
+ a.owner_user_id,
922
+ a.type,
923
+ a.subject,
924
+ a.notes,
925
+ a.due_at,
926
+ a.completed_at,
927
+ a.created_at,
928
+ a.priority,
929
+ p.name AS person_name,
930
+ p.type AS person_type,
931
+ p.status AS person_status,
932
+ pc.trade_name AS person_trade_name,
933
+ owner_user.name AS owner_user_name
934
+ FROM crm_activity a
935
+ INNER JOIN person p ON p.id = a.person_id
936
+ LEFT JOIN person_company pc ON pc.id = p.id
937
+ LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
938
+ WHERE 1 = 1
939
+ ${filters}
940
+ ORDER BY a.due_at ASC, a.id ASC
941
+ LIMIT ${pageSize}
942
+ OFFSET ${skip}
943
943
  `);
944
944
  const data = rows.map((row) => this.mapCrmActivityListRow(row));
945
945
  const lastPage = Math.max(1, Math.ceil(total / pageSize));
@@ -959,24 +959,24 @@ let PersonService = class PersonService {
959
959
  const visibilityFilter = allowCompanyRegistration
960
960
  ? api_prisma_1.Prisma.empty
961
961
  : api_prisma_1.Prisma.sql `AND p.type = 'individual'`;
962
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
963
- SELECT
964
- COUNT(*) AS total,
965
- COUNT(*) FILTER (
966
- WHERE a.completed_at IS NULL
967
- AND a.due_at >= NOW()
968
- ) AS pending,
969
- COUNT(*) FILTER (
970
- WHERE a.completed_at IS NULL
971
- AND a.due_at < NOW()
972
- ) AS overdue,
973
- COUNT(*) FILTER (
974
- WHERE a.completed_at IS NOT NULL
975
- ) AS completed
976
- FROM crm_activity a
977
- INNER JOIN person p ON p.id = a.person_id
978
- WHERE 1 = 1
979
- ${visibilityFilter}
962
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
963
+ SELECT
964
+ COUNT(*) AS total,
965
+ COUNT(*) FILTER (
966
+ WHERE a.completed_at IS NULL
967
+ AND a.due_at >= NOW()
968
+ ) AS pending,
969
+ COUNT(*) FILTER (
970
+ WHERE a.completed_at IS NULL
971
+ AND a.due_at < NOW()
972
+ ) AS overdue,
973
+ COUNT(*) FILTER (
974
+ WHERE a.completed_at IS NOT NULL
975
+ ) AS completed
976
+ FROM crm_activity a
977
+ INNER JOIN person p ON p.id = a.person_id
978
+ WHERE 1 = 1
979
+ ${visibilityFilter}
980
980
  `);
981
981
  return {
982
982
  total: this.coerceCount((_a = rows[0]) === null || _a === void 0 ? void 0 : _a.total),
@@ -990,37 +990,37 @@ let PersonService = class PersonService {
990
990
  const visibilityFilter = allowCompanyRegistration
991
991
  ? api_prisma_1.Prisma.empty
992
992
  : api_prisma_1.Prisma.sql `AND p.type = 'individual'`;
993
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
994
- SELECT
995
- a.id,
996
- a.person_id,
997
- a.owner_user_id,
998
- a.created_by_user_id,
999
- a.completed_by_user_id,
1000
- a.type,
1001
- a.subject,
1002
- a.notes,
1003
- a.due_at,
1004
- a.completed_at,
1005
- a.created_at,
1006
- a.priority,
1007
- a.source_kind,
1008
- p.name AS person_name,
1009
- p.type AS person_type,
1010
- p.status AS person_status,
1011
- pc.trade_name AS person_trade_name,
1012
- owner_user.name AS owner_user_name,
1013
- created_by_user.name AS created_by_user_name,
1014
- completed_by_user.name AS completed_by_user_name
1015
- FROM crm_activity a
1016
- INNER JOIN person p ON p.id = a.person_id
1017
- LEFT JOIN person_company pc ON pc.id = p.id
1018
- LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
1019
- LEFT JOIN "user" created_by_user ON created_by_user.id = a.created_by_user_id
1020
- LEFT JOIN "user" completed_by_user ON completed_by_user.id = a.completed_by_user_id
1021
- WHERE a.id = ${id}
1022
- ${visibilityFilter}
1023
- LIMIT 1
993
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
994
+ SELECT
995
+ a.id,
996
+ a.person_id,
997
+ a.owner_user_id,
998
+ a.created_by_user_id,
999
+ a.completed_by_user_id,
1000
+ a.type,
1001
+ a.subject,
1002
+ a.notes,
1003
+ a.due_at,
1004
+ a.completed_at,
1005
+ a.created_at,
1006
+ a.priority,
1007
+ a.source_kind,
1008
+ p.name AS person_name,
1009
+ p.type AS person_type,
1010
+ p.status AS person_status,
1011
+ pc.trade_name AS person_trade_name,
1012
+ owner_user.name AS owner_user_name,
1013
+ created_by_user.name AS created_by_user_name,
1014
+ completed_by_user.name AS completed_by_user_name
1015
+ FROM crm_activity a
1016
+ INNER JOIN person p ON p.id = a.person_id
1017
+ LEFT JOIN person_company pc ON pc.id = p.id
1018
+ LEFT JOIN "user" owner_user ON owner_user.id = a.owner_user_id
1019
+ LEFT JOIN "user" created_by_user ON created_by_user.id = a.created_by_user_id
1020
+ LEFT JOIN "user" completed_by_user ON completed_by_user.id = a.completed_by_user_id
1021
+ WHERE a.id = ${id}
1022
+ ${visibilityFilter}
1023
+ LIMIT 1
1024
1024
  `);
1025
1025
  const row = rows[0];
1026
1026
  if (!row) {
@@ -1035,17 +1035,17 @@ let PersonService = class PersonService {
1035
1035
  ? api_prisma_1.Prisma.empty
1036
1036
  : api_prisma_1.Prisma.sql `AND p.type = 'individual'`;
1037
1037
  return this.prismaService.$transaction(async (tx) => {
1038
- const rows = (await tx.$queryRaw(api_prisma_1.Prisma.sql `
1039
- SELECT
1040
- a.id,
1041
- a.person_id,
1042
- a.completed_at,
1043
- a.source_kind
1044
- FROM crm_activity a
1045
- INNER JOIN person p ON p.id = a.person_id
1046
- WHERE a.id = ${id}
1047
- ${visibilityFilter}
1048
- LIMIT 1
1038
+ const rows = (await tx.$queryRaw(api_prisma_1.Prisma.sql `
1039
+ SELECT
1040
+ a.id,
1041
+ a.person_id,
1042
+ a.completed_at,
1043
+ a.source_kind
1044
+ FROM crm_activity a
1045
+ INNER JOIN person p ON p.id = a.person_id
1046
+ WHERE a.id = ${id}
1047
+ ${visibilityFilter}
1048
+ LIMIT 1
1049
1049
  `));
1050
1050
  const activity = rows[0];
1051
1051
  if (!activity) {
@@ -1058,13 +1058,13 @@ let PersonService = class PersonService {
1058
1058
  };
1059
1059
  }
1060
1060
  const completedAt = new Date();
1061
- await tx.$executeRaw(api_prisma_1.Prisma.sql `
1062
- UPDATE crm_activity
1063
- SET
1064
- completed_at = CAST(${completedAt.toISOString()} AS TIMESTAMPTZ),
1065
- completed_by_user_id = ${actorUserId},
1066
- updated_at = NOW()
1067
- WHERE id = ${id}
1061
+ await tx.$executeRaw(api_prisma_1.Prisma.sql `
1062
+ UPDATE crm_activity
1063
+ SET
1064
+ completed_at = CAST(${completedAt.toISOString()} AS TIMESTAMPTZ),
1065
+ completed_by_user_id = ${actorUserId},
1066
+ updated_at = NOW()
1067
+ WHERE id = ${id}
1068
1068
  `);
1069
1069
  if (activity.source_kind === 'followup') {
1070
1070
  await this.upsertMetadataValue(tx, activity.person_id, NEXT_ACTION_AT_METADATA_KEY, null);
@@ -1093,30 +1093,30 @@ let PersonService = class PersonService {
1093
1093
  dateTo: paginationParams.date_to,
1094
1094
  });
1095
1095
  const followupTimestampSql = this.getFollowupTimestampSql();
1096
- const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1097
- SELECT COUNT(*) AS total
1098
- FROM person p
1099
- INNER JOIN person_metadata pm_next
1100
- ON pm_next.person_id = p.id
1101
- AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1102
- WHERE 1 = 1
1103
- ${filters}
1096
+ const totalRows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1097
+ SELECT COUNT(*) AS total
1098
+ FROM person p
1099
+ INNER JOIN person_metadata pm_next
1100
+ ON pm_next.person_id = p.id
1101
+ AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1102
+ WHERE 1 = 1
1103
+ ${filters}
1104
1104
  `);
1105
1105
  const total = this.coerceCount((_a = totalRows[0]) === null || _a === void 0 ? void 0 : _a.total);
1106
1106
  if (total === 0) {
1107
1107
  return this.createEmptyFollowupPagination(page, pageSize);
1108
1108
  }
1109
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1110
- SELECT p.id AS person_id
1111
- FROM person p
1112
- INNER JOIN person_metadata pm_next
1113
- ON pm_next.person_id = p.id
1114
- AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1115
- WHERE 1 = 1
1116
- ${filters}
1117
- ORDER BY ${followupTimestampSql} ASC, p.id ASC
1118
- LIMIT ${pageSize}
1119
- OFFSET ${skip}
1109
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1110
+ SELECT p.id AS person_id
1111
+ FROM person p
1112
+ INNER JOIN person_metadata pm_next
1113
+ ON pm_next.person_id = p.id
1114
+ AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1115
+ WHERE 1 = 1
1116
+ ${filters}
1117
+ ORDER BY ${followupTimestampSql} ASC, p.id ASC
1118
+ LIMIT ${pageSize}
1119
+ OFFSET ${skip}
1120
1120
  `);
1121
1121
  const personIds = rows.map((row) => row.person_id).filter((id) => id > 0);
1122
1122
  const people = personIds.length > 0
@@ -1185,25 +1185,25 @@ let PersonService = class PersonService {
1185
1185
  });
1186
1186
  const followupTimestampSql = this.getFollowupTimestampSql();
1187
1187
  const { todayStartIso, tomorrowStartIso } = this.getFollowupDayBoundaryIsoStrings();
1188
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1189
- SELECT
1190
- COUNT(*) AS total,
1191
- COUNT(*) FILTER (
1192
- WHERE ${followupTimestampSql} >= CAST(${todayStartIso} AS TIMESTAMPTZ)
1193
- AND ${followupTimestampSql} < CAST(${tomorrowStartIso} AS TIMESTAMPTZ)
1194
- ) AS today,
1195
- COUNT(*) FILTER (
1196
- WHERE ${followupTimestampSql} < CAST(${todayStartIso} AS TIMESTAMPTZ)
1197
- ) AS overdue,
1198
- COUNT(*) FILTER (
1199
- WHERE ${followupTimestampSql} >= CAST(${tomorrowStartIso} AS TIMESTAMPTZ)
1200
- ) AS upcoming
1201
- FROM person p
1202
- INNER JOIN person_metadata pm_next
1203
- ON pm_next.person_id = p.id
1204
- AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1205
- WHERE 1 = 1
1206
- ${filters}
1188
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
1189
+ SELECT
1190
+ COUNT(*) AS total,
1191
+ COUNT(*) FILTER (
1192
+ WHERE ${followupTimestampSql} >= CAST(${todayStartIso} AS TIMESTAMPTZ)
1193
+ AND ${followupTimestampSql} < CAST(${tomorrowStartIso} AS TIMESTAMPTZ)
1194
+ ) AS today,
1195
+ COUNT(*) FILTER (
1196
+ WHERE ${followupTimestampSql} < CAST(${todayStartIso} AS TIMESTAMPTZ)
1197
+ ) AS overdue,
1198
+ COUNT(*) FILTER (
1199
+ WHERE ${followupTimestampSql} >= CAST(${tomorrowStartIso} AS TIMESTAMPTZ)
1200
+ ) AS upcoming
1201
+ FROM person p
1202
+ INNER JOIN person_metadata pm_next
1203
+ ON pm_next.person_id = p.id
1204
+ AND pm_next.key = ${NEXT_ACTION_AT_METADATA_KEY}
1205
+ WHERE 1 = 1
1206
+ ${filters}
1207
1207
  `);
1208
1208
  return {
1209
1209
  total: this.coerceCount((_a = rows[0]) === null || _a === void 0 ? void 0 : _a.total),
@@ -2059,25 +2059,25 @@ let PersonService = class PersonService {
2059
2059
  const fromStageSql = fromStage
2060
2060
  ? api_prisma_1.Prisma.sql `CAST(${fromStage} AS crm_stage_history_from_stage_enum)`
2061
2061
  : api_prisma_1.Prisma.sql `NULL`;
2062
- await tx.$executeRaw(api_prisma_1.Prisma.sql `
2063
- INSERT INTO crm_stage_history (
2064
- person_id,
2065
- from_stage,
2066
- to_stage,
2067
- changed_by_user_id,
2068
- changed_at,
2069
- created_at,
2070
- updated_at
2071
- )
2072
- VALUES (
2073
- ${personId},
2074
- ${fromStageSql},
2075
- CAST(${toStage} AS crm_stage_history_to_stage_enum),
2076
- ${changedByUserId},
2077
- NOW(),
2078
- NOW(),
2079
- NOW()
2080
- )
2062
+ await tx.$executeRaw(api_prisma_1.Prisma.sql `
2063
+ INSERT INTO crm_stage_history (
2064
+ person_id,
2065
+ from_stage,
2066
+ to_stage,
2067
+ changed_by_user_id,
2068
+ changed_at,
2069
+ created_at,
2070
+ updated_at
2071
+ )
2072
+ VALUES (
2073
+ ${personId},
2074
+ ${fromStageSql},
2075
+ CAST(${toStage} AS crm_stage_history_to_stage_enum),
2076
+ ${changedByUserId},
2077
+ NOW(),
2078
+ NOW(),
2079
+ NOW()
2080
+ )
2081
2081
  `);
2082
2082
  }
2083
2083
  async syncPersonSubtypeData(tx, personId, currentType, data, locale) {
@@ -2723,94 +2723,94 @@ let PersonService = class PersonService {
2723
2723
  return ownerUserId && ownerUserId > 0 ? ownerUserId : null;
2724
2724
  }
2725
2725
  async upsertFollowupActivity(tx, { personId, ownerUserId, dueAt, notes, actorUserId, }) {
2726
- const existingRows = (await tx.$queryRaw(api_prisma_1.Prisma.sql `
2727
- SELECT id
2728
- FROM crm_activity
2729
- WHERE person_id = ${personId}
2730
- AND source_kind = 'followup'
2731
- AND completed_at IS NULL
2732
- ORDER BY id DESC
2733
- LIMIT 1
2726
+ const existingRows = (await tx.$queryRaw(api_prisma_1.Prisma.sql `
2727
+ SELECT id
2728
+ FROM crm_activity
2729
+ WHERE person_id = ${personId}
2730
+ AND source_kind = 'followup'
2731
+ AND completed_at IS NULL
2732
+ ORDER BY id DESC
2733
+ LIMIT 1
2734
2734
  `));
2735
2735
  const existing = existingRows[0];
2736
2736
  const normalizedNotes = this.normalizeTextOrNull(notes);
2737
2737
  if (existing) {
2738
- await tx.$executeRaw(api_prisma_1.Prisma.sql `
2739
- UPDATE crm_activity
2740
- SET
2741
- owner_user_id = ${ownerUserId},
2742
- type = CAST(${'task'} AS crm_activity_type_enum),
2743
- subject = ${this.getFollowupActivitySubject()},
2744
- notes = ${normalizedNotes},
2745
- due_at = CAST(${dueAt} AS TIMESTAMPTZ),
2746
- priority = CAST(${'medium'} AS crm_activity_priority_enum),
2747
- updated_at = NOW()
2748
- WHERE id = ${existing.id}
2738
+ await tx.$executeRaw(api_prisma_1.Prisma.sql `
2739
+ UPDATE crm_activity
2740
+ SET
2741
+ owner_user_id = ${ownerUserId},
2742
+ type = CAST(${'task'} AS crm_activity_type_enum),
2743
+ subject = ${this.getFollowupActivitySubject()},
2744
+ notes = ${normalizedNotes},
2745
+ due_at = CAST(${dueAt} AS TIMESTAMPTZ),
2746
+ priority = CAST(${'medium'} AS crm_activity_priority_enum),
2747
+ updated_at = NOW()
2748
+ WHERE id = ${existing.id}
2749
2749
  `);
2750
2750
  return;
2751
2751
  }
2752
- await tx.$executeRaw(api_prisma_1.Prisma.sql `
2753
- INSERT INTO crm_activity (
2754
- person_id,
2755
- owner_user_id,
2756
- created_by_user_id,
2757
- type,
2758
- subject,
2759
- notes,
2760
- due_at,
2761
- priority,
2762
- source_kind,
2763
- created_at,
2764
- updated_at
2765
- )
2766
- VALUES (
2767
- ${personId},
2768
- ${ownerUserId},
2769
- ${actorUserId},
2770
- CAST(${'task'} AS crm_activity_type_enum),
2771
- ${this.getFollowupActivitySubject()},
2772
- ${normalizedNotes},
2773
- CAST(${dueAt} AS TIMESTAMPTZ),
2774
- CAST(${'medium'} AS crm_activity_priority_enum),
2775
- CAST(${'followup'} AS crm_activity_source_kind_enum),
2776
- NOW(),
2777
- NOW()
2778
- )
2752
+ await tx.$executeRaw(api_prisma_1.Prisma.sql `
2753
+ INSERT INTO crm_activity (
2754
+ person_id,
2755
+ owner_user_id,
2756
+ created_by_user_id,
2757
+ type,
2758
+ subject,
2759
+ notes,
2760
+ due_at,
2761
+ priority,
2762
+ source_kind,
2763
+ created_at,
2764
+ updated_at
2765
+ )
2766
+ VALUES (
2767
+ ${personId},
2768
+ ${ownerUserId},
2769
+ ${actorUserId},
2770
+ CAST(${'task'} AS crm_activity_type_enum),
2771
+ ${this.getFollowupActivitySubject()},
2772
+ ${normalizedNotes},
2773
+ CAST(${dueAt} AS TIMESTAMPTZ),
2774
+ CAST(${'medium'} AS crm_activity_priority_enum),
2775
+ CAST(${'followup'} AS crm_activity_source_kind_enum),
2776
+ NOW(),
2777
+ NOW()
2778
+ )
2779
2779
  `);
2780
2780
  }
2781
2781
  async createCompletedInteractionActivity(tx, { personId, ownerUserId, interaction, actorUserId, }) {
2782
2782
  const completedAt = new Date(interaction.created_at);
2783
- await tx.$executeRaw(api_prisma_1.Prisma.sql `
2784
- INSERT INTO crm_activity (
2785
- person_id,
2786
- owner_user_id,
2787
- created_by_user_id,
2788
- completed_by_user_id,
2789
- type,
2790
- subject,
2791
- notes,
2792
- due_at,
2793
- completed_at,
2794
- priority,
2795
- source_kind,
2796
- created_at,
2797
- updated_at
2798
- )
2799
- VALUES (
2800
- ${personId},
2801
- ${ownerUserId},
2802
- ${actorUserId},
2803
- ${actorUserId},
2804
- CAST(${interaction.type} AS crm_activity_type_enum),
2805
- ${this.getInteractionActivitySubject(interaction.type)},
2806
- ${this.normalizeTextOrNull(interaction.notes)},
2807
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
2808
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
2809
- CAST(${'medium'} AS crm_activity_priority_enum),
2810
- CAST(${'interaction'} AS crm_activity_source_kind_enum),
2811
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
2812
- NOW()
2813
- )
2783
+ await tx.$executeRaw(api_prisma_1.Prisma.sql `
2784
+ INSERT INTO crm_activity (
2785
+ person_id,
2786
+ owner_user_id,
2787
+ created_by_user_id,
2788
+ completed_by_user_id,
2789
+ type,
2790
+ subject,
2791
+ notes,
2792
+ due_at,
2793
+ completed_at,
2794
+ priority,
2795
+ source_kind,
2796
+ created_at,
2797
+ updated_at
2798
+ )
2799
+ VALUES (
2800
+ ${personId},
2801
+ ${ownerUserId},
2802
+ ${actorUserId},
2803
+ ${actorUserId},
2804
+ CAST(${interaction.type} AS crm_activity_type_enum),
2805
+ ${this.getInteractionActivitySubject(interaction.type)},
2806
+ ${this.normalizeTextOrNull(interaction.notes)},
2807
+ CAST(${interaction.created_at} AS TIMESTAMPTZ),
2808
+ CAST(${interaction.created_at} AS TIMESTAMPTZ),
2809
+ CAST(${'medium'} AS crm_activity_priority_enum),
2810
+ CAST(${'interaction'} AS crm_activity_source_kind_enum),
2811
+ CAST(${interaction.created_at} AS TIMESTAMPTZ),
2812
+ NOW()
2813
+ )
2814
2814
  `);
2815
2815
  }
2816
2816
  getFollowupActivitySubject() {
@@ -3080,33 +3080,33 @@ let PersonService = class PersonService {
3080
3080
  const searchLike = `%${search}%`;
3081
3081
  const normalizedDigits = this.normalizeDigits(search);
3082
3082
  const digitsLike = `%${normalizedDigits}%`;
3083
- filters.push(api_prisma_1.Prisma.sql `
3084
- AND (
3085
- p.name ILIKE ${searchLike}
3086
- OR COALESCE(pc.trade_name, '') ILIKE ${searchLike}
3087
- OR COALESCE(pc.city, '') ILIKE ${searchLike}
3088
- OR COALESCE(pc.state, '') ILIKE ${searchLike}
3089
- OR EXISTS (
3090
- SELECT 1
3091
- FROM contact c
3092
- INNER JOIN contact_type ct ON ct.id = c.contact_type_id
3093
- WHERE c.person_id = p.id
3094
- AND UPPER(ct.code) = 'EMAIL'
3095
- AND c.value ILIKE ${searchLike}
3096
- )
3083
+ filters.push(api_prisma_1.Prisma.sql `
3084
+ AND (
3085
+ p.name ILIKE ${searchLike}
3086
+ OR COALESCE(pc.trade_name, '') ILIKE ${searchLike}
3087
+ OR COALESCE(pc.city, '') ILIKE ${searchLike}
3088
+ OR COALESCE(pc.state, '') ILIKE ${searchLike}
3089
+ OR EXISTS (
3090
+ SELECT 1
3091
+ FROM contact c
3092
+ INNER JOIN contact_type ct ON ct.id = c.contact_type_id
3093
+ WHERE c.person_id = p.id
3094
+ AND UPPER(ct.code) = 'EMAIL'
3095
+ AND c.value ILIKE ${searchLike}
3096
+ )
3097
3097
  ${normalizedDigits.length > 0
3098
- ? api_prisma_1.Prisma.sql `
3099
- OR EXISTS (
3100
- SELECT 1
3101
- FROM contact c
3102
- INNER JOIN contact_type ct ON ct.id = c.contact_type_id
3103
- WHERE c.person_id = p.id
3104
- AND UPPER(ct.code) IN ('PHONE', 'MOBILE', 'WHATSAPP')
3105
- AND regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') ILIKE ${digitsLike}
3106
- )
3098
+ ? api_prisma_1.Prisma.sql `
3099
+ OR EXISTS (
3100
+ SELECT 1
3101
+ FROM contact c
3102
+ INNER JOIN contact_type ct ON ct.id = c.contact_type_id
3103
+ WHERE c.person_id = p.id
3104
+ AND UPPER(ct.code) IN ('PHONE', 'MOBILE', 'WHATSAPP')
3105
+ AND regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') ILIKE ${digitsLike}
3106
+ )
3107
3107
  `
3108
- : api_prisma_1.Prisma.empty}
3109
- )
3108
+ : api_prisma_1.Prisma.empty}
3109
+ )
3110
3110
  `);
3111
3111
  }
3112
3112
  return filters.length > 0 ? api_prisma_1.Prisma.join(filters, '\n') : api_prisma_1.Prisma.empty;
@@ -3156,13 +3156,13 @@ let PersonService = class PersonService {
3156
3156
  }
3157
3157
  if (search) {
3158
3158
  const searchLike = `%${search}%`;
3159
- filters.push(api_prisma_1.Prisma.sql `
3160
- AND (
3161
- a.subject ILIKE ${searchLike}
3162
- OR COALESCE(a.notes, '') ILIKE ${searchLike}
3163
- OR p.name ILIKE ${searchLike}
3164
- OR COALESCE(owner_user.name, '') ILIKE ${searchLike}
3165
- )
3159
+ filters.push(api_prisma_1.Prisma.sql `
3160
+ AND (
3161
+ a.subject ILIKE ${searchLike}
3162
+ OR COALESCE(a.notes, '') ILIKE ${searchLike}
3163
+ OR p.name ILIKE ${searchLike}
3164
+ OR COALESCE(owner_user.name, '') ILIKE ${searchLike}
3165
+ )
3166
3166
  `);
3167
3167
  }
3168
3168
  return filters.length > 0 ? api_prisma_1.Prisma.join(filters, '\n') : api_prisma_1.Prisma.empty;
@@ -3243,16 +3243,16 @@ let PersonService = class PersonService {
3243
3243
  }
3244
3244
  async findPersonIdsByNormalizedDigits(normalizedDigits) {
3245
3245
  const likeValue = `%${normalizedDigits}%`;
3246
- const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
3247
- SELECT DISTINCT p.id
3248
- FROM person p
3249
- LEFT JOIN contact c ON c.person_id = p.id
3250
- LEFT JOIN document d ON d.person_id = p.id
3251
- LEFT JOIN person_address pa ON pa.person_id = p.id
3252
- LEFT JOIN address a ON a.id = pa.address_id
3253
- WHERE regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3254
- OR regexp_replace(COALESCE(d.value, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3255
- OR regexp_replace(COALESCE(a.postal_code, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3246
+ const rows = await this.prismaService.$queryRaw(api_prisma_1.Prisma.sql `
3247
+ SELECT DISTINCT p.id
3248
+ FROM person p
3249
+ LEFT JOIN contact c ON c.person_id = p.id
3250
+ LEFT JOIN document d ON d.person_id = p.id
3251
+ LEFT JOIN person_address pa ON pa.person_id = p.id
3252
+ LEFT JOIN address a ON a.id = pa.address_id
3253
+ WHERE regexp_replace(COALESCE(c.value, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3254
+ OR regexp_replace(COALESCE(d.value, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3255
+ OR regexp_replace(COALESCE(a.postal_code, ''), '[^0-9]+', '', 'g') ILIKE ${likeValue}
3256
3256
  `);
3257
3257
  return rows.map((row) => row.id);
3258
3258
  }