@aglyn/plugins-crm 1.0.0-beta.220 → 1.0.0-beta.222

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 (514) 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-record-picker.d.ts +15 -0
  34. package/src/lib/components/crm-record-picker.js +16 -10
  35. package/src/lib/components/crm-record-picker.js.map +1 -1
  36. package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
  37. package/src/lib/components/crm-transfer-buttons.js +167 -0
  38. package/src/lib/components/crm-transfer-buttons.js.map +1 -0
  39. package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
  40. package/src/lib/components/deal-contact-roles-card.js +368 -0
  41. package/src/lib/components/deal-contact-roles-card.js.map +1 -0
  42. package/src/lib/components/deal-detail-page.d.ts +3 -2
  43. package/src/lib/components/deal-detail-page.js +25 -6
  44. package/src/lib/components/deal-detail-page.js.map +1 -1
  45. package/src/lib/components/deal-edit-drawer.d.ts +10 -0
  46. package/src/lib/components/deal-edit-drawer.js +219 -10
  47. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  48. package/src/lib/components/deal-properties-card.d.ts +7 -0
  49. package/src/lib/components/deal-properties-card.js +34 -3
  50. package/src/lib/components/deal-properties-card.js.map +1 -1
  51. package/src/lib/components/deal-stage-card.d.ts +4 -0
  52. package/src/lib/components/deal-stage-card.js +10 -2
  53. package/src/lib/components/deal-stage-card.js.map +1 -1
  54. package/src/lib/components/deals-bulk-bar.d.ts +0 -3
  55. package/src/lib/components/deals-bulk-bar.js +7 -20
  56. package/src/lib/components/deals-bulk-bar.js.map +1 -1
  57. package/src/lib/components/deals-section.d.ts +3 -3
  58. package/src/lib/components/deals-section.js +123 -63
  59. package/src/lib/components/deals-section.js.map +1 -1
  60. package/src/lib/components/fields-section.js +31 -17
  61. package/src/lib/components/fields-section.js.map +1 -1
  62. package/src/lib/components/lead-convert-dialog.d.ts +4 -0
  63. package/src/lib/components/lead-convert-dialog.js +23 -1
  64. package/src/lib/components/lead-convert-dialog.js.map +1 -1
  65. package/src/lib/components/lead-detail-page.js +8 -2
  66. package/src/lib/components/lead-detail-page.js.map +1 -1
  67. package/src/lib/components/lead-profile-fields.d.ts +71 -0
  68. package/src/lib/components/lead-profile-fields.js +386 -0
  69. package/src/lib/components/lead-profile-fields.js.map +1 -0
  70. package/src/lib/components/lead-properties-card.d.ts +2 -1
  71. package/src/lib/components/lead-properties-card.js +85 -176
  72. package/src/lib/components/lead-properties-card.js.map +1 -1
  73. package/src/lib/components/lead-source-select.d.ts +4 -23
  74. package/src/lib/components/lead-source-select.js +9 -73
  75. package/src/lib/components/lead-source-select.js.map +1 -1
  76. package/src/lib/components/lead-status-chip.d.ts +9 -3
  77. package/src/lib/components/lead-status-chip.js +8 -4
  78. package/src/lib/components/lead-status-chip.js.map +1 -1
  79. package/src/lib/components/lead-status-options.d.ts +20 -0
  80. package/src/lib/components/lead-status-options.js +51 -0
  81. package/src/lib/components/lead-status-options.js.map +1 -0
  82. package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
  83. package/src/lib/components/lead-unqualify-dialog.js +50 -18
  84. package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
  85. package/src/lib/components/leads-bulk-bar.d.ts +0 -3
  86. package/src/lib/components/leads-bulk-bar.js +29 -29
  87. package/src/lib/components/leads-bulk-bar.js.map +1 -1
  88. package/src/lib/components/leads-section.js +120 -68
  89. package/src/lib/components/leads-section.js.map +1 -1
  90. package/src/lib/components/linked-deals-card.d.ts +4 -0
  91. package/src/lib/components/linked-deals-card.js +47 -3
  92. package/src/lib/components/linked-deals-card.js.map +1 -1
  93. package/src/lib/components/log-activity-dialog.js +43 -3
  94. package/src/lib/components/log-activity-dialog.js.map +1 -1
  95. package/src/lib/components/new-contact-drawer.d.ts +8 -0
  96. package/src/lib/components/new-contact-drawer.js +108 -6
  97. package/src/lib/components/new-contact-drawer.js.map +1 -1
  98. package/src/lib/components/new-lead-drawer.d.ts +9 -2
  99. package/src/lib/components/new-lead-drawer.js +88 -121
  100. package/src/lib/components/new-lead-drawer.js.map +1 -1
  101. package/src/lib/components/picklist-select.d.ts +36 -0
  102. package/src/lib/components/picklist-select.js +114 -0
  103. package/src/lib/components/picklist-select.js.map +1 -0
  104. package/src/lib/components/picklist-values-card.d.ts +34 -0
  105. package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
  106. package/src/lib/components/picklist-values-card.js.map +1 -0
  107. package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
  108. package/src/lib/components/pipeline-stages-dialog.js +26 -4
  109. package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
  110. package/src/lib/components/pipelines-dialog.js +15 -6
  111. package/src/lib/components/pipelines-dialog.js.map +1 -1
  112. package/src/lib/components/recent-activity-feed.js +20 -5
  113. package/src/lib/components/recent-activity-feed.js.map +1 -1
  114. package/src/lib/components/recipes-card.js +4 -2
  115. package/src/lib/components/recipes-card.js.map +1 -1
  116. package/src/lib/components/record-tasks-card.js +15 -8
  117. package/src/lib/components/record-tasks-card.js.map +1 -1
  118. package/src/lib/components/reports/forecast-card.d.ts +6 -0
  119. package/src/lib/components/reports/forecast-card.js +101 -3
  120. package/src/lib/components/reports/forecast-card.js.map +1 -1
  121. package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
  122. package/src/lib/components/reports/lead-funnel-card.js +9 -5
  123. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  124. package/src/lib/components/reports/lead-source-card.d.ts +23 -0
  125. package/src/lib/components/reports/lead-source-card.js +318 -0
  126. package/src/lib/components/reports/lead-source-card.js.map +1 -0
  127. package/src/lib/components/reports/report-export.js +1 -1
  128. package/src/lib/components/reports/report-export.js.map +1 -1
  129. package/src/lib/components/reports-section.js +8 -0
  130. package/src/lib/components/reports-section.js.map +1 -1
  131. package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
  132. package/src/lib/components/sharing-rule-drawer.js +108 -19
  133. package/src/lib/components/sharing-rule-drawer.js.map +1 -1
  134. package/src/lib/components/sharing-rules-card.js +11 -3
  135. package/src/lib/components/sharing-rules-card.js.map +1 -1
  136. package/src/lib/components/task-cells.d.ts +9 -1
  137. package/src/lib/components/task-cells.js +9 -4
  138. package/src/lib/components/task-cells.js.map +1 -1
  139. package/src/lib/components/task-edit-drawer.js +111 -43
  140. package/src/lib/components/task-edit-drawer.js.map +1 -1
  141. package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
  142. package/src/lib/components/tasks-bulk-bar.js +7 -20
  143. package/src/lib/components/tasks-bulk-bar.js.map +1 -1
  144. package/src/lib/components/tasks-calendar.d.ts +3 -0
  145. package/src/lib/components/tasks-calendar.js +8 -1
  146. package/src/lib/components/tasks-calendar.js.map +1 -1
  147. package/src/lib/components/tasks-section.d.ts +3 -3
  148. package/src/lib/components/tasks-section.js +58 -43
  149. package/src/lib/components/tasks-section.js.map +1 -1
  150. package/src/lib/constants/api-routes.d.ts +2 -9
  151. package/src/lib/constants/api-routes.js +1 -7
  152. package/src/lib/constants/api-routes.js.map +1 -1
  153. package/src/lib/constants/bundle-common.d.ts +6 -0
  154. package/src/lib/constants/bundle-common.js +11 -0
  155. package/src/lib/constants/bundle-common.js.map +1 -1
  156. package/src/lib/constants/company-filters.d.ts +14 -0
  157. package/src/lib/constants/company-filters.js +51 -1
  158. package/src/lib/constants/company-filters.js.map +1 -1
  159. package/src/lib/constants/contact-filters.d.ts +2 -0
  160. package/src/lib/constants/contact-filters.js +18 -1
  161. package/src/lib/constants/contact-filters.js.map +1 -1
  162. package/src/lib/constants/deal-filters.d.ts +22 -1
  163. package/src/lib/constants/deal-filters.js +67 -4
  164. package/src/lib/constants/deal-filters.js.map +1 -1
  165. package/src/lib/declarations.console-server.d.ts +3 -0
  166. package/src/lib/declarations.console-server.js +35 -0
  167. package/src/lib/declarations.console-server.js.map +1 -1
  168. package/src/lib/declarations.d.ts +26 -0
  169. package/src/lib/declarations.js +35 -0
  170. package/src/lib/declarations.js.map +1 -0
  171. package/src/lib/declarations.server.d.ts +11 -0
  172. package/src/lib/declarations.server.js +92 -1
  173. package/src/lib/declarations.server.js.map +1 -1
  174. package/src/lib/hooks/use-company-picklists.d.ts +15 -0
  175. package/src/lib/hooks/use-company-picklists.js +55 -0
  176. package/src/lib/hooks/use-company-picklists.js.map +1 -0
  177. package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
  178. package/src/lib/hooks/use-crm-picklist.js +53 -0
  179. package/src/lib/hooks/use-crm-picklist.js.map +1 -0
  180. package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
  181. package/src/lib/hooks/use-crm-record-names.js +45 -19
  182. package/src/lib/hooks/use-crm-record-names.js.map +1 -1
  183. package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
  184. package/src/lib/hooks/use-crm-task-picklists.js +45 -0
  185. package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
  186. package/src/lib/hooks/use-deals.d.ts +14 -0
  187. package/src/lib/hooks/use-deals.js +29 -0
  188. package/src/lib/hooks/use-deals.js.map +1 -1
  189. package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
  190. package/src/lib/hooks/use-lead-picklists.js +46 -0
  191. package/src/lib/hooks/use-lead-picklists.js.map +1 -0
  192. package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
  193. package/src/lib/hooks/use-lead-source-picklist.js +12 -22
  194. package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
  195. package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
  196. package/src/lib/hooks/use-lead-status-picklist.js +40 -0
  197. package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
  198. package/src/lib/model/companies.d.ts +32 -3
  199. package/src/lib/model/companies.js +133 -16
  200. package/src/lib/model/companies.js.map +1 -1
  201. package/src/lib/model/contact-merge.d.ts +27 -3
  202. package/src/lib/model/contact-merge.js +81 -7
  203. package/src/lib/model/contact-merge.js.map +1 -1
  204. package/src/lib/model/contact-record.d.ts +17 -0
  205. package/src/lib/model/contact-record.js +15 -1
  206. package/src/lib/model/contact-record.js.map +1 -1
  207. package/src/lib/model/contact-update.d.ts +31 -2
  208. package/src/lib/model/contact-update.js +14 -0
  209. package/src/lib/model/contact-update.js.map +1 -1
  210. package/src/lib/model/crm-list-query.d.ts +5 -0
  211. package/src/lib/model/crm-list-query.js +8 -0
  212. package/src/lib/model/crm-list-query.js.map +1 -1
  213. package/src/lib/model/crm-recipes.d.ts +40 -0
  214. package/src/lib/model/crm-recipes.js +244 -0
  215. package/src/lib/model/crm-recipes.js.map +1 -0
  216. package/src/lib/model/crm-record-counts.d.ts +36 -0
  217. package/src/lib/model/crm-record-counts.js +54 -0
  218. package/src/lib/model/crm-record-counts.js.map +1 -0
  219. package/src/lib/model/crm-record-lists.d.ts +23 -0
  220. package/src/lib/model/crm-record-lists.js +161 -0
  221. package/src/lib/model/crm-record-lists.js.map +1 -0
  222. package/src/lib/model/crm-reports.d.ts +73 -3
  223. package/src/lib/model/crm-reports.js +120 -1
  224. package/src/lib/model/crm-reports.js.map +1 -1
  225. package/src/lib/model/crm-sharing.d.ts +21 -3
  226. package/src/lib/model/crm-sharing.js +27 -6
  227. package/src/lib/model/crm-sharing.js.map +1 -1
  228. package/src/lib/model/deal-board-model.d.ts +8 -2
  229. package/src/lib/model/deal-board-model.js +15 -2
  230. package/src/lib/model/deal-board-model.js.map +1 -1
  231. package/src/lib/model/deal-contact-roles.d.ts +45 -0
  232. package/src/lib/model/deal-contact-roles.js +73 -0
  233. package/src/lib/model/deal-contact-roles.js.map +1 -0
  234. package/src/lib/model/deal-form-model.d.ts +56 -0
  235. package/src/lib/model/deal-form-model.js +79 -7
  236. package/src/lib/model/deal-form-model.js.map +1 -1
  237. package/src/lib/model/download-text-file.d.ts +23 -0
  238. package/src/lib/model/download-text-file.js +34 -0
  239. package/src/lib/model/download-text-file.js.map +1 -0
  240. package/src/lib/model/lead-filters.d.ts +36 -2
  241. package/src/lib/model/lead-filters.js +123 -2
  242. package/src/lib/model/lead-filters.js.map +1 -1
  243. package/src/lib/model/lead-source-direction.d.ts +38 -0
  244. package/src/lib/model/lead-source-direction.js +52 -0
  245. package/src/lib/model/lead-source-direction.js.map +1 -0
  246. package/src/lib/model/picklist-values.d.ts +119 -0
  247. package/src/lib/model/picklist-values.js +256 -0
  248. package/src/lib/model/picklist-values.js.map +1 -0
  249. package/src/lib/model/record-facts-disclosed.d.ts +81 -0
  250. package/src/lib/model/record-facts-disclosed.js +281 -0
  251. package/src/lib/model/record-facts-disclosed.js.map +1 -0
  252. package/src/lib/model/record-facts.d.ts +215 -34
  253. package/src/lib/model/record-facts.js +307 -128
  254. package/src/lib/model/record-facts.js.map +1 -1
  255. package/src/lib/model/task-picklists.d.ts +52 -0
  256. package/src/lib/model/task-picklists.js +83 -0
  257. package/src/lib/model/task-picklists.js.map +1 -0
  258. package/src/lib/model/task-routes.d.ts +13 -0
  259. package/src/lib/model/task-routes.js +23 -4
  260. package/src/lib/model/task-routes.js.map +1 -1
  261. package/src/lib/plan-entitlements.js +6 -4
  262. package/src/lib/plan-entitlements.js.map +1 -1
  263. package/src/lib/plugin.js +17 -0
  264. package/src/lib/plugin.js.map +1 -1
  265. package/src/lib/server/api-v1/contacts.js +220 -14
  266. package/src/lib/server/api-v1/contacts.js.map +1 -1
  267. package/src/lib/server/api-v1/crm-activities.js +14 -1
  268. package/src/lib/server/api-v1/crm-activities.js.map +1 -1
  269. package/src/lib/server/api-v1/crm-companies.js +123 -14
  270. package/src/lib/server/api-v1/crm-companies.js.map +1 -1
  271. package/src/lib/server/api-v1/crm-deals.js +291 -26
  272. package/src/lib/server/api-v1/crm-deals.js.map +1 -1
  273. package/src/lib/server/api-v1/crm-leads.js +144 -38
  274. package/src/lib/server/api-v1/crm-leads.js.map +1 -1
  275. package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
  276. package/src/lib/server/api-v1/crm-pipelines.js +8 -2
  277. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
  278. package/src/lib/server/api-v1/crm-tasks.js +75 -33
  279. package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
  280. package/src/lib/server/api-v1/openapi.d.ts +0 -11
  281. package/src/lib/server/api-v1/openapi.js +171 -16
  282. package/src/lib/server/api-v1/openapi.js.map +1 -1
  283. package/src/lib/server/assign-contact-owner.d.ts +162 -0
  284. package/src/lib/server/assign-contact-owner.js +340 -0
  285. package/src/lib/server/assign-contact-owner.js.map +1 -0
  286. package/src/lib/server/associate-company-by-domain.d.ts +70 -0
  287. package/src/lib/server/associate-company-by-domain.js +153 -0
  288. package/src/lib/server/associate-company-by-domain.js.map +1 -0
  289. package/src/lib/server/automation-steps.d.ts +139 -0
  290. package/src/lib/server/automation-steps.js +472 -0
  291. package/src/lib/server/automation-steps.js.map +1 -0
  292. package/src/lib/server/capture-contact.js +69 -22
  293. package/src/lib/server/capture-contact.js.map +1 -1
  294. package/src/lib/server/capture-host-contact.d.ts +104 -0
  295. package/src/lib/server/capture-host-contact.js +151 -0
  296. package/src/lib/server/capture-host-contact.js.map +1 -0
  297. package/src/lib/server/company-children.d.ts +25 -0
  298. package/src/lib/server/company-children.js +42 -0
  299. package/src/lib/server/company-children.js.map +1 -0
  300. package/src/lib/server/company-delete.d.ts +18 -0
  301. package/src/lib/server/company-delete.js +44 -21
  302. package/src/lib/server/company-delete.js.map +1 -1
  303. package/src/lib/server/contact-email-history.js +21 -19
  304. package/src/lib/server/contact-email-history.js.map +1 -1
  305. package/src/lib/server/contact-remove.js +27 -2
  306. package/src/lib/server/contact-remove.js.map +1 -1
  307. package/src/lib/server/contact-reports-to.d.ts +61 -0
  308. package/src/lib/server/contact-reports-to.js +114 -0
  309. package/src/lib/server/contact-reports-to.js.map +1 -0
  310. package/src/lib/server/contact-update.d.ts +42 -1
  311. package/src/lib/server/contact-update.js +204 -23
  312. package/src/lib/server/contact-update.js.map +1 -1
  313. package/src/lib/server/convert-host-lead.d.ts +46 -1
  314. package/src/lib/server/convert-host-lead.js +173 -15
  315. package/src/lib/server/convert-host-lead.js.map +1 -1
  316. package/src/lib/server/convert-open-lead.js +12 -5
  317. package/src/lib/server/convert-open-lead.js.map +1 -1
  318. package/src/lib/server/crm-record-indexes.d.ts +45 -0
  319. package/src/lib/server/crm-record-indexes.js +78 -0
  320. package/src/lib/server/crm-record-indexes.js.map +1 -0
  321. package/src/lib/server/crm-sharing.d.ts +9 -1
  322. package/src/lib/server/crm-sharing.js +23 -3
  323. package/src/lib/server/crm-sharing.js.map +1 -1
  324. package/src/lib/server/deal-contact-roles.d.ts +27 -0
  325. package/src/lib/server/deal-contact-roles.js +89 -0
  326. package/src/lib/server/deal-contact-roles.js.map +1 -0
  327. package/src/lib/server/lead-convert.d.ts +5 -0
  328. package/src/lib/server/lead-convert.js +19 -3
  329. package/src/lib/server/lead-convert.js.map +1 -1
  330. package/src/lib/server/lead-create.d.ts +17 -2
  331. package/src/lib/server/lead-create.js +46 -14
  332. package/src/lib/server/lead-create.js.map +1 -1
  333. package/src/lib/server/lead-nurturing.d.ts +6 -0
  334. package/src/lib/server/lead-nurturing.js +57 -20
  335. package/src/lib/server/lead-nurturing.js.map +1 -1
  336. package/src/lib/server/lead-picklists.d.ts +35 -0
  337. package/src/lib/server/lead-picklists.js +52 -0
  338. package/src/lib/server/lead-picklists.js.map +1 -0
  339. package/src/lib/server/lead-source-picklist.d.ts +1 -13
  340. package/src/lib/server/lead-source-picklist.js +6 -33
  341. package/src/lib/server/lead-source-picklist.js.map +1 -1
  342. package/src/lib/server/merge-contacts.js +22 -4
  343. package/src/lib/server/merge-contacts.js.map +1 -1
  344. package/src/lib/server/person-eraser.d.ts +53 -0
  345. package/src/lib/server/person-eraser.js +149 -0
  346. package/src/lib/server/person-eraser.js.map +1 -0
  347. package/src/lib/server/person-records.d.ts +69 -0
  348. package/src/lib/server/person-records.js +317 -0
  349. package/src/lib/server/person-records.js.map +1 -0
  350. package/src/lib/server/person-refund.d.ts +97 -0
  351. package/src/lib/server/person-refund.js +221 -0
  352. package/src/lib/server/person-refund.js.map +1 -0
  353. package/src/lib/server/picklist-values.d.ts +21 -0
  354. package/src/lib/server/picklist-values.js +308 -0
  355. package/src/lib/server/picklist-values.js.map +1 -0
  356. package/src/lib/server/read-picklist.d.ts +44 -0
  357. package/src/lib/server/read-picklist.js +65 -0
  358. package/src/lib/server/read-picklist.js.map +1 -0
  359. package/src/lib/server/recipe-routes.js +15 -7
  360. package/src/lib/server/recipe-routes.js.map +1 -1
  361. package/src/lib/server/record-email-state.js +7 -1
  362. package/src/lib/server/record-email-state.js.map +1 -1
  363. package/src/lib/server/record-facts.d.ts +3 -0
  364. package/src/lib/server/record-facts.js +233 -31
  365. package/src/lib/server/record-facts.js.map +1 -1
  366. package/src/lib/server/record-origin.d.ts +27 -0
  367. package/src/lib/server/record-origin.js +104 -0
  368. package/src/lib/server/record-origin.js.map +1 -0
  369. package/src/lib/server/record-timeline.js +15 -2
  370. package/src/lib/server/record-timeline.js.map +1 -1
  371. package/src/lib/server/suite-gate.js +4 -3
  372. package/src/lib/server/suite-gate.js.map +1 -1
  373. package/src/lib/server/task-routes.js +30 -16
  374. package/src/lib/server/task-routes.js.map +1 -1
  375. package/src/lib/server-deal-stage.d.ts +5 -1
  376. package/src/lib/server-deal-stage.js +10 -5
  377. package/src/lib/server-deal-stage.js.map +1 -1
  378. package/src/lib/server.d.ts +8 -3
  379. package/src/lib/server.js +47 -43
  380. package/src/lib/server.js.map +1 -1
  381. package/src/lib/testing/memory-firestore.d.ts +55 -0
  382. package/src/lib/testing/memory-firestore.js +335 -0
  383. package/src/lib/testing/memory-firestore.js.map +1 -0
  384. package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
  385. package/src/lib/testing/stand-in-email-send-index.js +49 -0
  386. package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
  387. package/src/lib/transfer/activities.d.ts +30 -0
  388. package/src/lib/transfer/activities.js +132 -0
  389. package/src/lib/transfer/activities.js.map +1 -0
  390. package/src/lib/transfer/aliases.d.ts +22 -0
  391. package/src/lib/transfer/aliases.js +1342 -0
  392. package/src/lib/transfer/aliases.js.map +1 -0
  393. package/src/lib/transfer/common.d.ts +222 -0
  394. package/src/lib/transfer/common.js +832 -0
  395. package/src/lib/transfer/common.js.map +1 -0
  396. package/src/lib/transfer/companies.d.ts +43 -0
  397. package/src/lib/transfer/companies.js +549 -0
  398. package/src/lib/transfer/companies.js.map +1 -0
  399. package/src/lib/transfer/contacts.d.ts +31 -0
  400. package/src/lib/transfer/contacts.js +727 -0
  401. package/src/lib/transfer/contacts.js.map +1 -0
  402. package/src/lib/transfer/deals.d.ts +66 -0
  403. package/src/lib/transfer/deals.js +698 -0
  404. package/src/lib/transfer/deals.js.map +1 -0
  405. package/src/lib/transfer/email-templates-package.d.ts +40 -0
  406. package/src/lib/transfer/email-templates-package.js +76 -0
  407. package/src/lib/transfer/email-templates-package.js.map +1 -0
  408. package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
  409. package/src/lib/transfer/email-templates-package.server.js +176 -0
  410. package/src/lib/transfer/email-templates-package.server.js.map +1 -0
  411. package/src/lib/transfer/fields.d.ts +102 -0
  412. package/src/lib/transfer/fields.js +1755 -0
  413. package/src/lib/transfer/fields.js.map +1 -0
  414. package/src/lib/transfer/leads.d.ts +30 -0
  415. package/src/lib/transfer/leads.js +582 -0
  416. package/src/lib/transfer/leads.js.map +1 -0
  417. package/src/lib/transfer/pipelines.d.ts +21 -0
  418. package/src/lib/transfer/pipelines.js +172 -0
  419. package/src/lib/transfer/pipelines.js.map +1 -0
  420. package/src/lib/transfer/register.d.ts +47 -0
  421. package/src/lib/transfer/register.js +277 -0
  422. package/src/lib/transfer/register.js.map +1 -0
  423. package/src/lib/transfer/tasks.d.ts +30 -0
  424. package/src/lib/transfer/tasks.js +418 -0
  425. package/src/lib/transfer/tasks.js.map +1 -0
  426. package/src/lib/transfer/ui.d.ts +20 -0
  427. package/src/lib/transfer/ui.js +42 -0
  428. package/src/lib/transfer/ui.js.map +1 -0
  429. package/src/lib/components/company-import-drawer.d.ts +0 -42
  430. package/src/lib/components/company-import-drawer.js +0 -99
  431. package/src/lib/components/company-import-drawer.js.map +0 -1
  432. package/src/lib/components/contact-import-drawer.d.ts +0 -46
  433. package/src/lib/components/contact-import-drawer.js +0 -149
  434. package/src/lib/components/contact-import-drawer.js.map +0 -1
  435. package/src/lib/components/crm-export-all-button.d.ts +0 -39
  436. package/src/lib/components/crm-export-all-button.js +0 -98
  437. package/src/lib/components/crm-export-all-button.js.map +0 -1
  438. package/src/lib/components/csv-import-drawer.d.ts +0 -139
  439. package/src/lib/components/csv-import-drawer.js +0 -757
  440. package/src/lib/components/csv-import-drawer.js.map +0 -1
  441. package/src/lib/components/deal-import-drawer.d.ts +0 -24
  442. package/src/lib/components/deal-import-drawer.js +0 -83
  443. package/src/lib/components/deal-import-drawer.js.map +0 -1
  444. package/src/lib/components/lead-import-drawer.d.ts +0 -27
  445. package/src/lib/components/lead-import-drawer.js +0 -118
  446. package/src/lib/components/lead-import-drawer.js.map +0 -1
  447. package/src/lib/components/lead-source-values-card.d.ts +0 -27
  448. package/src/lib/components/lead-source-values-card.js.map +0 -1
  449. package/src/lib/components/task-import-drawer.d.ts +0 -24
  450. package/src/lib/components/task-import-drawer.js +0 -82
  451. package/src/lib/components/task-import-drawer.js.map +0 -1
  452. package/src/lib/model/companies-csv.d.ts +0 -36
  453. package/src/lib/model/companies-csv.js +0 -38
  454. package/src/lib/model/companies-csv.js.map +0 -1
  455. package/src/lib/model/contacts-csv.d.ts +0 -54
  456. package/src/lib/model/contacts-csv.js +0 -67
  457. package/src/lib/model/contacts-csv.js.map +0 -1
  458. package/src/lib/model/crm-company-import.d.ts +0 -154
  459. package/src/lib/model/crm-company-import.js +0 -331
  460. package/src/lib/model/crm-company-import.js.map +0 -1
  461. package/src/lib/model/crm-csv.d.ts +0 -239
  462. package/src/lib/model/crm-csv.js +0 -467
  463. package/src/lib/model/crm-csv.js.map +0 -1
  464. package/src/lib/model/crm-deal-import.d.ts +0 -112
  465. package/src/lib/model/crm-deal-import.js +0 -263
  466. package/src/lib/model/crm-deal-import.js.map +0 -1
  467. package/src/lib/model/crm-import.d.ts +0 -250
  468. package/src/lib/model/crm-import.js +0 -467
  469. package/src/lib/model/crm-import.js.map +0 -1
  470. package/src/lib/model/crm-lead-import.d.ts +0 -138
  471. package/src/lib/model/crm-lead-import.js +0 -446
  472. package/src/lib/model/crm-lead-import.js.map +0 -1
  473. package/src/lib/model/crm-task-import.d.ts +0 -94
  474. package/src/lib/model/crm-task-import.js +0 -275
  475. package/src/lib/model/crm-task-import.js.map +0 -1
  476. package/src/lib/model/deals-csv.d.ts +0 -32
  477. package/src/lib/model/deals-csv.js +0 -36
  478. package/src/lib/model/deals-csv.js.map +0 -1
  479. package/src/lib/model/import-column-shapes.d.ts +0 -36
  480. package/src/lib/model/import-column-shapes.js +0 -76
  481. package/src/lib/model/import-column-shapes.js.map +0 -1
  482. package/src/lib/model/lead-source-values.d.ts +0 -82
  483. package/src/lib/model/lead-source-values.js +0 -149
  484. package/src/lib/model/lead-source-values.js.map +0 -1
  485. package/src/lib/model/leads-csv.d.ts +0 -40
  486. package/src/lib/model/leads-csv.js +0 -40
  487. package/src/lib/model/leads-csv.js.map +0 -1
  488. package/src/lib/model/tasks-csv.d.ts +0 -28
  489. package/src/lib/model/tasks-csv.js +0 -28
  490. package/src/lib/model/tasks-csv.js.map +0 -1
  491. package/src/lib/server/companies-import.d.ts +0 -57
  492. package/src/lib/server/companies-import.js +0 -242
  493. package/src/lib/server/companies-import.js.map +0 -1
  494. package/src/lib/server/contacts-import.d.ts +0 -109
  495. package/src/lib/server/contacts-import.js +0 -393
  496. package/src/lib/server/contacts-import.js.map +0 -1
  497. package/src/lib/server/deals-import.d.ts +0 -76
  498. package/src/lib/server/deals-import.js +0 -248
  499. package/src/lib/server/deals-import.js.map +0 -1
  500. package/src/lib/server/export-route.d.ts +0 -60
  501. package/src/lib/server/export-route.js +0 -447
  502. package/src/lib/server/export-route.js.map +0 -1
  503. package/src/lib/server/import-context.d.ts +0 -87
  504. package/src/lib/server/import-context.js +0 -182
  505. package/src/lib/server/import-context.js.map +0 -1
  506. package/src/lib/server/lead-source-values.d.ts +0 -50
  507. package/src/lib/server/lead-source-values.js +0 -191
  508. package/src/lib/server/lead-source-values.js.map +0 -1
  509. package/src/lib/server/leads-import.d.ts +0 -84
  510. package/src/lib/server/leads-import.js +0 -367
  511. package/src/lib/server/leads-import.js.map +0 -1
  512. package/src/lib/server/tasks-import.d.ts +0 -54
  513. package/src/lib/server/tasks-import.js +0 -160
  514. package/src/lib/server/tasks-import.js.map +0 -1
