@aglyn/plugins-crm 1.0.0-beta.221 → 1.0.0-beta.223

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 (541) hide show
  1. package/README.md +2 -1
  2. package/package.json +13 -13
  3. package/src/lib/components/activity-list.js +12 -5
  4. package/src/lib/components/activity-list.js.map +1 -1
  5. package/src/lib/components/companies-bulk-bar.d.ts +1 -4
  6. package/src/lib/components/companies-bulk-bar.js +7 -20
  7. package/src/lib/components/companies-bulk-bar.js.map +1 -1
  8. package/src/lib/components/companies-section.d.ts +3 -3
  9. package/src/lib/components/companies-section.js +149 -46
  10. package/src/lib/components/companies-section.js.map +1 -1
  11. package/src/lib/components/company-edit-drawer.js +281 -20
  12. package/src/lib/components/company-edit-drawer.js.map +1 -1
  13. package/src/lib/components/company-properties-card.js +99 -8
  14. package/src/lib/components/company-properties-card.js.map +1 -1
  15. package/src/lib/components/contact-detail-page.js +3 -1
  16. package/src/lib/components/contact-detail-page.js.map +1 -1
  17. package/src/lib/components/contact-merge-dialog.js +33 -10
  18. package/src/lib/components/contact-merge-dialog.js.map +1 -1
  19. package/src/lib/components/contact-properties-card.d.ts +0 -69
  20. package/src/lib/components/contact-properties-card.js +350 -60
  21. package/src/lib/components/contact-properties-card.js.map +1 -1
  22. package/src/lib/components/contact-reports-to-field.d.ts +46 -0
  23. package/src/lib/components/contact-reports-to-field.js +67 -0
  24. package/src/lib/components/contact-reports-to-field.js.map +1 -0
  25. package/src/lib/components/contacts-bulk-bar.d.ts +1 -8
  26. package/src/lib/components/contacts-bulk-bar.js +7 -19
  27. package/src/lib/components/contacts-bulk-bar.js.map +1 -1
  28. package/src/lib/components/contacts-section.js +46 -33
  29. package/src/lib/components/contacts-section.js.map +1 -1
  30. package/src/lib/components/crm-call-actions.d.ts +10 -0
  31. package/src/lib/components/crm-call-actions.js +26 -9
  32. package/src/lib/components/crm-call-actions.js.map +1 -1
  33. package/src/lib/components/crm-next-activity-column.js +1 -1
  34. package/src/lib/components/crm-next-activity-column.js.map +1 -1
  35. package/src/lib/components/crm-record-picker.d.ts +15 -0
  36. package/src/lib/components/crm-record-picker.js +16 -10
  37. package/src/lib/components/crm-record-picker.js.map +1 -1
  38. package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
  39. package/src/lib/components/crm-transfer-buttons.js +167 -0
  40. package/src/lib/components/crm-transfer-buttons.js.map +1 -0
  41. package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
  42. package/src/lib/components/deal-contact-roles-card.js +368 -0
  43. package/src/lib/components/deal-contact-roles-card.js.map +1 -0
  44. package/src/lib/components/deal-detail-page.d.ts +3 -2
  45. package/src/lib/components/deal-detail-page.js +25 -6
  46. package/src/lib/components/deal-detail-page.js.map +1 -1
  47. package/src/lib/components/deal-edit-drawer.d.ts +10 -0
  48. package/src/lib/components/deal-edit-drawer.js +219 -10
  49. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  50. package/src/lib/components/deal-properties-card.d.ts +7 -0
  51. package/src/lib/components/deal-properties-card.js +34 -3
  52. package/src/lib/components/deal-properties-card.js.map +1 -1
  53. package/src/lib/components/deal-stage-card.d.ts +4 -0
  54. package/src/lib/components/deal-stage-card.js +10 -2
  55. package/src/lib/components/deal-stage-card.js.map +1 -1
  56. package/src/lib/components/deals-bulk-bar.d.ts +0 -3
  57. package/src/lib/components/deals-bulk-bar.js +7 -20
  58. package/src/lib/components/deals-bulk-bar.js.map +1 -1
  59. package/src/lib/components/deals-section.d.ts +3 -3
  60. package/src/lib/components/deals-section.js +123 -63
  61. package/src/lib/components/deals-section.js.map +1 -1
  62. package/src/lib/components/fields-section.js +31 -17
  63. package/src/lib/components/fields-section.js.map +1 -1
  64. package/src/lib/components/lead-convert-dialog.d.ts +4 -0
  65. package/src/lib/components/lead-convert-dialog.js +23 -1
  66. package/src/lib/components/lead-convert-dialog.js.map +1 -1
  67. package/src/lib/components/lead-detail-page.js +8 -2
  68. package/src/lib/components/lead-detail-page.js.map +1 -1
  69. package/src/lib/components/lead-profile-fields.d.ts +71 -0
  70. package/src/lib/components/lead-profile-fields.js +386 -0
  71. package/src/lib/components/lead-profile-fields.js.map +1 -0
  72. package/src/lib/components/lead-properties-card.d.ts +2 -1
  73. package/src/lib/components/lead-properties-card.js +85 -176
  74. package/src/lib/components/lead-properties-card.js.map +1 -1
  75. package/src/lib/components/lead-source-select.d.ts +4 -23
  76. package/src/lib/components/lead-source-select.js +9 -73
  77. package/src/lib/components/lead-source-select.js.map +1 -1
  78. package/src/lib/components/lead-status-chip.d.ts +9 -3
  79. package/src/lib/components/lead-status-chip.js +8 -4
  80. package/src/lib/components/lead-status-chip.js.map +1 -1
  81. package/src/lib/components/lead-status-options.d.ts +20 -0
  82. package/src/lib/components/lead-status-options.js +51 -0
  83. package/src/lib/components/lead-status-options.js.map +1 -0
  84. package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
  85. package/src/lib/components/lead-unqualify-dialog.js +50 -18
  86. package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
  87. package/src/lib/components/leads-bulk-bar.d.ts +0 -3
  88. package/src/lib/components/leads-bulk-bar.js +29 -29
  89. package/src/lib/components/leads-bulk-bar.js.map +1 -1
  90. package/src/lib/components/leads-section.js +120 -68
  91. package/src/lib/components/leads-section.js.map +1 -1
  92. package/src/lib/components/linked-deals-card.d.ts +4 -0
  93. package/src/lib/components/linked-deals-card.js +47 -3
  94. package/src/lib/components/linked-deals-card.js.map +1 -1
  95. package/src/lib/components/log-activity-dialog.js +43 -3
  96. package/src/lib/components/log-activity-dialog.js.map +1 -1
  97. package/src/lib/components/new-contact-drawer.d.ts +8 -0
  98. package/src/lib/components/new-contact-drawer.js +108 -6
  99. package/src/lib/components/new-contact-drawer.js.map +1 -1
  100. package/src/lib/components/new-lead-drawer.d.ts +9 -2
  101. package/src/lib/components/new-lead-drawer.js +88 -121
  102. package/src/lib/components/new-lead-drawer.js.map +1 -1
  103. package/src/lib/components/picklist-select.d.ts +36 -0
  104. package/src/lib/components/picklist-select.js +114 -0
  105. package/src/lib/components/picklist-select.js.map +1 -0
  106. package/src/lib/components/picklist-values-card.d.ts +34 -0
  107. package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
  108. package/src/lib/components/picklist-values-card.js.map +1 -0
  109. package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
  110. package/src/lib/components/pipeline-stages-dialog.js +26 -4
  111. package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
  112. package/src/lib/components/pipelines-dialog.js +15 -6
  113. package/src/lib/components/pipelines-dialog.js.map +1 -1
  114. package/src/lib/components/recent-activity-feed.js +20 -5
  115. package/src/lib/components/recent-activity-feed.js.map +1 -1
  116. package/src/lib/components/recipes-card.js +4 -2
  117. package/src/lib/components/recipes-card.js.map +1 -1
  118. package/src/lib/components/record-tasks-card.js +15 -8
  119. package/src/lib/components/record-tasks-card.js.map +1 -1
  120. package/src/lib/components/reports/forecast-card.d.ts +6 -0
  121. package/src/lib/components/reports/forecast-card.js +101 -3
  122. package/src/lib/components/reports/forecast-card.js.map +1 -1
  123. package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
  124. package/src/lib/components/reports/lead-funnel-card.js +9 -5
  125. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  126. package/src/lib/components/reports/lead-source-card.d.ts +23 -0
  127. package/src/lib/components/reports/lead-source-card.js +318 -0
  128. package/src/lib/components/reports/lead-source-card.js.map +1 -0
  129. package/src/lib/components/reports/pipeline-card.js +2 -1
  130. package/src/lib/components/reports/pipeline-card.js.map +1 -1
  131. package/src/lib/components/reports/report-export.js +1 -1
  132. package/src/lib/components/reports/report-export.js.map +1 -1
  133. package/src/lib/components/reports-section.js +8 -0
  134. package/src/lib/components/reports-section.js.map +1 -1
  135. package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
  136. package/src/lib/components/sharing-rule-drawer.js +108 -19
  137. package/src/lib/components/sharing-rule-drawer.js.map +1 -1
  138. package/src/lib/components/sharing-rules-card.js +11 -3
  139. package/src/lib/components/sharing-rules-card.js.map +1 -1
  140. package/src/lib/components/task-cells.d.ts +9 -1
  141. package/src/lib/components/task-cells.js +9 -4
  142. package/src/lib/components/task-cells.js.map +1 -1
  143. package/src/lib/components/task-edit-drawer.js +111 -43
  144. package/src/lib/components/task-edit-drawer.js.map +1 -1
  145. package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
  146. package/src/lib/components/tasks-bulk-bar.js +7 -20
  147. package/src/lib/components/tasks-bulk-bar.js.map +1 -1
  148. package/src/lib/components/tasks-calendar.d.ts +3 -0
  149. package/src/lib/components/tasks-calendar.js +8 -1
  150. package/src/lib/components/tasks-calendar.js.map +1 -1
  151. package/src/lib/components/tasks-section.d.ts +3 -3
  152. package/src/lib/components/tasks-section.js +58 -43
  153. package/src/lib/components/tasks-section.js.map +1 -1
  154. package/src/lib/constants/api-routes.d.ts +2 -9
  155. package/src/lib/constants/api-routes.js +1 -7
  156. package/src/lib/constants/api-routes.js.map +1 -1
  157. package/src/lib/constants/bundle-common.d.ts +6 -0
  158. package/src/lib/constants/bundle-common.js +11 -0
  159. package/src/lib/constants/bundle-common.js.map +1 -1
  160. package/src/lib/constants/company-filters.d.ts +14 -0
  161. package/src/lib/constants/company-filters.js +51 -1
  162. package/src/lib/constants/company-filters.js.map +1 -1
  163. package/src/lib/constants/contact-filters.d.ts +2 -0
  164. package/src/lib/constants/contact-filters.js +18 -1
  165. package/src/lib/constants/contact-filters.js.map +1 -1
  166. package/src/lib/constants/deal-filters.d.ts +22 -1
  167. package/src/lib/constants/deal-filters.js +67 -4
  168. package/src/lib/constants/deal-filters.js.map +1 -1
  169. package/src/lib/declarations.console-server.d.ts +3 -0
  170. package/src/lib/declarations.console-server.js +35 -0
  171. package/src/lib/declarations.console-server.js.map +1 -1
  172. package/src/lib/declarations.d.ts +26 -0
  173. package/src/lib/declarations.js +35 -0
  174. package/src/lib/declarations.js.map +1 -0
  175. package/src/lib/declarations.server.d.ts +11 -0
  176. package/src/lib/declarations.server.js +97 -1
  177. package/src/lib/declarations.server.js.map +1 -1
  178. package/src/lib/hooks/use-company-picklists.d.ts +15 -0
  179. package/src/lib/hooks/use-company-picklists.js +55 -0
  180. package/src/lib/hooks/use-company-picklists.js.map +1 -0
  181. package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
  182. package/src/lib/hooks/use-crm-picklist.js +53 -0
  183. package/src/lib/hooks/use-crm-picklist.js.map +1 -0
  184. package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
  185. package/src/lib/hooks/use-crm-record-names.js +45 -19
  186. package/src/lib/hooks/use-crm-record-names.js.map +1 -1
  187. package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
  188. package/src/lib/hooks/use-crm-task-picklists.js +45 -0
  189. package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
  190. package/src/lib/hooks/use-deals.d.ts +14 -0
  191. package/src/lib/hooks/use-deals.js +29 -0
  192. package/src/lib/hooks/use-deals.js.map +1 -1
  193. package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
  194. package/src/lib/hooks/use-lead-picklists.js +46 -0
  195. package/src/lib/hooks/use-lead-picklists.js.map +1 -0
  196. package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
  197. package/src/lib/hooks/use-lead-source-picklist.js +12 -22
  198. package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
  199. package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
  200. package/src/lib/hooks/use-lead-status-picklist.js +40 -0
  201. package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
  202. package/src/lib/model/companies.d.ts +32 -3
  203. package/src/lib/model/companies.js +133 -16
  204. package/src/lib/model/companies.js.map +1 -1
  205. package/src/lib/model/contact-merge.d.ts +27 -3
  206. package/src/lib/model/contact-merge.js +81 -7
  207. package/src/lib/model/contact-merge.js.map +1 -1
  208. package/src/lib/model/contact-record.d.ts +17 -0
  209. package/src/lib/model/contact-record.js +17 -2
  210. package/src/lib/model/contact-record.js.map +1 -1
  211. package/src/lib/model/contact-update.d.ts +31 -2
  212. package/src/lib/model/contact-update.js +14 -0
  213. package/src/lib/model/contact-update.js.map +1 -1
  214. package/src/lib/model/crm-list-query.d.ts +5 -0
  215. package/src/lib/model/crm-list-query.js +8 -0
  216. package/src/lib/model/crm-list-query.js.map +1 -1
  217. package/src/lib/model/crm-next-activity.d.ts +107 -0
  218. package/src/lib/model/crm-next-activity.js +140 -0
  219. package/src/lib/model/crm-next-activity.js.map +1 -0
  220. package/src/lib/model/crm-recipes.d.ts +40 -0
  221. package/src/lib/model/crm-recipes.js +259 -0
  222. package/src/lib/model/crm-recipes.js.map +1 -0
  223. package/src/lib/model/crm-record-counts.d.ts +36 -0
  224. package/src/lib/model/crm-record-counts.js +54 -0
  225. package/src/lib/model/crm-record-counts.js.map +1 -0
  226. package/src/lib/model/crm-record-lists.d.ts +23 -0
  227. package/src/lib/model/crm-record-lists.js +161 -0
  228. package/src/lib/model/crm-record-lists.js.map +1 -0
  229. package/src/lib/model/crm-reports.d.ts +73 -3
  230. package/src/lib/model/crm-reports.js +120 -1
  231. package/src/lib/model/crm-reports.js.map +1 -1
  232. package/src/lib/model/crm-sharing.d.ts +21 -3
  233. package/src/lib/model/crm-sharing.js +27 -6
  234. package/src/lib/model/crm-sharing.js.map +1 -1
  235. package/src/lib/model/crm-task-reminders.d.ts +152 -0
  236. package/src/lib/model/crm-task-reminders.js +161 -0
  237. package/src/lib/model/crm-task-reminders.js.map +1 -0
  238. package/src/lib/model/deal-board-model.d.ts +8 -2
  239. package/src/lib/model/deal-board-model.js +15 -2
  240. package/src/lib/model/deal-board-model.js.map +1 -1
  241. package/src/lib/model/deal-contact-roles.d.ts +45 -0
  242. package/src/lib/model/deal-contact-roles.js +73 -0
  243. package/src/lib/model/deal-contact-roles.js.map +1 -0
  244. package/src/lib/model/deal-form-model.d.ts +56 -0
  245. package/src/lib/model/deal-form-model.js +79 -7
  246. package/src/lib/model/deal-form-model.js.map +1 -1
  247. package/src/lib/model/download-text-file.d.ts +23 -0
  248. package/src/lib/model/download-text-file.js +34 -0
  249. package/src/lib/model/download-text-file.js.map +1 -0
  250. package/src/lib/model/lead-filters.d.ts +36 -2
  251. package/src/lib/model/lead-filters.js +123 -2
  252. package/src/lib/model/lead-filters.js.map +1 -1
  253. package/src/lib/model/lead-source-direction.d.ts +38 -0
  254. package/src/lib/model/lead-source-direction.js +52 -0
  255. package/src/lib/model/lead-source-direction.js.map +1 -0
  256. package/src/lib/model/next-activity.d.ts +1 -1
  257. package/src/lib/model/next-activity.js.map +1 -1
  258. package/src/lib/model/picklist-values.d.ts +119 -0
  259. package/src/lib/model/picklist-values.js +256 -0
  260. package/src/lib/model/picklist-values.js.map +1 -0
  261. package/src/lib/model/record-facts-disclosed.d.ts +81 -0
  262. package/src/lib/model/record-facts-disclosed.js +281 -0
  263. package/src/lib/model/record-facts-disclosed.js.map +1 -0
  264. package/src/lib/model/record-facts.d.ts +215 -34
  265. package/src/lib/model/record-facts.js +307 -128
  266. package/src/lib/model/record-facts.js.map +1 -1
  267. package/src/lib/model/task-picklists.d.ts +52 -0
  268. package/src/lib/model/task-picklists.js +83 -0
  269. package/src/lib/model/task-picklists.js.map +1 -0
  270. package/src/lib/model/task-routes.d.ts +13 -0
  271. package/src/lib/model/task-routes.js +23 -4
  272. package/src/lib/model/task-routes.js.map +1 -1
  273. package/src/lib/plan-entitlements.js +6 -4
  274. package/src/lib/plan-entitlements.js.map +1 -1
  275. package/src/lib/plugin.js +17 -0
  276. package/src/lib/plugin.js.map +1 -1
  277. package/src/lib/server/api-v1/contacts.js +220 -14
  278. package/src/lib/server/api-v1/contacts.js.map +1 -1
  279. package/src/lib/server/api-v1/crm-activities.js +14 -1
  280. package/src/lib/server/api-v1/crm-activities.js.map +1 -1
  281. package/src/lib/server/api-v1/crm-companies.js +123 -14
  282. package/src/lib/server/api-v1/crm-companies.js.map +1 -1
  283. package/src/lib/server/api-v1/crm-deals.js +291 -26
  284. package/src/lib/server/api-v1/crm-deals.js.map +1 -1
  285. package/src/lib/server/api-v1/crm-leads.js +144 -38
  286. package/src/lib/server/api-v1/crm-leads.js.map +1 -1
  287. package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
  288. package/src/lib/server/api-v1/crm-pipelines.js +8 -2
  289. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
  290. package/src/lib/server/api-v1/crm-tasks.js +78 -34
  291. package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
  292. package/src/lib/server/api-v1/openapi.d.ts +0 -11
  293. package/src/lib/server/api-v1/openapi.js +171 -16
  294. package/src/lib/server/api-v1/openapi.js.map +1 -1
  295. package/src/lib/server/assign-contact-owner.d.ts +188 -0
  296. package/src/lib/server/assign-contact-owner.js +446 -0
  297. package/src/lib/server/assign-contact-owner.js.map +1 -0
  298. package/src/lib/server/associate-company-by-domain.d.ts +70 -0
  299. package/src/lib/server/associate-company-by-domain.js +153 -0
  300. package/src/lib/server/associate-company-by-domain.js.map +1 -0
  301. package/src/lib/server/automation-steps.d.ts +140 -0
  302. package/src/lib/server/automation-steps.js +669 -0
  303. package/src/lib/server/automation-steps.js.map +1 -0
  304. package/src/lib/server/capture-contact.d.ts +17 -0
  305. package/src/lib/server/capture-contact.js +95 -26
  306. package/src/lib/server/capture-contact.js.map +1 -1
  307. package/src/lib/server/capture-host-contact.d.ts +104 -0
  308. package/src/lib/server/capture-host-contact.js +151 -0
  309. package/src/lib/server/capture-host-contact.js.map +1 -0
  310. package/src/lib/server/company-children.d.ts +25 -0
  311. package/src/lib/server/company-children.js +42 -0
  312. package/src/lib/server/company-children.js.map +1 -0
  313. package/src/lib/server/company-delete.d.ts +18 -0
  314. package/src/lib/server/company-delete.js +44 -21
  315. package/src/lib/server/company-delete.js.map +1 -1
  316. package/src/lib/server/contact-email-history.js +21 -19
  317. package/src/lib/server/contact-email-history.js.map +1 -1
  318. package/src/lib/server/contact-remove.js +27 -2
  319. package/src/lib/server/contact-remove.js.map +1 -1
  320. package/src/lib/server/contact-reports-to.d.ts +61 -0
  321. package/src/lib/server/contact-reports-to.js +114 -0
  322. package/src/lib/server/contact-reports-to.js.map +1 -0
  323. package/src/lib/server/contact-update.d.ts +42 -1
  324. package/src/lib/server/contact-update.js +204 -23
  325. package/src/lib/server/contact-update.js.map +1 -1
  326. package/src/lib/server/convert-host-lead.d.ts +46 -1
  327. package/src/lib/server/convert-host-lead.js +173 -15
  328. package/src/lib/server/convert-host-lead.js.map +1 -1
  329. package/src/lib/server/convert-open-lead.js +12 -5
  330. package/src/lib/server/convert-open-lead.js.map +1 -1
  331. package/src/lib/server/crm-email-activity.d.ts +118 -0
  332. package/src/lib/server/crm-email-activity.js +148 -0
  333. package/src/lib/server/crm-email-activity.js.map +1 -0
  334. package/src/lib/server/crm-inbound-email.js +1 -1
  335. package/src/lib/server/crm-inbound-email.js.map +1 -1
  336. package/src/lib/server/crm-next-activity.d.ts +74 -0
  337. package/src/lib/server/crm-next-activity.js +194 -0
  338. package/src/lib/server/crm-next-activity.js.map +1 -0
  339. package/src/lib/server/crm-record-indexes.d.ts +45 -0
  340. package/src/lib/server/crm-record-indexes.js +78 -0
  341. package/src/lib/server/crm-record-indexes.js.map +1 -0
  342. package/src/lib/server/crm-sharing.d.ts +9 -1
  343. package/src/lib/server/crm-sharing.js +23 -3
  344. package/src/lib/server/crm-sharing.js.map +1 -1
  345. package/src/lib/server/deal-contact-roles.d.ts +27 -0
  346. package/src/lib/server/deal-contact-roles.js +89 -0
  347. package/src/lib/server/deal-contact-roles.js.map +1 -0
  348. package/src/lib/server/email-send.js +2 -1
  349. package/src/lib/server/email-send.js.map +1 -1
  350. package/src/lib/server/lead-convert.d.ts +5 -0
  351. package/src/lib/server/lead-convert.js +19 -3
  352. package/src/lib/server/lead-convert.js.map +1 -1
  353. package/src/lib/server/lead-create.d.ts +17 -2
  354. package/src/lib/server/lead-create.js +46 -14
  355. package/src/lib/server/lead-create.js.map +1 -1
  356. package/src/lib/server/lead-nurturing.d.ts +6 -0
  357. package/src/lib/server/lead-nurturing.js +57 -20
  358. package/src/lib/server/lead-nurturing.js.map +1 -1
  359. package/src/lib/server/lead-picklists.d.ts +35 -0
  360. package/src/lib/server/lead-picklists.js +52 -0
  361. package/src/lib/server/lead-picklists.js.map +1 -0
  362. package/src/lib/server/lead-source-picklist.d.ts +1 -13
  363. package/src/lib/server/lead-source-picklist.js +6 -33
  364. package/src/lib/server/lead-source-picklist.js.map +1 -1
  365. package/src/lib/server/merge-contacts.js +23 -5
  366. package/src/lib/server/merge-contacts.js.map +1 -1
  367. package/src/lib/server/next-activity-routes.js +2 -1
  368. package/src/lib/server/next-activity-routes.js.map +1 -1
  369. package/src/lib/server/person-eraser.d.ts +53 -0
  370. package/src/lib/server/person-eraser.js +149 -0
  371. package/src/lib/server/person-eraser.js.map +1 -0
  372. package/src/lib/server/person-records.d.ts +69 -0
  373. package/src/lib/server/person-records.js +317 -0
  374. package/src/lib/server/person-records.js.map +1 -0
  375. package/src/lib/server/person-refund.d.ts +97 -0
  376. package/src/lib/server/person-refund.js +221 -0
  377. package/src/lib/server/person-refund.js.map +1 -0
  378. package/src/lib/server/picklist-values.d.ts +21 -0
  379. package/src/lib/server/picklist-values.js +308 -0
  380. package/src/lib/server/picklist-values.js.map +1 -0
  381. package/src/lib/server/read-picklist.d.ts +44 -0
  382. package/src/lib/server/read-picklist.js +65 -0
  383. package/src/lib/server/read-picklist.js.map +1 -0
  384. package/src/lib/server/recipe-routes.js +22 -8
  385. package/src/lib/server/recipe-routes.js.map +1 -1
  386. package/src/lib/server/record-email-state.js +7 -1
  387. package/src/lib/server/record-email-state.js.map +1 -1
  388. package/src/lib/server/record-facts.d.ts +3 -0
  389. package/src/lib/server/record-facts.js +233 -31
  390. package/src/lib/server/record-facts.js.map +1 -1
  391. package/src/lib/server/record-origin.d.ts +27 -0
  392. package/src/lib/server/record-origin.js +104 -0
  393. package/src/lib/server/record-origin.js.map +1 -0
  394. package/src/lib/server/record-timeline.js +40 -3
  395. package/src/lib/server/record-timeline.js.map +1 -1
  396. package/src/lib/server/suite-gate.js +4 -3
  397. package/src/lib/server/suite-gate.js.map +1 -1
  398. package/src/lib/server/task-reminders-route.js +2 -1
  399. package/src/lib/server/task-reminders-route.js.map +1 -1
  400. package/src/lib/server/task-routes.js +33 -17
  401. package/src/lib/server/task-routes.js.map +1 -1
  402. package/src/lib/server-deal-stage.d.ts +5 -1
  403. package/src/lib/server-deal-stage.js +10 -5
  404. package/src/lib/server-deal-stage.js.map +1 -1
  405. package/src/lib/server.d.ts +8 -3
  406. package/src/lib/server.js +47 -43
  407. package/src/lib/server.js.map +1 -1
  408. package/src/lib/testing/memory-firestore.d.ts +55 -0
  409. package/src/lib/testing/memory-firestore.js +335 -0
  410. package/src/lib/testing/memory-firestore.js.map +1 -0
  411. package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
  412. package/src/lib/testing/stand-in-email-send-index.js +49 -0
  413. package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
  414. package/src/lib/transfer/activities.d.ts +30 -0
  415. package/src/lib/transfer/activities.js +132 -0
  416. package/src/lib/transfer/activities.js.map +1 -0
  417. package/src/lib/transfer/aliases.d.ts +22 -0
  418. package/src/lib/transfer/aliases.js +1342 -0
  419. package/src/lib/transfer/aliases.js.map +1 -0
  420. package/src/lib/transfer/common.d.ts +222 -0
  421. package/src/lib/transfer/common.js +832 -0
  422. package/src/lib/transfer/common.js.map +1 -0
  423. package/src/lib/transfer/companies.d.ts +43 -0
  424. package/src/lib/transfer/companies.js +549 -0
  425. package/src/lib/transfer/companies.js.map +1 -0
  426. package/src/lib/transfer/contacts.d.ts +31 -0
  427. package/src/lib/transfer/contacts.js +727 -0
  428. package/src/lib/transfer/contacts.js.map +1 -0
  429. package/src/lib/transfer/deals.d.ts +66 -0
  430. package/src/lib/transfer/deals.js +698 -0
  431. package/src/lib/transfer/deals.js.map +1 -0
  432. package/src/lib/transfer/email-templates-package.d.ts +40 -0
  433. package/src/lib/transfer/email-templates-package.js +76 -0
  434. package/src/lib/transfer/email-templates-package.js.map +1 -0
  435. package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
  436. package/src/lib/transfer/email-templates-package.server.js +176 -0
  437. package/src/lib/transfer/email-templates-package.server.js.map +1 -0
  438. package/src/lib/transfer/fields.d.ts +102 -0
  439. package/src/lib/transfer/fields.js +1755 -0
  440. package/src/lib/transfer/fields.js.map +1 -0
  441. package/src/lib/transfer/leads.d.ts +30 -0
  442. package/src/lib/transfer/leads.js +582 -0
  443. package/src/lib/transfer/leads.js.map +1 -0
  444. package/src/lib/transfer/pipelines.d.ts +21 -0
  445. package/src/lib/transfer/pipelines.js +172 -0
  446. package/src/lib/transfer/pipelines.js.map +1 -0
  447. package/src/lib/transfer/register.d.ts +47 -0
  448. package/src/lib/transfer/register.js +277 -0
  449. package/src/lib/transfer/register.js.map +1 -0
  450. package/src/lib/transfer/tasks.d.ts +30 -0
  451. package/src/lib/transfer/tasks.js +418 -0
  452. package/src/lib/transfer/tasks.js.map +1 -0
  453. package/src/lib/transfer/ui.d.ts +20 -0
  454. package/src/lib/transfer/ui.js +42 -0
  455. package/src/lib/transfer/ui.js.map +1 -0
  456. package/src/lib/components/company-import-drawer.d.ts +0 -42
  457. package/src/lib/components/company-import-drawer.js +0 -99
  458. package/src/lib/components/company-import-drawer.js.map +0 -1
  459. package/src/lib/components/contact-import-drawer.d.ts +0 -46
  460. package/src/lib/components/contact-import-drawer.js +0 -149
  461. package/src/lib/components/contact-import-drawer.js.map +0 -1
  462. package/src/lib/components/crm-export-all-button.d.ts +0 -39
  463. package/src/lib/components/crm-export-all-button.js +0 -98
  464. package/src/lib/components/crm-export-all-button.js.map +0 -1
  465. package/src/lib/components/csv-import-drawer.d.ts +0 -139
  466. package/src/lib/components/csv-import-drawer.js +0 -757
  467. package/src/lib/components/csv-import-drawer.js.map +0 -1
  468. package/src/lib/components/deal-import-drawer.d.ts +0 -24
  469. package/src/lib/components/deal-import-drawer.js +0 -83
  470. package/src/lib/components/deal-import-drawer.js.map +0 -1
  471. package/src/lib/components/lead-import-drawer.d.ts +0 -27
  472. package/src/lib/components/lead-import-drawer.js +0 -118
  473. package/src/lib/components/lead-import-drawer.js.map +0 -1
  474. package/src/lib/components/lead-source-values-card.d.ts +0 -27
  475. package/src/lib/components/lead-source-values-card.js.map +0 -1
  476. package/src/lib/components/task-import-drawer.d.ts +0 -24
  477. package/src/lib/components/task-import-drawer.js +0 -82
  478. package/src/lib/components/task-import-drawer.js.map +0 -1
  479. package/src/lib/model/companies-csv.d.ts +0 -36
  480. package/src/lib/model/companies-csv.js +0 -38
  481. package/src/lib/model/companies-csv.js.map +0 -1
  482. package/src/lib/model/contacts-csv.d.ts +0 -54
  483. package/src/lib/model/contacts-csv.js +0 -67
  484. package/src/lib/model/contacts-csv.js.map +0 -1
  485. package/src/lib/model/crm-company-import.d.ts +0 -154
  486. package/src/lib/model/crm-company-import.js +0 -331
  487. package/src/lib/model/crm-company-import.js.map +0 -1
  488. package/src/lib/model/crm-csv.d.ts +0 -239
  489. package/src/lib/model/crm-csv.js +0 -467
  490. package/src/lib/model/crm-csv.js.map +0 -1
  491. package/src/lib/model/crm-deal-import.d.ts +0 -112
  492. package/src/lib/model/crm-deal-import.js +0 -263
  493. package/src/lib/model/crm-deal-import.js.map +0 -1
  494. package/src/lib/model/crm-import.d.ts +0 -250
  495. package/src/lib/model/crm-import.js +0 -467
  496. package/src/lib/model/crm-import.js.map +0 -1
  497. package/src/lib/model/crm-lead-import.d.ts +0 -138
  498. package/src/lib/model/crm-lead-import.js +0 -446
  499. package/src/lib/model/crm-lead-import.js.map +0 -1
  500. package/src/lib/model/crm-task-import.d.ts +0 -94
  501. package/src/lib/model/crm-task-import.js +0 -275
  502. package/src/lib/model/crm-task-import.js.map +0 -1
  503. package/src/lib/model/deals-csv.d.ts +0 -32
  504. package/src/lib/model/deals-csv.js +0 -36
  505. package/src/lib/model/deals-csv.js.map +0 -1
  506. package/src/lib/model/import-column-shapes.d.ts +0 -36
  507. package/src/lib/model/import-column-shapes.js +0 -76
  508. package/src/lib/model/import-column-shapes.js.map +0 -1
  509. package/src/lib/model/lead-source-values.d.ts +0 -82
  510. package/src/lib/model/lead-source-values.js +0 -149
  511. package/src/lib/model/lead-source-values.js.map +0 -1
  512. package/src/lib/model/leads-csv.d.ts +0 -40
  513. package/src/lib/model/leads-csv.js +0 -40
  514. package/src/lib/model/leads-csv.js.map +0 -1
  515. package/src/lib/model/tasks-csv.d.ts +0 -28
  516. package/src/lib/model/tasks-csv.js +0 -28
  517. package/src/lib/model/tasks-csv.js.map +0 -1
  518. package/src/lib/server/companies-import.d.ts +0 -57
  519. package/src/lib/server/companies-import.js +0 -242
  520. package/src/lib/server/companies-import.js.map +0 -1
  521. package/src/lib/server/contacts-import.d.ts +0 -109
  522. package/src/lib/server/contacts-import.js +0 -393
  523. package/src/lib/server/contacts-import.js.map +0 -1
  524. package/src/lib/server/deals-import.d.ts +0 -76
  525. package/src/lib/server/deals-import.js +0 -248
  526. package/src/lib/server/deals-import.js.map +0 -1
  527. package/src/lib/server/export-route.d.ts +0 -60
  528. package/src/lib/server/export-route.js +0 -447
  529. package/src/lib/server/export-route.js.map +0 -1
  530. package/src/lib/server/import-context.d.ts +0 -87
  531. package/src/lib/server/import-context.js +0 -182
  532. package/src/lib/server/import-context.js.map +0 -1
  533. package/src/lib/server/lead-source-values.d.ts +0 -50
  534. package/src/lib/server/lead-source-values.js +0 -191
  535. package/src/lib/server/lead-source-values.js.map +0 -1
  536. package/src/lib/server/leads-import.d.ts +0 -84
  537. package/src/lib/server/leads-import.js +0 -367
  538. package/src/lib/server/leads-import.js.map +0 -1
  539. package/src/lib/server/tasks-import.d.ts +0 -54
  540. package/src/lib/server/tasks-import.js +0 -160
  541. package/src/lib/server/tasks-import.js.map +0 -1
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * The `nextTaskAtMs` a record carries, as the Admin SDK writes it (AGL-2661).
18
+ *
19
+ * A contact, a company and a deal each denormalize when their earliest OPEN
20
+ * task is due, so a list can draw a "Next activity" column and answer
21
+ * "which deals have nothing scheduled" without a task query per row. This
22
+ * is the writer: given the records a task names, read that record's tasks
23
+ * back and store the answer `nextTaskAtMsOf` gives. Every server-side task
24
+ * writer — the console's task routes, the REST resource, the workflow step
25
+ * that files a task, the merge that repoints one — calls it after its own
26
+ * write; a client-direct task write asks the console's `crm/next-activity`
27
+ * route, which calls it too.
28
+ *
29
+ * Imported by module path rather than from the `@aglyn/aglyn/server`
30
+ * barrel, for the reason `contact-company-link.ts` gives.
31
+ *
32
+ * The task query is ONE equality on the link field, the query the record
33
+ * cards already run, and the status is judged in memory — no composite
34
+ * index, and a record with a hundred done tasks reads a hundred small
35
+ * documents once per write against it, which is the price of never being
36
+ * wrong. The record write is a bare `update()` of the one field, so
37
+ * `updatedAt` stays where the person's last edit left it: a task moving is
38
+ * not the record being edited, and the lists order by `updatedAt`.
39
+ */ import { CRM_COLLECTIONS } from "@aglyn/aglyn/app-utils/crm";
40
+ import { CRM_NEXT_ACTIVITY_FIELD, mergeNextActivityLinks, nextActivityLinksOfTask, nextTaskAtMsOf, readNextTaskAtMs } from "../model/crm-next-activity.js";
41
+ /** The collection each link field's record lives in, under `orgs/{orgId}/`. */ export const CRM_NEXT_ACTIVITY_COLLECTIONS = {
42
+ contactId: 'contacts',
43
+ companyId: CRM_COLLECTIONS.companies,
44
+ dealId: CRM_COLLECTIONS.deals
45
+ };
46
+ const isNotFound = (error)=>{
47
+ const { code, message } = error != null ? error : {};
48
+ return code === 5 || code === 'not-found' || code === 'NOT_FOUND' || typeof message === 'string' && message.includes('NOT_FOUND');
49
+ };
50
+ /**
51
+ * Store one record's `nextTaskAtMs` from its tasks. `null` for a record
52
+ * that no longer exists, else the value written.
53
+ */ async function recomputeOne(orgRef, field, id) {
54
+ const tasks = await orgRef.collection(CRM_COLLECTIONS.tasks).where(field, '==', id).get();
55
+ const value = nextTaskAtMsOf(tasks.docs.map((doc)=>({
56
+ status: doc.get('status'),
57
+ dueAtMs: doc.get('dueAtMs')
58
+ })));
59
+ try {
60
+ await orgRef.collection(CRM_NEXT_ACTIVITY_COLLECTIONS[field]).doc(id).update({
61
+ [CRM_NEXT_ACTIVITY_FIELD]: value
62
+ });
63
+ } catch (error) {
64
+ if (isNotFound(error)) return {
65
+ written: false
66
+ };
67
+ throw error;
68
+ }
69
+ return {
70
+ written: true,
71
+ value
72
+ };
73
+ }
74
+ /**
75
+ * Recompute `nextTaskAtMs` for every record the links name, once each.
76
+ *
77
+ * Hand in BOTH sides of a change — the links a task carried before an
78
+ * update and the ones it carries after — so a task moved from one deal to
79
+ * another leaves neither stale. A record the task named that has since been
80
+ * deleted is counted in `missing` and skipped; nothing here creates one.
81
+ */ export async function recomputeCrmNextTaskAt(firestore, orgId, links) {
82
+ const orgRef = firestore.collection('orgs').doc(orgId);
83
+ const result = {
84
+ records: 0,
85
+ missing: 0
86
+ };
87
+ for (const { field, id } of mergeNextActivityLinks(links)){
88
+ const outcome = await recomputeOne(orgRef, field, id);
89
+ if (outcome.written) result.records += 1;
90
+ else result.missing += 1;
91
+ }
92
+ return result;
93
+ }
94
+ /**
95
+ * The same, from a task document rather than a link — what a writer holds
96
+ * after it has read the task it is about to change or has just changed.
97
+ */ export function crmNextActivityLinksOf(task) {
98
+ return nextActivityLinksOfTask(task);
99
+ }
100
+ /** The most open tasks one org-wide recompute walks. */ export const CRM_NEXT_ACTIVITY_SWEEP_MAX = 20000;
101
+ /**
102
+ * The one-off recompute for a whole organization — the Fields section's
103
+ * "Recompute next activity" (AGL-2661), for the records written before the
104
+ * field existed and for whatever a client-direct write left behind.
105
+ *
106
+ * Two passes and no per-record task query: every OPEN task once, grouped
107
+ * by the records it names, so each record with something scheduled is
108
+ * written its earliest due time; then every record in the three
109
+ * collections that CARRIES a time and was not in that group, cleared to
110
+ * `null`. A record that was never written is left alone — every reader
111
+ * already treats absent as `null`, and writing a null onto every contact
112
+ * an org holds would cost a write per person to say nothing.
113
+ *
114
+ * The clearing pass runs only when the first pass read EVERY open task.
115
+ * "No open task names this record" is a statement about all of them, and
116
+ * an organization holding more than the cap has open tasks the sweep never
117
+ * saw — clearing on a partial read would blank records that are perfectly
118
+ * scheduled. Past the cap the sweep is the writing half alone, and
119
+ * `truncated` says so.
120
+ */ export async function sweepCrmNextTaskAt(firestore, orgId, options = {}) {
121
+ var _options_max;
122
+ const max = (_options_max = options.max) != null ? _options_max : CRM_NEXT_ACTIVITY_SWEEP_MAX;
123
+ const orgRef = firestore.collection('orgs').doc(orgId);
124
+ const open = await orgRef.collection(CRM_COLLECTIONS.tasks).where('status', '==', 'open').limit(max + 1).get();
125
+ const truncated = open.docs.length > max;
126
+ const docs = open.docs.slice(0, max);
127
+ const earliest = new Map();
128
+ for (const field of Object.keys(CRM_NEXT_ACTIVITY_COLLECTIONS)){
129
+ earliest.set(field, new Map());
130
+ }
131
+ for (const doc of docs){
132
+ const task = {
133
+ status: doc.get('status'),
134
+ dueAtMs: doc.get('dueAtMs')
135
+ };
136
+ const links = nextActivityLinksOfTask({
137
+ contactId: doc.get('contactId'),
138
+ companyId: doc.get('companyId'),
139
+ dealId: doc.get('dealId')
140
+ });
141
+ for (const { field, id } of mergeNextActivityLinks([
142
+ links
143
+ ])){
144
+ var _byId_get;
145
+ const byId = earliest.get(field);
146
+ byId.set(id, nextTaskAtMsOf([
147
+ task,
148
+ {
149
+ status: 'open',
150
+ dueAtMs: (_byId_get = byId.get(id)) != null ? _byId_get : null
151
+ }
152
+ ]));
153
+ }
154
+ }
155
+ const sweep = {
156
+ tasks: docs.length,
157
+ scheduled: 0,
158
+ cleared: 0,
159
+ truncated
160
+ };
161
+ for (const [field, byId] of earliest){
162
+ const records = orgRef.collection(CRM_NEXT_ACTIVITY_COLLECTIONS[field]);
163
+ for (const [id, value] of byId){
164
+ try {
165
+ await records.doc(id).update({
166
+ [CRM_NEXT_ACTIVITY_FIELD]: value
167
+ });
168
+ sweep.scheduled += 1;
169
+ } catch (error) {
170
+ if (!isNotFound(error)) throw error;
171
+ }
172
+ }
173
+ /*
174
+ * The stale half: a record that says something is scheduled when no
175
+ * open task names it any more. `> 0` is the one query a stored time
176
+ * answers without an index of its own, and matches what
177
+ * `readNextTaskAtMs` calls a usable value.
178
+ */ if (truncated) continue;
179
+ const carrying = await records.where(CRM_NEXT_ACTIVITY_FIELD, '>', 0).get();
180
+ for (const doc of carrying.docs){
181
+ if (byId.has(doc.id)) continue;
182
+ if (readNextTaskAtMs({
183
+ nextTaskAtMs: doc.get(CRM_NEXT_ACTIVITY_FIELD)
184
+ }) === null) continue;
185
+ await doc.ref.update({
186
+ [CRM_NEXT_ACTIVITY_FIELD]: null
187
+ });
188
+ sweep.cleared += 1;
189
+ }
190
+ }
191
+ return sweep;
192
+ }
193
+
194
+ //# sourceMappingURL=crm-next-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/crm-next-activity.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The `nextTaskAtMs` a record carries, as the Admin SDK writes it (AGL-2661).\n *\n * A contact, a company and a deal each denormalize when their earliest OPEN\n * task is due, so a list can draw a \"Next activity\" column and answer\n * \"which deals have nothing scheduled\" without a task query per row. This\n * is the writer: given the records a task names, read that record's tasks\n * back and store the answer `nextTaskAtMsOf` gives. Every server-side task\n * writer — the console's task routes, the REST resource, the workflow step\n * that files a task, the merge that repoints one — calls it after its own\n * write; a client-direct task write asks the console's `crm/next-activity`\n * route, which calls it too.\n *\n * Imported by module path rather than from the `@aglyn/aglyn/server`\n * barrel, for the reason `contact-company-link.ts` gives.\n *\n * The task query is ONE equality on the link field, the query the record\n * cards already run, and the status is judged in memory — no composite\n * index, and a record with a hundred done tasks reads a hundred small\n * documents once per write against it, which is the price of never being\n * wrong. The record write is a bare `update()` of the one field, so\n * `updatedAt` stays where the person's last edit left it: a task moving is\n * not the record being edited, and the lists order by `updatedAt`.\n */\n\nimport { CRM_COLLECTIONS } from '@aglyn/aglyn/app-utils/crm'\nimport {\n CRM_NEXT_ACTIVITY_FIELD,\n type CrmNextActivityLink,\n type CrmNextActivityLinkField,\n mergeNextActivityLinks,\n nextActivityLinksOfTask,\n nextTaskAtMsOf,\n readNextTaskAtMs,\n} from '../model/crm-next-activity'\n\n/** The collection each link field's record lives in, under `orgs/{orgId}/`. */\nexport const CRM_NEXT_ACTIVITY_COLLECTIONS: Readonly<Record<CrmNextActivityLinkField, string>> = {\n contactId: 'contacts',\n companyId: CRM_COLLECTIONS.companies,\n dealId: CRM_COLLECTIONS.deals,\n}\n\nexport interface CrmNextActivityRecompute {\n /** Records whose stored value was written (or rewritten to the same figure). */\n records: number\n /** Records that no longer exist — a task can outlive what it was filed against. */\n missing: number\n}\n\nconst isNotFound = (error: unknown): boolean => {\n const { code, message } = (error ?? {}) as { code?: unknown; message?: unknown }\n return (\n code === 5 ||\n code === 'not-found' ||\n code === 'NOT_FOUND' ||\n (typeof message === 'string' && message.includes('NOT_FOUND'))\n )\n}\n\n/**\n * Store one record's `nextTaskAtMs` from its tasks. `null` for a record\n * that no longer exists, else the value written.\n */\nasync function recomputeOne(\n orgRef: FirebaseFirestore.DocumentReference,\n field: CrmNextActivityLinkField,\n id: string,\n): Promise<{ written: true; value: number | null } | { written: false }> {\n const tasks = await orgRef\n .collection(CRM_COLLECTIONS.tasks)\n .where(field, '==', id)\n .get()\n const value = nextTaskAtMsOf(\n tasks.docs.map((doc) => ({\n status: doc.get('status'),\n dueAtMs: doc.get('dueAtMs'),\n })),\n )\n try {\n await orgRef\n .collection(CRM_NEXT_ACTIVITY_COLLECTIONS[field])\n .doc(id)\n .update({ [CRM_NEXT_ACTIVITY_FIELD]: value })\n } catch (error) {\n if (isNotFound(error)) return { written: false }\n throw error\n }\n return { written: true, value }\n}\n\n/**\n * Recompute `nextTaskAtMs` for every record the links name, once each.\n *\n * Hand in BOTH sides of a change — the links a task carried before an\n * update and the ones it carries after — so a task moved from one deal to\n * another leaves neither stale. A record the task named that has since been\n * deleted is counted in `missing` and skipped; nothing here creates one.\n */\nexport async function recomputeCrmNextTaskAt(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n links: readonly (CrmNextActivityLink | null | undefined)[],\n): Promise<CrmNextActivityRecompute> {\n const orgRef = firestore.collection('orgs').doc(orgId)\n const result: CrmNextActivityRecompute = { records: 0, missing: 0 }\n for (const { field, id } of mergeNextActivityLinks(links)) {\n const outcome = await recomputeOne(orgRef, field, id)\n if (outcome.written) result.records += 1\n else result.missing += 1\n }\n return result\n}\n\n/**\n * The same, from a task document rather than a link — what a writer holds\n * after it has read the task it is about to change or has just changed.\n */\nexport function crmNextActivityLinksOf(\n task: Record<string, unknown> | null | undefined,\n): CrmNextActivityLink {\n return nextActivityLinksOfTask(task as Parameters<typeof nextActivityLinksOfTask>[0])\n}\n\n/** The most open tasks one org-wide recompute walks. */\nexport const CRM_NEXT_ACTIVITY_SWEEP_MAX = 20_000\n\nexport interface CrmNextActivitySweep {\n /** Open tasks read. */\n tasks: number\n /** Records that now carry a time. */\n scheduled: number\n /** Records that carried a time and now carry `null`. */\n cleared: number\n /** Whether the org held more open tasks than the sweep reads. */\n truncated: boolean\n}\n\n/**\n * The one-off recompute for a whole organization — the Fields section's\n * \"Recompute next activity\" (AGL-2661), for the records written before the\n * field existed and for whatever a client-direct write left behind.\n *\n * Two passes and no per-record task query: every OPEN task once, grouped\n * by the records it names, so each record with something scheduled is\n * written its earliest due time; then every record in the three\n * collections that CARRIES a time and was not in that group, cleared to\n * `null`. A record that was never written is left alone — every reader\n * already treats absent as `null`, and writing a null onto every contact\n * an org holds would cost a write per person to say nothing.\n *\n * The clearing pass runs only when the first pass read EVERY open task.\n * \"No open task names this record\" is a statement about all of them, and\n * an organization holding more than the cap has open tasks the sweep never\n * saw — clearing on a partial read would blank records that are perfectly\n * scheduled. Past the cap the sweep is the writing half alone, and\n * `truncated` says so.\n */\nexport async function sweepCrmNextTaskAt(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n options: { max?: number } = {},\n): Promise<CrmNextActivitySweep> {\n const max = options.max ?? CRM_NEXT_ACTIVITY_SWEEP_MAX\n const orgRef = firestore.collection('orgs').doc(orgId)\n const open = await orgRef\n .collection(CRM_COLLECTIONS.tasks)\n .where('status', '==', 'open')\n .limit(max + 1)\n .get()\n const truncated = open.docs.length > max\n const docs = open.docs.slice(0, max)\n\n const earliest = new Map<CrmNextActivityLinkField, Map<string, number | null>>()\n for (const field of Object.keys(CRM_NEXT_ACTIVITY_COLLECTIONS) as CrmNextActivityLinkField[]) {\n earliest.set(field, new Map())\n }\n for (const doc of docs) {\n const task = { status: doc.get('status'), dueAtMs: doc.get('dueAtMs') }\n const links = nextActivityLinksOfTask({\n contactId: doc.get('contactId'),\n companyId: doc.get('companyId'),\n dealId: doc.get('dealId'),\n })\n for (const { field, id } of mergeNextActivityLinks([links])) {\n const byId = earliest.get(field) as Map<string, number | null>\n byId.set(id, nextTaskAtMsOf([task, { status: 'open', dueAtMs: byId.get(id) ?? null }]))\n }\n }\n\n const sweep: CrmNextActivitySweep = { tasks: docs.length, scheduled: 0, cleared: 0, truncated }\n for (const [field, byId] of earliest) {\n const records = orgRef.collection(CRM_NEXT_ACTIVITY_COLLECTIONS[field])\n for (const [id, value] of byId) {\n try {\n await records.doc(id).update({ [CRM_NEXT_ACTIVITY_FIELD]: value })\n sweep.scheduled += 1\n } catch (error) {\n if (!isNotFound(error)) throw error\n }\n }\n /*\n * The stale half: a record that says something is scheduled when no\n * open task names it any more. `> 0` is the one query a stored time\n * answers without an index of its own, and matches what\n * `readNextTaskAtMs` calls a usable value.\n */\n if (truncated) continue\n const carrying = await records.where(CRM_NEXT_ACTIVITY_FIELD, '>', 0).get()\n for (const doc of carrying.docs) {\n if (byId.has(doc.id)) continue\n if (readNextTaskAtMs({ nextTaskAtMs: doc.get(CRM_NEXT_ACTIVITY_FIELD) }) === null) continue\n await doc.ref.update({ [CRM_NEXT_ACTIVITY_FIELD]: null })\n sweep.cleared += 1\n }\n }\n return sweep\n}\n"],"names":["CRM_COLLECTIONS","CRM_NEXT_ACTIVITY_FIELD","mergeNextActivityLinks","nextActivityLinksOfTask","nextTaskAtMsOf","readNextTaskAtMs","CRM_NEXT_ACTIVITY_COLLECTIONS","contactId","companyId","companies","dealId","deals","isNotFound","error","code","message","includes","recomputeOne","orgRef","field","id","tasks","collection","where","get","value","docs","map","doc","status","dueAtMs","update","written","recomputeCrmNextTaskAt","firestore","orgId","links","result","records","missing","outcome","crmNextActivityLinksOf","task","CRM_NEXT_ACTIVITY_SWEEP_MAX","sweepCrmNextTaskAt","options","max","open","limit","truncated","length","slice","earliest","Map","Object","keys","set","byId","sweep","scheduled","cleared","carrying","has","nextTaskAtMs","ref"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginIndexedRecord, PluginRecordIndex } from '@aglyn/aglyn/plugin-manager/plugin-record-index';
18
+ /**
19
+ * Two more of the CRM's record kinds, as another plugin reads them on the
20
+ * server (AGL-3080) — a sales sequence judging the company a person works
21
+ * for, and sending the body of a template a rep wrote — without knowing where
22
+ * either is stored.
23
+ *
24
+ * Both org-scoped (`orgId` required; a site-only scope answers nothing). With
25
+ * a `hostId`, only what that site may see.
26
+ *
27
+ * `company` — `name` is the company's, else its domain, else its id. Its
28
+ * `facts` are the company as the CRM stores it: `name`, `domain`, `address`
29
+ * (`{ line1, city, region, postalCode, country }`), `phone`, `website`,
30
+ * `industry` and the rest of the record's fields.
31
+ *
32
+ * `messageTemplate` — the CRM's email templates and snippets, by the name a
33
+ * rep gave it; one saved without a name is left out. Its `facts`:
34
+ * `{ kind: 'template' | 'snippet', subject: string, body: string }`, the body
35
+ * in the CRM's merge-field grammar.
36
+ */
37
+ type Firestore = FirebaseFirestore.Firestore;
38
+ export declare function companyIndexedRecord(id: string, data: Record<string, unknown> | undefined): PluginIndexedRecord | null;
39
+ export declare function messageTemplateIndexedRecord(id: string, data: Record<string, unknown> | undefined): PluginIndexedRecord | null;
40
+ /** Builds the two indexes over a Firestore; the CRM registers them over the admin app's. */
41
+ export declare function crmRecordIndexes(firestore?: () => Firestore): {
42
+ company: PluginRecordIndex;
43
+ messageTemplate: PluginRecordIndex;
44
+ };
45
+ export {};
@@ -0,0 +1,78 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { CRM_COLLECTIONS } from "@aglyn/aglyn/app-utils/crm";
18
+ import { normalizeCrmEmailTemplate } from "@aglyn/aglyn/app-utils/crm-email-templates";
19
+ import { visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
20
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
21
+ const str = (value)=>typeof value === 'string' ? value.trim() : '';
22
+ export function companyIndexedRecord(id, data) {
23
+ if (!data) return null;
24
+ return {
25
+ id,
26
+ name: str(data['name']) || str(data['domain']) || id,
27
+ facts: _extends({}, data)
28
+ };
29
+ }
30
+ export function messageTemplateIndexedRecord(id, data) {
31
+ if (!data) return null;
32
+ const template = normalizeCrmEmailTemplate(data);
33
+ if (!template.name) return null;
34
+ return {
35
+ id,
36
+ name: template.name,
37
+ facts: {
38
+ kind: template.kind,
39
+ subject: template.subject,
40
+ body: template.body
41
+ }
42
+ };
43
+ }
44
+ function orgIndex(collection, record, firestore) {
45
+ const of = (orgId)=>firestore().collection('orgs').doc(orgId).collection(collection);
46
+ const visible = (data, hostId)=>!hostId || visibleToHost(data == null ? void 0 : data['visibleTo'], hostId);
47
+ return {
48
+ async list ({ orgId, hostId, limit }) {
49
+ if (!orgId || limit <= 0) return {
50
+ records: [],
51
+ truncated: false
52
+ };
53
+ // One more than asked, so `truncated` is a fact; scope and names are
54
+ // applied after the read.
55
+ const snapshot = await of(orgId).orderBy('name').limit(limit + 1).get();
56
+ const records = snapshot.docs.slice(0, limit).filter((doc)=>visible(doc.data(), hostId)).map((doc)=>record(doc.id, doc.data())).filter((entry)=>entry !== null);
57
+ return {
58
+ records,
59
+ truncated: snapshot.docs.length > limit
60
+ };
61
+ },
62
+ async get ({ orgId, hostId, id }) {
63
+ if (!orgId || !id) return null;
64
+ const snapshot = await of(orgId).doc(id).get();
65
+ if (!snapshot.exists || !visible(snapshot.data(), hostId)) return null;
66
+ return record(snapshot.id, snapshot.data());
67
+ }
68
+ };
69
+ }
70
+ const adminFirestore = ()=>firebaseAdmin.app().firestore();
71
+ /** Builds the two indexes over a Firestore; the CRM registers them over the admin app's. */ export function crmRecordIndexes(firestore = adminFirestore) {
72
+ return {
73
+ company: orgIndex(CRM_COLLECTIONS.companies, companyIndexedRecord, firestore),
74
+ messageTemplate: orgIndex(CRM_COLLECTIONS.emailTemplates, messageTemplateIndexedRecord, firestore)
75
+ };
76
+ }
77
+
78
+ //# sourceMappingURL=crm-record-indexes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/crm-record-indexes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CRM_COLLECTIONS } from '@aglyn/aglyn/app-utils/crm'\nimport { normalizeCrmEmailTemplate } from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport type {\n PluginIndexedRecord,\n PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\n\n/**\n * Two more of the CRM's record kinds, as another plugin reads them on the\n * server (AGL-3080) — a sales sequence judging the company a person works\n * for, and sending the body of a template a rep wrote — without knowing where\n * either is stored.\n *\n * Both org-scoped (`orgId` required; a site-only scope answers nothing). With\n * a `hostId`, only what that site may see.\n *\n * `company` — `name` is the company's, else its domain, else its id. Its\n * `facts` are the company as the CRM stores it: `name`, `domain`, `address`\n * (`{ line1, city, region, postalCode, country }`), `phone`, `website`,\n * `industry` and the rest of the record's fields.\n *\n * `messageTemplate` — the CRM's email templates and snippets, by the name a\n * rep gave it; one saved without a name is left out. Its `facts`:\n * `{ kind: 'template' | 'snippet', subject: string, body: string }`, the body\n * in the CRM's merge-field grammar.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nexport function companyIndexedRecord(\n id: string,\n data: Record<string, unknown> | undefined,\n): PluginIndexedRecord | null {\n if (!data) return null\n return { id, name: str(data['name']) || str(data['domain']) || id, facts: { ...data } }\n}\n\nexport function messageTemplateIndexedRecord(\n id: string,\n data: Record<string, unknown> | undefined,\n): PluginIndexedRecord | null {\n if (!data) return null\n const template = normalizeCrmEmailTemplate(data)\n if (!template.name) return null\n return {\n id,\n name: template.name,\n facts: { kind: template.kind, subject: template.subject, body: template.body },\n }\n}\n\nfunction orgIndex(\n collection: string,\n record: (id: string, data: Record<string, unknown> | undefined) => PluginIndexedRecord | null,\n firestore: () => Firestore,\n): PluginRecordIndex {\n const of = (orgId: string) => firestore().collection('orgs').doc(orgId).collection(collection)\n const visible = (data: Record<string, unknown> | undefined, hostId: string | null | undefined) =>\n !hostId || visibleToHost(data?.['visibleTo'] as string[] | undefined, hostId)\n return {\n async list({ orgId, hostId, limit }) {\n if (!orgId || limit <= 0) return { records: [], truncated: false }\n // One more than asked, so `truncated` is a fact; scope and names are\n // applied after the read.\n const snapshot = await of(orgId).orderBy('name').limit(limit + 1).get()\n const records = snapshot.docs\n .slice(0, limit)\n .filter((doc) => visible(doc.data(), hostId))\n .map((doc) => record(doc.id, doc.data()))\n .filter((entry): entry is PluginIndexedRecord => entry !== null)\n return { records, truncated: snapshot.docs.length > limit }\n },\n async get({ orgId, hostId, id }) {\n if (!orgId || !id) return null\n const snapshot = await of(orgId).doc(id).get()\n if (!snapshot.exists || !visible(snapshot.data(), hostId)) return null\n return record(snapshot.id, snapshot.data())\n },\n }\n}\n\nconst adminFirestore = () => firebaseAdmin.app().firestore()\n\n/** Builds the two indexes over a Firestore; the CRM registers them over the admin app's. */\nexport function crmRecordIndexes(firestore: () => Firestore = adminFirestore): {\n company: PluginRecordIndex\n messageTemplate: PluginRecordIndex\n} {\n return {\n company: orgIndex(CRM_COLLECTIONS.companies, companyIndexedRecord, firestore),\n messageTemplate: orgIndex(CRM_COLLECTIONS.emailTemplates, messageTemplateIndexedRecord, firestore),\n }\n}\n"],"names":["CRM_COLLECTIONS","normalizeCrmEmailTemplate","visibleToHost","firebaseAdmin","str","value","trim","companyIndexedRecord","id","data","name","facts","messageTemplateIndexedRecord","template","kind","subject","body","orgIndex","collection","record","firestore","of","orgId","doc","visible","hostId","list","limit","records","truncated","snapshot","orderBy","get","docs","slice","filter","map","entry","length","exists","adminFirestore","app","crmRecordIndexes","company","companies","messageTemplate","emailTemplates"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,eAAe,QAAQ,6BAA4B;AAC5D,SAASC,yBAAyB,QAAQ,6CAA4C;AACtF,SAASC,aAAa,QAAQ,sCAAqC;AAKnE,SAASC,aAAa,QAAQ,iDAAgD;AAwB9E,MAAMC,MAAM,CAACC,QAA4B,OAAOA,UAAU,WAAWA,MAAMC,IAAI,KAAK;AAEpF,OAAO,SAASC,qBACdC,EAAU,EACVC,IAAyC;IAEzC,IAAI,CAACA,MAAM,OAAO;IAClB,OAAO;QAAED;QAAIE,MAAMN,IAAIK,IAAI,CAAC,OAAO,KAAKL,IAAIK,IAAI,CAAC,SAAS,KAAKD;QAAIG,OAAO,aAAKF;IAAO;AACxF;AAEA,OAAO,SAASG,6BACdJ,EAAU,EACVC,IAAyC;IAEzC,IAAI,CAACA,MAAM,OAAO;IAClB,MAAMI,WAAWZ,0BAA0BQ;IAC3C,IAAI,CAACI,SAASH,IAAI,EAAE,OAAO;IAC3B,OAAO;QACLF;QACAE,MAAMG,SAASH,IAAI;QACnBC,OAAO;YAAEG,MAAMD,SAASC,IAAI;YAAEC,SAASF,SAASE,OAAO;YAAEC,MAAMH,SAASG,IAAI;QAAC;IAC/E;AACF;AAEA,SAASC,SACPC,UAAkB,EAClBC,MAA6F,EAC7FC,SAA0B;IAE1B,MAAMC,KAAK,CAACC,QAAkBF,YAAYF,UAAU,CAAC,QAAQK,GAAG,CAACD,OAAOJ,UAAU,CAACA;IACnF,MAAMM,UAAU,CAACf,MAA2CgB,SAC1D,CAACA,UAAUvB,cAAcO,wBAAAA,IAAM,CAAC,YAAY,EAA0BgB;IACxE,OAAO;QACL,MAAMC,MAAK,EAAEJ,KAAK,EAAEG,MAAM,EAAEE,KAAK,EAAE;YACjC,IAAI,CAACL,SAASK,SAAS,GAAG,OAAO;gBAAEC,SAAS,EAAE;gBAAEC,WAAW;YAAM;YACjE,qEAAqE;YACrE,0BAA0B;YAC1B,MAAMC,WAAW,MAAMT,GAAGC,OAAOS,OAAO,CAAC,QAAQJ,KAAK,CAACA,QAAQ,GAAGK,GAAG;YACrE,MAAMJ,UAAUE,SAASG,IAAI,CAC1BC,KAAK,CAAC,GAAGP,OACTQ,MAAM,CAAC,CAACZ,MAAQC,QAAQD,IAAId,IAAI,IAAIgB,SACpCW,GAAG,CAAC,CAACb,MAAQJ,OAAOI,IAAIf,EAAE,EAAEe,IAAId,IAAI,KACpC0B,MAAM,CAAC,CAACE,QAAwCA,UAAU;YAC7D,OAAO;gBAAET;gBAASC,WAAWC,SAASG,IAAI,CAACK,MAAM,GAAGX;YAAM;QAC5D;QACA,MAAMK,KAAI,EAAEV,KAAK,EAAEG,MAAM,EAAEjB,EAAE,EAAE;YAC7B,IAAI,CAACc,SAAS,CAACd,IAAI,OAAO;YAC1B,MAAMsB,WAAW,MAAMT,GAAGC,OAAOC,GAAG,CAACf,IAAIwB,GAAG;YAC5C,IAAI,CAACF,SAASS,MAAM,IAAI,CAACf,QAAQM,SAASrB,IAAI,IAAIgB,SAAS,OAAO;YAClE,OAAON,OAAOW,SAAStB,EAAE,EAAEsB,SAASrB,IAAI;QAC1C;IACF;AACF;AAEA,MAAM+B,iBAAiB,IAAMrC,cAAcsC,GAAG,GAAGrB,SAAS;AAE1D,0FAA0F,GAC1F,OAAO,SAASsB,iBAAiBtB,YAA6BoB,cAAc;IAI1E,OAAO;QACLG,SAAS1B,SAASjB,gBAAgB4C,SAAS,EAAErC,sBAAsBa;QACnEyB,iBAAiB5B,SAASjB,gBAAgB8C,cAAc,EAAElC,8BAA8BQ;IAC1F;AACF"}
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import type { PluginApiHandler } from '@aglyn/aglyn/server';
18
- import { type CrmShareGrant, type CrmSharingObject, type CrmSharingRule, type CrmSharingSite } from '../model/crm-sharing';
18
+ import { type CrmShareGrant, type CrmSharingContext, type CrmSharingObject, type CrmSharingRule, type CrmSharingSite } from '../model/crm-sharing';
19
19
  type Firestore = FirebaseFirestore.Firestore;
20
20
  /** Test seam, and what a rule write calls so its own process reads it fresh. */
21
21
  export declare function forgetCrmSharingRules(orgId?: string): void;
@@ -26,6 +26,12 @@ export declare function crmSharingRulesOf(firestore: Firestore, orgId: string, o
26
26
  rules: CrmSharingRule[];
27
27
  fresh: boolean;
28
28
  }>;
29
+ /**
30
+ * What the rules for `object` are evaluated against beyond the record: the
31
+ * org's Lead source list, read only when an active rule asks a lead source
32
+ * direction (AGL-3511) — an org whose rules never do pays no read for it.
33
+ */
34
+ export declare function crmSharingContextFor(firestore: Firestore, orgId: string, rules: readonly CrmSharingRule[], object: CrmSharingObject): Promise<CrmSharingContext>;
29
35
  export interface RecomputeRecordSharingInput {
30
36
  firestore: Firestore;
31
37
  orgId: string;
@@ -37,6 +43,8 @@ export interface RecomputeRecordSharingInput {
37
43
  fresh: boolean;
38
44
  /** The hand shares' change, for a share or an unshare. */
39
45
  manual?: (grants: Record<string, CrmShareGrant>) => Record<string, CrmShareGrant>;
46
+ /** What the rules are evaluated against beyond the record — see {@link crmSharingContextFor}. */
47
+ context?: CrmSharingContext;
40
48
  atMs: number;
41
49
  }
42
50
  export type RecomputeOutcome = 'changed' | 'unchanged' | 'missing';
@@ -39,7 +39,8 @@ import { firebaseAdmin, getOrgForHost, logOrgActivity } from "@aglyn/tenant-data
39
39
  import { countCrmActivitiesForRecord } from "@aglyn/tenant-data-admin/server/crm-records";
40
40
  import { FieldPath, FieldValue } from "firebase-admin/firestore";
41
41
  import { BUNDLE_ID } from "../constants/bundle-common.js";
42
- import { activeSharingRules, CRM_SHARING_CRITERIA_FOR, CRM_SHARING_CRITERIA_KEYS, CRM_SHARING_FIELD, CRM_SHARING_IDS_MAX, CRM_SHARING_OBJECT_LABELS, CRM_SHARING_RULE_IDS_PATH, CRM_SHARING_RULES_MAX, CRM_WRITE_SCOPE_FIELD, describeShareTargets, evaluateRecordGrants, isCrmShareAccess, isCrmSharingObject, planRecordSharing, readCrmSharingRule, readCrmSharingRules, recordHeldScope, sharingPlanChanges, shareTargetsFrom, withManualShares, withoutManualShares } from "../model/crm-sharing.js";
42
+ import { activeSharingRules, CRM_SHARING_CRITERIA_FOR, CRM_SHARING_CRITERIA_KEYS, CRM_SHARING_FIELD, CRM_SHARING_IDS_MAX, CRM_SHARING_OBJECT_LABELS, CRM_SHARING_RULE_IDS_PATH, CRM_SHARING_RULES_MAX, CRM_WRITE_SCOPE_FIELD, describeShareTargets, evaluateRecordGrants, isCrmShareAccess, isCrmSharingObject, planRecordSharing, readCrmSharingRule, readCrmSharingRules, recordHeldScope, sharingPlanChanges, shareTargetsFrom, sharingRulesAskLeadSourceDirection, withManualShares, withoutManualShares } from "../model/crm-sharing.js";
43
+ import { readLeadSourcePicklist } from "./lead-source-picklist.js";
43
44
  import { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from "./org-caller.js";
44
45
  import { crmSuiteRefusal } from "./suite-gate.js";
45
46
  import { authorizeCrmWriter } from "./task-routes.js";
@@ -76,11 +77,22 @@ export async function crmSharingRulesOf(firestore, orgId, options = {}) {
76
77
  fresh: true
77
78
  };
78
79
  }
80
+ /**
81
+ * What the rules for `object` are evaluated against beyond the record: the
82
+ * org's Lead source list, read only when an active rule asks a lead source
83
+ * direction (AGL-3511) — an org whose rules never do pays no read for it.
84
+ */ export async function crmSharingContextFor(firestore, orgId, rules, object) {
85
+ if (!sharingRulesAskLeadSourceDirection(rules, object)) return {};
86
+ return {
87
+ leadSources: await readLeadSourcePicklist(firestore, orgId)
88
+ };
89
+ }
79
90
  export async function recomputeRecordSharing(input) {
80
91
  const { firestore, orgId, object, id, atMs } = input;
81
92
  const ref = firestore.collection('orgs').doc(orgId).collection(object).doc(id);
82
93
  return await firestore.runTransaction(async (tx)=>{
83
94
  var _snapshot_data, _plan_writeTo, _plan_sharing, _plan_writeTo1, _plan_sharing1;
95
+ var _input_context;
84
96
  const snapshot = await tx.get(ref);
85
97
  if (!snapshot.exists) return 'missing';
86
98
  const record = (_snapshot_data = snapshot.data()) != null ? _snapshot_data : {};
@@ -100,7 +112,9 @@ export async function recomputeRecordSharing(input) {
100
112
  rules = readCrmSharingRules((_org_data = org.data()) != null ? _org_data : null);
101
113
  }
102
114
  }
103
- let grants = evaluateRecordGrants(object, record, rules, atMs);
115
+ // A rule read afresh above may ask a direction the caller's context was not read for.
116
+ const context = ((_input_context = input.context) == null ? void 0 : _input_context.leadSources) || rules === input.rules ? input.context : await crmSharingContextFor(firestore, orgId, rules, object);
117
+ let grants = evaluateRecordGrants(object, record, rules, atMs, context);
104
118
  if (input.manual) grants = input.manual(grants);
105
119
  const plan = planRecordSharing(record, grants);
106
120
  if (!sharingPlanChanges(record, plan)) return 'unchanged';
@@ -151,12 +165,14 @@ export async function recomputeRecordSharing(input) {
151
165
  changed: 0,
152
166
  missing: 0
153
167
  };
168
+ const context = await crmSharingContextFor(firestore, orgId, rules, object);
154
169
  const outcome = await recomputeMany({
155
170
  firestore,
156
171
  orgId,
157
172
  object,
158
173
  rules,
159
174
  fresh,
175
+ context,
160
176
  atMs: Date.now()
161
177
  }, ids);
162
178
  return {
@@ -404,6 +420,7 @@ const ORG_TARGET = {
404
420
  const { rules } = await crmSharingRulesOf(firestore, orgId, {
405
421
  fresh: true
406
422
  });
423
+ const context = await crmSharingContextFor(firestore, orgId, rules, rule.object);
407
424
  let page;
408
425
  if (run.mode === 'apply') {
409
426
  let query = records.orderBy(FieldPath.documentId()).limit(RUN_PAGE);
@@ -417,7 +434,7 @@ const ORG_TARGET = {
417
434
  const moving = page.docs.filter((doc)=>{
418
435
  var _doc_data;
419
436
  const record = (_doc_data = doc.data()) != null ? _doc_data : {};
420
- const plan = planRecordSharing(record, evaluateRecordGrants(rule.object, record, rules, atMs));
437
+ const plan = planRecordSharing(record, evaluateRecordGrants(rule.object, record, rules, atMs, context));
421
438
  return sharingPlanChanges(record, plan);
422
439
  }).map((doc)=>doc.id);
423
440
  if (run.mode === 'remove' && page.docs.length && page.docs.every((doc)=>seen.has(doc.id))) {
@@ -430,6 +447,7 @@ const ORG_TARGET = {
430
447
  object: rule.object,
431
448
  rules,
432
449
  fresh: true,
450
+ context,
433
451
  atMs
434
452
  }, moving);
435
453
  const done = run.mode === 'apply' ? page.size < RUN_PAGE : page.empty;
@@ -593,12 +611,14 @@ function readIds(raw) {
593
611
  }
594
612
  const access = body['access'] === 'edit' ? 'edit' : 'read';
595
613
  const { rules, fresh } = await crmSharingRulesOf(firestore, orgId);
614
+ const context = await crmSharingContextFor(firestore, orgId, rules, object);
596
615
  const outcome = await recomputeMany({
597
616
  firestore,
598
617
  orgId,
599
618
  object,
600
619
  rules,
601
620
  fresh,
621
+ context,
602
622
  atMs: nowMs,
603
623
  manual: (grants)=>action === 'share' ? withManualShares(grants, targets, access, {
604
624
  uid: caller.uid,