@@ -0,0 +1,472 @@
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 { buildCrmEmailActivity, checkEntitlement, CONTACT_LIFECYCLE_STAGE_LABELS, CONTACT_TAG_MAX_LENGTH, contactFacetPath, CRM_ACTIVITY_LOG_FULL_MESSAGE, CRM_COLLECTIONS, crmActivityLogHasRoom, crmActivityDirection, crmEmailDeliveryTags, crmScopeTokens, crmTaskLabelsForNew, crmTaskListFields, crmTaskReminderAfterEdit, normalizeContactEmail, parseCrmMemberRef, planLabelGrantingFeature, readContactFacet, visibleToHost } from "@aglyn/aglyn/server";
18
+ import { consentGroupForSite, countCrmActivitiesForRecord, firebaseAdmin, newCrmActivityRef, orgDataQueryForHost, recomputeCrmNextTaskAt, restampCrmListFieldsAt, writeCrmEmailActivity } from "@aglyn/tenant-data-admin";
19
+ // The leaf, not the barrel: the specs substitute the barrel wholesale, and
20
+ // the lookup must reach the real index logic under them.
21
+ import { findContactByEmail } from "@aglyn/tenant-data-admin/server/contact-email-index";
22
+ import { FieldValue } from "firebase-admin/firestore";
23
+ // The leaf, not the runtime barrel: the specs substitute it.
24
+ import { OWNER_ASSIGNMENT_REFUSALS, reassignContactOwner } from "./assign-contact-owner.js";
25
+ import { readCrmTaskPicklists } from "./read-picklist.js";
26
+ import { CRM_SUITE_FEATURE } from "./suite-gate.js";
27
+ const DAY_MS = 24 * 60 * 60 * 1000;
28
+ /**
29
+ * The contact an event names, as this site may see it.
30
+ *
31
+ * Id first, because a CRM event carries the document the door just wrote and
32
+ * a lookup by id is a read, not a query. The address second, through the
33
+ * org's address index narrowed to this site (AGL-2633), for every event that
34
+ * predates the CRM and knows only who filled in the form — so an address a
35
+ * merge folded into another record still reaches the person who now holds it.
36
+ */ async function resolveEventContact(hostId, named) {
37
+ var _named_contactId, _hit_data;
38
+ const { ref: contactsRef } = await orgDataQueryForHost(hostId, 'contacts');
39
+ const contactId = String((_named_contactId = named.contactId) != null ? _named_contactId : '').trim();
40
+ if (contactId) {
41
+ const doc = await contactsRef.doc(contactId).get();
42
+ if (doc.exists && visibleToHost(doc.get('visibleTo'), hostId)) {
43
+ var _doc_data;
44
+ return {
45
+ id: doc.id,
46
+ ref: doc.ref,
47
+ data: (_doc_data = doc.data()) != null ? _doc_data : {}
48
+ };
49
+ }
50
+ }
51
+ const hit = await findContactByEmail(contactsRef, named.email, {
52
+ hostId
53
+ });
54
+ return hit ? {
55
+ id: hit.id,
56
+ ref: hit.ref,
57
+ data: (_hit_data = hit.data()) != null ? _hit_data : {}
58
+ } : null;
59
+ }
60
+ /**
61
+ * The uid a step names for somebody on the team — an owner, an assignee —
62
+ * resolved against the org's roster, whichever way the step named them.
63
+ *
64
+ * A step names a member by uid when a picker wrote it and by address when a
65
+ * person typed it, and the editor's one text field accepts either — so both
66
+ * fields are read through `parseCrmMemberRef`, and an address typed into the
67
+ * uid slot or a uid typed into the address slot still names the person.
68
+ *
69
+ * Both are RESOLVED, not trusted. `orgs/{orgId}/members/{uid}` is keyed by the
70
+ * uid, so a uid costs one document read and must exist: `ownerUid` and
71
+ * `assigneeUid` are fields every reader resolves as a member, and a stranger's
72
+ * uid in one is a task nobody on the team can find. An address is matched on
73
+ * the roster's own `email` field, and two production paths create a member
74
+ * document WITHOUT one (a host-access re-grant, and an add whose auth record
75
+ * carried none) — such a member is named by uid, which is the reason the
76
+ * editor takes one.
77
+ *
78
+ * The roster is the only directory consulted. A project-level Auth lookup by
79
+ * address would resolve people who are not on this organization at all
80
+ * (AGL-1122). A reference that resolves neither way is an error, not a stored
81
+ * string.
82
+ */ async function resolveMemberUid(orgId, named, role) {
83
+ var _parseCrmMemberRef;
84
+ const ref = (_parseCrmMemberRef = parseCrmMemberRef(named.uid)) != null ? _parseCrmMemberRef : parseCrmMemberRef(named.email);
85
+ if (!ref) return {
86
+ error: `no ${role} named on the step`
87
+ };
88
+ if (!orgId) return {
89
+ error: 'this site has no organization'
90
+ };
91
+ const members = firebaseAdmin.app().firestore().collection('orgs').doc(orgId).collection('members');
92
+ if (ref.kind === 'uid') {
93
+ const member = await members.doc(ref.uid).get();
94
+ if (member.exists) return {
95
+ uid: ref.uid,
96
+ detail: ref.uid
97
+ };
98
+ return {
99
+ error: `no team member with the id ${ref.uid}`
100
+ };
101
+ }
102
+ const byField = (await members.where('email', '==', ref.email).limit(1).get()).docs[0];
103
+ if (byField) return {
104
+ uid: byField.id,
105
+ detail: ref.email
106
+ };
107
+ return {
108
+ error: `no team member with the address ${ref.email}`
109
+ };
110
+ }
111
+ /**
112
+ * Runs one CRM step for the person the event names: the executor the CRM
113
+ * registers on the server-step seam for every type in `CRM_STEP_TYPES`.
114
+ *
115
+ * Never throws for a plan without the CRM, a missing person, an unknown owner
116
+ * or a site with no org — those are answered as `error` so the run records
117
+ * them; a Firestore failure propagates, and the engine records it the same
118
+ * way.
119
+ */ export async function runCrmAutomationStep(request, nowMs = Date.now()) {
120
+ var _contact_data_email, _env_org;
121
+ if (!checkEntitlement(request.org, CRM_SUITE_FEATURE)) {
122
+ return {
123
+ error: `CRM steps require the ${planLabelGrantingFeature(CRM_SUITE_FEATURE)} plan`
124
+ };
125
+ }
126
+ const env = {
127
+ hostId: request.hostId,
128
+ org: request.org,
129
+ orgId: request.orgId
130
+ };
131
+ const step = request.step;
132
+ const actionId = request.run.id;
133
+ const { hostId } = env;
134
+ const contact = await resolveEventContact(hostId, request.payload);
135
+ if (!contact) {
136
+ var _ref, _request_payload_contactId;
137
+ const named = String((_ref = (_request_payload_contactId = request.payload['contactId']) != null ? _request_payload_contactId : request.payload['email']) != null ? _ref : '').trim();
138
+ return {
139
+ error: named ? `no contact this site can see for ${named}` : 'the event names no contact — no contactId or email in its payload'
140
+ };
141
+ }
142
+ /*
143
+ * THE HOLDER whose facet the write addresses — the site's consent group,
144
+ * which is the site alone unless the org declared the site one of a set
145
+ * that presents as one sender. Same resolution the capture doors use, so a
146
+ * stage set here lands in the facet the console reads.
147
+ */ const group = await consentGroupForSite(hostId);
148
+ const facet = readContactFacet(contact.data, group.groupId);
149
+ const email = String((_contact_data_email = contact.data['email']) != null ? _contact_data_email : '');
150
+ if (step.type === 'setContactStage') {
151
+ var _facet_lifecycleStage;
152
+ const previous = (_facet_lifecycleStage = facet.lifecycleStage) != null ? _facet_lifecycleStage : '';
153
+ const label = CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage];
154
+ /*
155
+ * A stage set to what it already is changes nothing and ANNOUNCES
156
+ * nothing. The write is skipped so the row's `updatedAt` does not move for
157
+ * a non-event, and the absent emit is what stops an action that sets the
158
+ * stage it listens for from re-running on its own write — the engine's
159
+ * nesting cap bounds that chain, this ends it at once.
160
+ */ if (previous === step.lifecycleStage) {
161
+ return {
162
+ detail: `${label} (already)`
163
+ };
164
+ }
165
+ await contact.ref.update({
166
+ [contactFacetPath(group.groupId, 'lifecycleStage')]: step.lifecycleStage,
167
+ updatedAt: FieldValue.serverTimestamp()
168
+ });
169
+ // The stage is what the Contacts list filters by (AGL-3321).
170
+ await restampCrmListFieldsAt(contact.ref, 'contacts');
171
+ /*
172
+ * A stage set by an automation IS a stage change, and the action
173
+ * listening for it must run — but through the engine's nesting cap, so
174
+ * the event is answered back for the engine to raise, never raised here.
175
+ */ return {
176
+ detail: label,
177
+ emit: {
178
+ event: 'contactStageChanged',
179
+ payload: {
180
+ contactId: contact.id,
181
+ email,
182
+ lifecycleStage: step.lifecycleStage,
183
+ previousStage: previous
184
+ }
185
+ }
186
+ };
187
+ }
188
+ if (step.type === 'addContactTag') {
189
+ var _step_tag;
190
+ const tag = String((_step_tag = step.tag) != null ? _step_tag : '').trim().slice(0, CONTACT_TAG_MAX_LENGTH);
191
+ if (!tag) return {
192
+ error: 'the step has no tag'
193
+ };
194
+ // `arrayUnion`, so the tag a merchant already applied by hand is not
195
+ // duplicated and the tags beside it are kept.
196
+ await contact.ref.update({
197
+ [contactFacetPath(group.groupId, 'tags')]: FieldValue.arrayUnion(tag),
198
+ updatedAt: FieldValue.serverTimestamp()
199
+ });
200
+ // The tags are what the Contacts list filters by (AGL-3321).
201
+ await restampCrmListFieldsAt(contact.ref, 'contacts');
202
+ return {
203
+ detail: tag
204
+ };
205
+ }
206
+ if (step.type === 'assignContactOwner') {
207
+ /*
208
+ * Through the one server assignment (AGL-2618), in both of the step's
209
+ * modes, rather than a facet write of its own: the helper is what moves
210
+ * the round-robin pointer inside the transaction that writes the owner,
211
+ * mirrors the owner onto the site's lead, and tells the new owner. A
212
+ * member named on the step is still resolved here — by address against
213
+ * the roster, as before — and handed over by uid; the helper checks the
214
+ * roster document again, which is one read and the same answer.
215
+ */ let assign;
216
+ let named = 'round robin';
217
+ if (step.roundRobin === true) {
218
+ assign = {
219
+ roundRobin: true
220
+ };
221
+ } else {
222
+ const owner = await resolveMemberUid(env.orgId, {
223
+ uid: step.ownerUid,
224
+ email: step.ownerEmail
225
+ }, 'owner');
226
+ if ('error' in owner) return {
227
+ error: owner.error
228
+ };
229
+ assign = {
230
+ memberUid: owner.uid
231
+ };
232
+ named = owner.detail;
233
+ }
234
+ const verdict = await reassignContactOwner({
235
+ hostId,
236
+ contactId: contact.id,
237
+ email,
238
+ assign
239
+ });
240
+ if (verdict.outcome === 'none') {
241
+ return {
242
+ error: OWNER_ASSIGNMENT_REFUSALS[verdict.reason]
243
+ };
244
+ }
245
+ if (verdict.outcome === 'unchanged') {
246
+ return {
247
+ detail: `${named} (already)`
248
+ };
249
+ }
250
+ return {
251
+ detail: step.roundRobin === true ? `round robin → ${verdict.ownerUid}` : named
252
+ };
253
+ }
254
+ // The two record creators: a task and an activity are documents of their
255
+ // own beside the contact, stamped with the scope a contact captured on this
256
+ // site would carry — `crmScopeTokens` is the create path's own expression,
257
+ // so a record an automation made is visible to exactly the sites a record a
258
+ // person made would be.
259
+ if (!env.orgId) return {
260
+ error: 'this site has no organization'
261
+ };
262
+ const orgRef = firebaseAdmin.app().firestore().collection('orgs').doc(env.orgId);
263
+ const visibleTo = crmScopeTokens((_env_org = env.org) != null ? _env_org : null, group);
264
+ const links = _extends({
265
+ contactId: contact.id
266
+ }, facet.companyId ? {
267
+ companyId: facet.companyId
268
+ } : {});
269
+ if (step.type === 'createCrmTask') {
270
+ var _step_title, _facet_ownerUid;
271
+ var _step_assigneeUid, _step_assigneeEmail;
272
+ const title = String((_step_title = step.title) != null ? _step_title : '').trim().slice(0, 200);
273
+ if (!title) return {
274
+ error: 'the task has no title'
275
+ };
276
+ const dueInDays = Math.max(0, Math.round(Number(step.dueInDays) || 0));
277
+ /*
278
+ * The assignee the step names, else the person who OWNS the contact, else
279
+ * nobody. A follow-up task belongs to whoever holds the relationship, and
280
+ * an automation that had to name one person for every contact it touches
281
+ * would assign the whole list to one rep. A named assignee who cannot be
282
+ * resolved is an error rather than a fallback to the owner: the author
283
+ * named somebody on purpose.
284
+ */ let assignee = (_facet_ownerUid = facet.ownerUid) != null ? _facet_ownerUid : '';
285
+ if (((_step_assigneeUid = step.assigneeUid) == null ? void 0 : _step_assigneeUid.trim()) || ((_step_assigneeEmail = step.assigneeEmail) == null ? void 0 : _step_assigneeEmail.trim())) {
286
+ const named = await resolveMemberUid(env.orgId, {
287
+ uid: step.assigneeUid,
288
+ email: step.assigneeEmail
289
+ }, 'assignee');
290
+ if ('error' in named) return {
291
+ error: named.error
292
+ };
293
+ assignee = named.uid;
294
+ }
295
+ const dueAtMs = nowMs + dueInDays * DAY_MS;
296
+ const priority = step.priority === 'high' || step.priority === 'low' ? step.priority : 'normal';
297
+ // The org's labels for the meanings the step names (AGL-3517).
298
+ const labels = crmTaskLabelsForNew(await readCrmTaskPicklists(firebaseAdmin.app().firestore(), env.orgId), {
299
+ kind: step.kind,
300
+ priority
301
+ });
302
+ const task = _extends({
303
+ title,
304
+ kind: step.kind,
305
+ priority,
306
+ status: 'open'
307
+ }, labels, {
308
+ dueAtMs,
309
+ // The reminder a person's task gets (AGL-2659): the due time.
310
+ remindAtMs: crmTaskReminderAfterEdit({
311
+ dueAtMs,
312
+ previous: null
313
+ })
314
+ }, assignee ? {
315
+ assigneeUid: assignee
316
+ } : {}, {
317
+ createdByUid: '',
318
+ sourceActionId: actionId
319
+ }, links, {
320
+ hostId,
321
+ visibleTo,
322
+ createdAt: FieldValue.serverTimestamp(),
323
+ updatedAt: FieldValue.serverTimestamp()
324
+ });
325
+ // What the Tasks list searches by (AGL-3321), beside the task.
326
+ await orgRef.collection(CRM_COLLECTIONS.tasks).add(_extends({}, task, crmTaskListFields(task)));
327
+ // The contact and company the task names carry `nextTaskAtMs` (AGL-2661);
328
+ // a figure that could not move is the Fields section's recompute's.
329
+ await recomputeCrmNextTaskAt(firebaseAdmin.app().firestore(), env.orgId, [
330
+ links
331
+ ]).catch((error)=>{
332
+ console.error('[crm] next activity could not be recomputed', env.orgId, error);
333
+ });
334
+ return {
335
+ detail: title.slice(0, 60)
336
+ };
337
+ }
338
+ if (step.type === 'logCrmActivity') {
339
+ var _step_body;
340
+ const body = String((_step_body = step.body) != null ? _step_body : '').trim().slice(0, 2000);
341
+ if (!body) return {
342
+ error: 'the activity has no body'
343
+ };
344
+ /*
345
+ * The per-record ceiling (AGL-2611), and this step is the writer it exists
346
+ * for: a flow that logs on every page view fills one person's log in an
347
+ * afternoon. Refused into the run history as an error, the way an
348
+ * unresolvable assignee is, so the merchant reads why the flow stopped
349
+ * writing rather than finding a log that silently stopped.
350
+ */ const logged = await countCrmActivitiesForRecord(orgRef, links);
351
+ if (!crmActivityLogHasRoom(logged)) {
352
+ return {
353
+ error: CRM_ACTIVITY_LOG_FULL_MESSAGE
354
+ };
355
+ }
356
+ const direction = crmActivityDirection(step.kind, step.direction);
357
+ const activity = _extends({
358
+ kind: step.kind,
359
+ body
360
+ }, direction ? {
361
+ direction
362
+ } : {}, {
363
+ atMs: nowMs,
364
+ byUid: '',
365
+ sourceActionId: actionId
366
+ }, links, {
367
+ hostId,
368
+ visibleTo,
369
+ createdAt: FieldValue.serverTimestamp(),
370
+ updatedAt: FieldValue.serverTimestamp()
371
+ });
372
+ await orgRef.collection(CRM_COLLECTIONS.activities).add(activity);
373
+ return {
374
+ detail: step.kind
375
+ };
376
+ }
377
+ return {
378
+ error: `unknown CRM step "${step.type}"`
379
+ };
380
+ }
381
+ /**
382
+ * Whether — and where — an email a plugin is about to send lands on a
383
+ * timeline: the CRM's answer to the record timeline's `prepareEmail`.
384
+ *
385
+ * An automation's `sendEmail` step is not a CRM step: it mails whatever
386
+ * address the event carries, to a person who may be nobody the CRM knows. It
387
+ * earns a row on exactly one condition, that the message is ADDRESSED TO THE
388
+ * CONTACT the sender means — the person the request's link finds, at the
389
+ * address the row holds. A welcome sequence to a new contact is that; an
390
+ * internal alert routed to a merchant's own address through `toField` is not,
391
+ * and a row for it would put the merchant's inbox on a customer's history.
392
+ *
393
+ * Prepared ahead of the send, for the reason the console route mints its id
394
+ * first: the delivery webhook has nothing but the tags on the message to find
395
+ * the row with. Nothing is written until `file` is called, once the provider
396
+ * accepted the message. A workspace whose plan does not carry the CRM, a
397
+ * record at the activity ceiling and a site with no org earn no row — the
398
+ * message still goes, because the ceiling bounds the log and not the mail.
399
+ *
400
+ * **Never throws**, and neither does `file`. The row is bookkeeping beside a
401
+ * send, and a lookup that fails must not become a message that never left —
402
+ * the posture every meter beside a send takes. A failure is logged and the
403
+ * message goes out untagged.
404
+ */ export async function prepareCrmRecordEmail(request) {
405
+ var _request_orgId, _request_hostId;
406
+ const orgId = String((_request_orgId = request.orgId) != null ? _request_orgId : '').trim();
407
+ const hostId = String((_request_hostId = request.hostId) != null ? _request_hostId : '').trim();
408
+ if (!orgId || !hostId) return null;
409
+ const address = normalizeContactEmail(request.to);
410
+ if (!address) return null;
411
+ try {
412
+ var _data;
413
+ const firestore = firebaseAdmin.app().firestore();
414
+ const orgRef = firestore.collection('orgs').doc(orgId);
415
+ // The org the caller already read, or the one read here once.
416
+ const org = request.org !== undefined ? request.org : (_data = (await orgRef.get()).data()) != null ? _data : null;
417
+ if (!checkEntitlement(org, CRM_SUITE_FEATURE)) {
418
+ return null;
419
+ }
420
+ const contact = await resolveEventContact(hostId, request.link);
421
+ if (!contact || normalizeContactEmail(contact.data['email']) !== address) {
422
+ return null;
423
+ }
424
+ const group = await consentGroupForSite(hostId);
425
+ const facet = readContactFacet(contact.data, group.groupId);
426
+ const link = _extends({
427
+ contactId: contact.id
428
+ }, facet.companyId ? {
429
+ companyId: facet.companyId
430
+ } : {});
431
+ if (!crmActivityLogHasRoom(await countCrmActivitiesForRecord(orgRef, link))) {
432
+ return null;
433
+ }
434
+ const ref = newCrmActivityRef(firestore, orgId);
435
+ const visibleTo = crmScopeTokens(org != null ? org : null, group);
436
+ return {
437
+ tags: crmEmailDeliveryTags({
438
+ orgId,
439
+ hostId,
440
+ activityId: ref.id
441
+ }),
442
+ /*
443
+ * The same shape the console route logs — `buildCrmEmailActivity` is the
444
+ * one builder — with the sender's id as its source and nobody as its
445
+ * author. Never throws: the message has left, and a row that could not
446
+ * be written is a gap on the timeline rather than a failed send.
447
+ */ async file (sent) {
448
+ try {
449
+ var _normalizeContactEmail, _sent_atMs;
450
+ await writeCrmEmailActivity(ref, buildCrmEmailActivity({
451
+ subject: sent.subject,
452
+ body: sent.body,
453
+ to: (_normalizeContactEmail = normalizeContactEmail(sent.to)) != null ? _normalizeContactEmail : sent.to,
454
+ atMs: (_sent_atMs = sent.atMs) != null ? _sent_atMs : Date.now(),
455
+ byUid: '',
456
+ sourceActionId: sent.sourceRef,
457
+ link,
458
+ hostId,
459
+ visibleTo
460
+ }));
461
+ } catch (error) {
462
+ console.error('[crm] automation email activity write failed', ref.id, error);
463
+ }
464
+ }
465
+ };
466
+ } catch (error) {
467
+ console.error('[crm] automation email activity could not be prepared', hostId, error);
468
+ return null;
469
+ }
470
+ }
471
+
472
+ //# sourceMappingURL=automation-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/automation-steps.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 {\n buildCrmEmailActivity,\n checkEntitlement,\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_TAG_MAX_LENGTH,\n contactFacetPath,\n CRM_ACTIVITY_LOG_FULL_MESSAGE,\n CRM_COLLECTIONS,\n crmActivityLogHasRoom,\n type CrmActivity,\n type CrmActivityDirection,\n crmActivityDirection,\n type CrmActivityLink,\n type CrmTask,\n type CrmTaskPriority,\n crmEmailDeliveryTags,\n crmScopeTokens,\n crmTaskLabelsForNew,\n crmTaskListFields,\n crmTaskReminderAfterEdit,\n normalizeContactEmail,\n parseCrmMemberRef,\n planLabelGrantingFeature,\n readContactFacet,\n visibleToHost,\n} from '@aglyn/aglyn/server'\nimport type {\n PluginRecordEmailPrepareRequest,\n PluginRecordPreparedEmail,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-timeline'\nimport type {\n ServerStepAnswer,\n ServerStepRequest,\n} from '@aglyn/aglyn/plugin-manager/plugin-server-steps'\nimport {\n consentGroupForSite,\n countCrmActivitiesForRecord,\n firebaseAdmin,\n newCrmActivityRef,\n orgDataQueryForHost,\n recomputeCrmNextTaskAt,\n restampCrmListFieldsAt,\n writeCrmEmailActivity,\n} from '@aglyn/tenant-data-admin'\n// The leaf, not the barrel: the specs substitute the barrel wholesale, and\n// the lookup must reach the real index logic under them.\nimport { findContactByEmail } from '@aglyn/tenant-data-admin/server/contact-email-index'\nimport { FieldValue } from 'firebase-admin/firestore'\n// The leaf, not the runtime barrel: the specs substitute it.\nimport {\n OWNER_ASSIGNMENT_REFUSALS,\n reassignContactOwner,\n} from './assign-contact-owner'\nimport type { InteractionStepGuard } from '@aglyn/aglyn/app-utils/site-interactions'\nimport type { ContactLifecycleStage, CrmActivityKind, CrmTaskKind } from '@aglyn/aglyn/app-utils/crm-kinds'\nimport type { CRM_STEP_TYPES } from '../constants/bundle-common'\nimport { readCrmTaskPicklists } from './read-picklist'\nimport { CRM_SUITE_FEATURE } from './suite-gate'\n\n/**\n * THE AUTOMATION STEPS THAT WRITE THE CRM (AGL-2605), run for the automation\n * engine through the platform's server-step seam (`plugin-server-steps`,\n * AGL-3080): `setContactStage`, `addContactTag`, `assignContactOwner`,\n * `createCrmTask` and `logCrmActivity`.\n *\n * The engine keeps the step's guard, the run's order, its history and the\n * nesting cap; this answers what the step did. A refusal is ANSWERED as\n * `error`, never thrown, and its words are the line the run history carries —\n * the same words the engine wrote when these steps were its own.\n *\n * The five share one shape no other step has: each begins by finding THE\n * PERSON the event is about, and each writes either inside the site's facet on\n * that person or a record stamped with the site's scope. One resolver and one\n * scope expression, used five times, rather than five copies drifting apart.\n *\n * ## The person is the event's, never the step's\n *\n * A step carries no contact reference. The contact is whoever the event\n * names — by `contactId` when the emitting door knew the document, and by\n * `email` otherwise, which is what every pre-CRM event carries. Both lookups\n * are scoped to what THIS site may see: a document the id names but the site\n * cannot read is treated as absent, exactly as the scoped query would have\n * treated it, so an event replayed against the wrong site reaches nobody.\n *\n * ## A missing person is a reported no-op\n *\n * Nothing is written and the step's error names the reason, which lands in\n * the run summary beside the trigger — the same place \"why didn't it fire?\"\n * is answered for every other step. Silence here would make a mis-wired\n * trigger (a form with no email field) indistinguishable from a working one.\n *\n * ## The plan gate is the CRM's\n *\n * Every step is refused on a workspace whose plan does not carry the CRM,\n * into the run history with the tier that carries it, so a Free workspace\n * whose flow names a CRM step reads why the step did nothing rather than a\n * log that says it ran. The org document is the one the run's gate already\n * read, handed over with the step, so the check costs no read.\n *\n * The same module answers the record timeline's `prepareEmail` (AGL-2615): an\n * automation's email addressed to the contact the event is about is filed on\n * that contact's timeline — see {@link prepareCrmRecordEmail}.\n */\n\n/** One of the steps this module runs. */\nexport type CrmStepType = (typeof CRM_STEP_TYPES)[number]\n\n/**\n * A CRM step, as it is stored: the CRM's own shapes of the steps it runs. The\n * automation editor writes them and checks them; `apps/console/specs/\n * crm-automation-steps.spec.ts` runs what it writes through this module.\n */\nexport type CrmAutomationStep = (\n | { type: 'setContactStage'; lifecycleStage: ContactLifecycleStage }\n | { type: 'addContactTag'; tag: string }\n | { type: 'assignContactOwner'; ownerUid?: string; ownerEmail?: string; roundRobin?: boolean }\n | {\n type: 'createCrmTask'\n title: string\n kind: CrmTaskKind\n /** `normal` when absent — the steps written before priorities were (AGL-3517). */\n priority?: CrmTaskPriority\n dueInDays: number\n assigneeUid?: string\n assigneeEmail?: string\n }\n | {\n type: 'logCrmActivity'\n kind: CrmActivityKind\n body: string\n /** Which way a call or an email went (AGL-3517). */\n direction?: CrmActivityDirection\n }\n) & { when?: InteractionStepGuard | null } & { type: CrmStepType }\n\n/** The site, the plan and the org a step or an email is answered for. */\ninterface CrmStepEnv {\n hostId: string\n /** The owning org's billing doc, already read by the run's gate. */\n org: unknown\n orgId: string | null\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\ninterface ResolvedContact {\n id: string\n ref: FirebaseFirestore.DocumentReference\n data: Record<string, unknown>\n}\n\n/**\n * The contact an event names, as this site may see it.\n *\n * Id first, because a CRM event carries the document the door just wrote and\n * a lookup by id is a read, not a query. The address second, through the\n * org's address index narrowed to this site (AGL-2633), for every event that\n * predates the CRM and knows only who filled in the form — so an address a\n * merge folded into another record still reaches the person who now holds it.\n */\nasync function resolveEventContact(\n hostId: string,\n named: { contactId?: unknown; email?: unknown },\n): Promise<ResolvedContact | null> {\n const { ref: contactsRef } = await orgDataQueryForHost(hostId, 'contacts')\n const contactId = String(named.contactId ?? '').trim()\n if (contactId) {\n const doc = await contactsRef.doc(contactId).get()\n if (doc.exists && visibleToHost(doc.get('visibleTo'), hostId)) {\n return { id: doc.id, ref: doc.ref, data: doc.data() ?? {} }\n }\n }\n const hit = await findContactByEmail(contactsRef, named.email, { hostId })\n return hit ? { id: hit.id, ref: hit.ref, data: hit.data() ?? {} } : null\n}\n\n/**\n * The uid a step names for somebody on the team — an owner, an assignee —\n * resolved against the org's roster, whichever way the step named them.\n *\n * A step names a member by uid when a picker wrote it and by address when a\n * person typed it, and the editor's one text field accepts either — so both\n * fields are read through `parseCrmMemberRef`, and an address typed into the\n * uid slot or a uid typed into the address slot still names the person.\n *\n * Both are RESOLVED, not trusted. `orgs/{orgId}/members/{uid}` is keyed by the\n * uid, so a uid costs one document read and must exist: `ownerUid` and\n * `assigneeUid` are fields every reader resolves as a member, and a stranger's\n * uid in one is a task nobody on the team can find. An address is matched on\n * the roster's own `email` field, and two production paths create a member\n * document WITHOUT one (a host-access re-grant, and an add whose auth record\n * carried none) — such a member is named by uid, which is the reason the\n * editor takes one.\n *\n * The roster is the only directory consulted. A project-level Auth lookup by\n * address would resolve people who are not on this organization at all\n * (AGL-1122). A reference that resolves neither way is an error, not a stored\n * string.\n */\nasync function resolveMemberUid(\n orgId: string | null,\n named: { uid?: string; email?: string },\n role: 'owner' | 'assignee',\n): Promise<{ uid: string; detail: string } | { error: string }> {\n const ref = parseCrmMemberRef(named.uid) ?? parseCrmMemberRef(named.email)\n if (!ref) return { error: `no ${role} named on the step` }\n if (!orgId) return { error: 'this site has no organization' }\n const members = firebaseAdmin\n .app()\n .firestore()\n .collection('orgs')\n .doc(orgId)\n .collection('members')\n if (ref.kind === 'uid') {\n const member = await members.doc(ref.uid).get()\n if (member.exists) return { uid: ref.uid, detail: ref.uid }\n return { error: `no team member with the id ${ref.uid}` }\n }\n const byField = (await members.where('email', '==', ref.email).limit(1).get()).docs[0]\n if (byField) return { uid: byField.id, detail: ref.email }\n return { error: `no team member with the address ${ref.email}` }\n}\n\n/**\n * Runs one CRM step for the person the event names: the executor the CRM\n * registers on the server-step seam for every type in `CRM_STEP_TYPES`.\n *\n * Never throws for a plan without the CRM, a missing person, an unknown owner\n * or a site with no org — those are answered as `error` so the run records\n * them; a Firestore failure propagates, and the engine records it the same\n * way.\n */\nexport async function runCrmAutomationStep(\n request: ServerStepRequest,\n nowMs = Date.now(),\n): Promise<ServerStepAnswer> {\n if (!checkEntitlement(request.org as Parameters<typeof checkEntitlement>[0], CRM_SUITE_FEATURE)) {\n return { error: `CRM steps require the ${planLabelGrantingFeature(CRM_SUITE_FEATURE)} plan` }\n }\n const env: CrmStepEnv = { hostId: request.hostId, org: request.org, orgId: request.orgId }\n const step = request.step as unknown as CrmAutomationStep\n const actionId = request.run.id\n const { hostId } = env\n const contact = await resolveEventContact(hostId, request.payload)\n if (!contact) {\n const named = String(request.payload['contactId'] ?? request.payload['email'] ?? '').trim()\n return {\n error: named\n ? `no contact this site can see for ${named}`\n : 'the event names no contact — no contactId or email in its payload',\n }\n }\n /*\n * THE HOLDER whose facet the write addresses — the site's consent group,\n * which is the site alone unless the org declared the site one of a set\n * that presents as one sender. Same resolution the capture doors use, so a\n * stage set here lands in the facet the console reads.\n */\n const group = await consentGroupForSite(hostId)\n const facet = readContactFacet(contact.data, group.groupId)\n const email = String(contact.data['email'] ?? '')\n\n if (step.type === 'setContactStage') {\n const previous = facet.lifecycleStage ?? ''\n const label = CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage]\n /*\n * A stage set to what it already is changes nothing and ANNOUNCES\n * nothing. The write is skipped so the row's `updatedAt` does not move for\n * a non-event, and the absent emit is what stops an action that sets the\n * stage it listens for from re-running on its own write — the engine's\n * nesting cap bounds that chain, this ends it at once.\n */\n if (previous === step.lifecycleStage) {\n return { detail: `${label} (already)` }\n }\n await contact.ref.update({\n [contactFacetPath(group.groupId, 'lifecycleStage')]: step.lifecycleStage,\n updatedAt: FieldValue.serverTimestamp(),\n })\n // The stage is what the Contacts list filters by (AGL-3321).\n await restampCrmListFieldsAt(contact.ref, 'contacts')\n /*\n * A stage set by an automation IS a stage change, and the action\n * listening for it must run — but through the engine's nesting cap, so\n * the event is answered back for the engine to raise, never raised here.\n */\n return {\n detail: label,\n emit: {\n event: 'contactStageChanged',\n payload: {\n contactId: contact.id,\n email,\n lifecycleStage: step.lifecycleStage,\n previousStage: previous,\n },\n },\n }\n }\n\n if (step.type === 'addContactTag') {\n const tag = String(step.tag ?? '')\n .trim()\n .slice(0, CONTACT_TAG_MAX_LENGTH)\n if (!tag) return { error: 'the step has no tag' }\n // `arrayUnion`, so the tag a merchant already applied by hand is not\n // duplicated and the tags beside it are kept.\n await contact.ref.update({\n [contactFacetPath(group.groupId, 'tags')]: FieldValue.arrayUnion(tag),\n updatedAt: FieldValue.serverTimestamp(),\n })\n // The tags are what the Contacts list filters by (AGL-3321).\n await restampCrmListFieldsAt(contact.ref, 'contacts')\n return { detail: tag }\n }\n\n if (step.type === 'assignContactOwner') {\n /*\n * Through the one server assignment (AGL-2618), in both of the step's\n * modes, rather than a facet write of its own: the helper is what moves\n * the round-robin pointer inside the transaction that writes the owner,\n * mirrors the owner onto the site's lead, and tells the new owner. A\n * member named on the step is still resolved here — by address against\n * the roster, as before — and handed over by uid; the helper checks the\n * roster document again, which is one read and the same answer.\n */\n let assign: { memberUid: string } | { roundRobin: true }\n let named = 'round robin'\n if (step.roundRobin === true) {\n assign = { roundRobin: true }\n } else {\n const owner = await resolveMemberUid(\n env.orgId,\n { uid: step.ownerUid, email: step.ownerEmail },\n 'owner',\n )\n if ('error' in owner) return { error: owner.error }\n assign = { memberUid: owner.uid }\n named = owner.detail\n }\n const verdict = await reassignContactOwner({\n hostId,\n contactId: contact.id,\n email,\n assign,\n })\n if (verdict.outcome === 'none') {\n return { error: OWNER_ASSIGNMENT_REFUSALS[verdict.reason] }\n }\n if (verdict.outcome === 'unchanged') {\n return { detail: `${named} (already)` }\n }\n return {\n detail: step.roundRobin === true ? `round robin → ${verdict.ownerUid}` : named,\n }\n }\n\n // The two record creators: a task and an activity are documents of their\n // own beside the contact, stamped with the scope a contact captured on this\n // site would carry — `crmScopeTokens` is the create path's own expression,\n // so a record an automation made is visible to exactly the sites a record a\n // person made would be.\n if (!env.orgId) return { error: 'this site has no organization' }\n const orgRef = firebaseAdmin.app().firestore().collection('orgs').doc(env.orgId)\n const visibleTo = crmScopeTokens((env.org ?? null) as Record<string, unknown> | null, group)\n const links = {\n contactId: contact.id,\n ...(facet.companyId ? { companyId: facet.companyId } : {}),\n }\n\n if (step.type === 'createCrmTask') {\n const title = String(step.title ?? '')\n .trim()\n .slice(0, 200)\n if (!title) return { error: 'the task has no title' }\n const dueInDays = Math.max(0, Math.round(Number(step.dueInDays) || 0))\n /*\n * The assignee the step names, else the person who OWNS the contact, else\n * nobody. A follow-up task belongs to whoever holds the relationship, and\n * an automation that had to name one person for every contact it touches\n * would assign the whole list to one rep. A named assignee who cannot be\n * resolved is an error rather than a fallback to the owner: the author\n * named somebody on purpose.\n */\n let assignee = facet.ownerUid ?? ''\n if (step.assigneeUid?.trim() || step.assigneeEmail?.trim()) {\n const named = await resolveMemberUid(\n env.orgId,\n { uid: step.assigneeUid, email: step.assigneeEmail },\n 'assignee',\n )\n if ('error' in named) return { error: named.error }\n assignee = named.uid\n }\n const dueAtMs = nowMs + dueInDays * DAY_MS\n const priority: CrmTaskPriority =\n step.priority === 'high' || step.priority === 'low' ? step.priority : 'normal'\n // The org's labels for the meanings the step names (AGL-3517).\n const labels = crmTaskLabelsForNew(\n await readCrmTaskPicklists(firebaseAdmin.app().firestore(), env.orgId),\n { kind: step.kind, priority },\n )\n const task: CrmTask = {\n title,\n kind: step.kind,\n priority,\n status: 'open',\n ...labels,\n dueAtMs,\n // The reminder a person's task gets (AGL-2659): the due time.\n remindAtMs: crmTaskReminderAfterEdit({ dueAtMs, previous: null }),\n ...(assignee ? { assigneeUid: assignee } : {}),\n createdByUid: '',\n sourceActionId: actionId,\n ...links,\n hostId,\n visibleTo,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n }\n // What the Tasks list searches by (AGL-3321), beside the task.\n await orgRef.collection(CRM_COLLECTIONS.tasks).add({ ...task, ...crmTaskListFields(task) })\n // The contact and company the task names carry `nextTaskAtMs` (AGL-2661);\n // a figure that could not move is the Fields section's recompute's.\n await recomputeCrmNextTaskAt(firebaseAdmin.app().firestore(), env.orgId, [links]).catch(\n (error: unknown) => {\n console.error('[crm] next activity could not be recomputed', env.orgId, error)\n },\n )\n return { detail: title.slice(0, 60) }\n }\n\n if (step.type === 'logCrmActivity') {\n const body = String(step.body ?? '')\n .trim()\n .slice(0, 2000)\n if (!body) return { error: 'the activity has no body' }\n /*\n * The per-record ceiling (AGL-2611), and this step is the writer it exists\n * for: a flow that logs on every page view fills one person's log in an\n * afternoon. Refused into the run history as an error, the way an\n * unresolvable assignee is, so the merchant reads why the flow stopped\n * writing rather than finding a log that silently stopped.\n */\n const logged = await countCrmActivitiesForRecord(orgRef, links)\n if (!crmActivityLogHasRoom(logged)) {\n return { error: CRM_ACTIVITY_LOG_FULL_MESSAGE }\n }\n const direction = crmActivityDirection(step.kind, step.direction)\n const activity: CrmActivity = {\n kind: step.kind,\n body,\n ...(direction ? { direction } : {}),\n atMs: nowMs,\n byUid: '',\n sourceActionId: actionId,\n ...links,\n hostId,\n visibleTo,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n }\n await orgRef.collection(CRM_COLLECTIONS.activities).add(activity)\n return { detail: step.kind }\n }\n\n return { error: `unknown CRM step \"${(step as { type: string }).type}\"` }\n}\n\n/**\n * Whether — and where — an email a plugin is about to send lands on a\n * timeline: the CRM's answer to the record timeline's `prepareEmail`.\n *\n * An automation's `sendEmail` step is not a CRM step: it mails whatever\n * address the event carries, to a person who may be nobody the CRM knows. It\n * earns a row on exactly one condition, that the message is ADDRESSED TO THE\n * CONTACT the sender means — the person the request's link finds, at the\n * address the row holds. A welcome sequence to a new contact is that; an\n * internal alert routed to a merchant's own address through `toField` is not,\n * and a row for it would put the merchant's inbox on a customer's history.\n *\n * Prepared ahead of the send, for the reason the console route mints its id\n * first: the delivery webhook has nothing but the tags on the message to find\n * the row with. Nothing is written until `file` is called, once the provider\n * accepted the message. A workspace whose plan does not carry the CRM, a\n * record at the activity ceiling and a site with no org earn no row — the\n * message still goes, because the ceiling bounds the log and not the mail.\n *\n * **Never throws**, and neither does `file`. The row is bookkeeping beside a\n * send, and a lookup that fails must not become a message that never left —\n * the posture every meter beside a send takes. A failure is logged and the\n * message goes out untagged.\n */\nexport async function prepareCrmRecordEmail(\n request: PluginRecordEmailPrepareRequest,\n): Promise<PluginRecordPreparedEmail | null> {\n const orgId = String(request.orgId ?? '').trim()\n const hostId = String(request.hostId ?? '').trim()\n if (!orgId || !hostId) return null\n const address = normalizeContactEmail(request.to)\n if (!address) return null\n try {\n const firestore = firebaseAdmin.app().firestore()\n const orgRef = firestore.collection('orgs').doc(orgId)\n // The org the caller already read, or the one read here once.\n const org =\n request.org !== undefined ? request.org : ((await orgRef.get()).data() ?? null)\n if (!checkEntitlement(org as Parameters<typeof checkEntitlement>[0], CRM_SUITE_FEATURE)) {\n return null\n }\n const contact = await resolveEventContact(hostId, request.link)\n if (!contact || normalizeContactEmail(contact.data['email']) !== address) {\n return null\n }\n const group = await consentGroupForSite(hostId)\n const facet = readContactFacet(contact.data, group.groupId)\n const link: CrmActivityLink = {\n contactId: contact.id,\n ...(facet.companyId ? { companyId: facet.companyId } : {}),\n }\n if (!crmActivityLogHasRoom(await countCrmActivitiesForRecord(orgRef, link))) {\n return null\n }\n const ref = newCrmActivityRef(firestore, orgId)\n const visibleTo = crmScopeTokens((org ?? null) as Record<string, unknown> | null, group)\n return {\n tags: crmEmailDeliveryTags({ orgId, hostId, activityId: ref.id }),\n /*\n * The same shape the console route logs — `buildCrmEmailActivity` is the\n * one builder — with the sender's id as its source and nobody as its\n * author. Never throws: the message has left, and a row that could not\n * be written is a gap on the timeline rather than a failed send.\n */\n async file(sent) {\n try {\n await writeCrmEmailActivity(\n ref,\n buildCrmEmailActivity({\n subject: sent.subject,\n body: sent.body,\n to: normalizeContactEmail(sent.to) ?? sent.to,\n atMs: sent.atMs ?? Date.now(),\n byUid: '',\n sourceActionId: sent.sourceRef,\n link,\n hostId,\n visibleTo,\n }),\n )\n } catch (error) {\n console.error('[crm] automation email activity write failed', ref.id, error)\n }\n },\n }\n } catch (error) {\n console.error('[crm] automation email activity could not be prepared', hostId, error)\n return null\n }\n}\n"],"names":["buildCrmEmailActivity","checkEntitlement","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_TAG_MAX_LENGTH","contactFacetPath","CRM_ACTIVITY_LOG_FULL_MESSAGE","CRM_COLLECTIONS","crmActivityLogHasRoom","crmActivityDirection","crmEmailDeliveryTags","crmScopeTokens","crmTaskLabelsForNew","crmTaskListFields","crmTaskReminderAfterEdit","normalizeContactEmail","parseCrmMemberRef","planLabelGrantingFeature","readContactFacet","visibleToHost","consentGroupForSite","countCrmActivitiesForRecord","firebaseAdmin","newCrmActivityRef","orgDataQueryForHost","recomputeCrmNextTaskAt","restampCrmListFieldsAt","writeCrmEmailActivity","findContactByEmail","FieldValue","OWNER_ASSIGNMENT_REFUSALS","reassignContactOwner","readCrmTaskPicklists","CRM_SUITE_FEATURE","DAY_MS","resolveEventContact","hostId","named","hit","ref","contactsRef","contactId","String","trim","doc","get","exists","id","data","email","resolveMemberUid","orgId","role","uid","error","members","app","firestore","collection","kind","member","detail","byField","where","limit","docs","runCrmAutomationStep","request","nowMs","Date","now","contact","env","org","step","actionId","run","payload","group","facet","groupId","type","previous","lifecycleStage","label","update","updatedAt","serverTimestamp","emit","event","previousStage","tag","slice","arrayUnion","assign","roundRobin","owner","ownerUid","ownerEmail","memberUid","verdict","outcome","reason","orgRef","visibleTo","links","companyId","title","dueInDays","Math","max","round","Number","assignee","assigneeUid","assigneeEmail","dueAtMs","priority","labels","task","status","remindAtMs","createdByUid","sourceActionId","createdAt","tasks","add","catch","console","body","logged","direction","activity","atMs","byUid","activities","prepareCrmRecordEmail","address","to","undefined","link","tags","activityId","file","sent","subject","sourceRef"],"mappings":"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"}