@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
@@ -1,182 +0,0 @@
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
- * WHAT EVERY CSV IMPORT ROUTE ASKS BEFORE IT READS A ROW (AGL-2602, shared
18
- * by the companies import since AGL-2621).
19
- *
20
- * Who is asking and on whose behalf, whether the request is the size a
21
- * chunk is allowed to be, and — when a row names an owner — which member
22
- * of the org that address is. The contacts route settled each answer; the
23
- * companies route needs the same three and must not spell them
24
- * differently, because two gates for one permission is how one of them
25
- * comes to be wider.
26
- *
27
- * ## Who may call an import
28
- *
29
- * The same two gates the rules put on every CRM collection: a site role
30
- * on the host the request names, AND the organization's `data.manage`
31
- * permission resolved through the member's custom role and overrides. The
32
- * Admin SDK evaluates no rules, so this is the enforcement rather than an
33
- * echo of it — and `data.manage` rather than the raw role because an owner
34
- * who unticked "Manage data" on a custom role meant it. A staff token
35
- * passes both gates the way it does on every console route: support
36
- * importing a customer's file for them is the support act this exists for.
37
- */ import { consentGroupScope, crmScopeTokens, ORG_SCOPE_TOKEN } from "@aglyn/aglyn/server";
38
- import { consentGroupForSite, firebaseAdmin, getOrgForHost, listOrgMembers, memberHasOrgPermission, resolveOrgMembership } from "@aglyn/tenant-data-admin";
39
- import { crmSuiteRefusal } from "./suite-gate.js";
40
- /** Who is asking, and on whose behalf. */ export async function resolveImportContext(req) {
41
- var _ref, _req_headers_authorization, _hostSnapshot_get, _ref1, _ref2;
42
- var _req_body;
43
- const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
44
- if (!hostId) {
45
- return {
46
- ok: false,
47
- status: 400,
48
- body: {
49
- error: 'Missing hostId'
50
- }
51
- };
52
- }
53
- const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
54
- const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
55
- if (!idToken) {
56
- return {
57
- ok: false,
58
- status: 401,
59
- body: {
60
- error: 'Unauthenticated'
61
- }
62
- };
63
- }
64
- const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
65
- const staff = decoded['staff'] === true;
66
- const firestore = firebaseAdmin.app().firestore();
67
- const hostSnapshot = await firestore.collection('hosts').doc(hostId).get();
68
- if (!hostSnapshot.exists) {
69
- return {
70
- ok: false,
71
- status: 404,
72
- body: {
73
- error: 'Unknown site'
74
- }
75
- };
76
- }
77
- const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
78
- if (!staff && memberRole !== 'admin' && memberRole !== 'editor') {
79
- return {
80
- ok: false,
81
- status: 403,
82
- body: {
83
- error: 'Not a site admin or editor'
84
- }
85
- };
86
- }
87
- const resolved = await getOrgForHost(hostId).catch(()=>null);
88
- const orgId = String((_ref1 = resolved == null ? void 0 : resolved.orgId) != null ? _ref1 : '');
89
- if (!orgId) {
90
- return {
91
- ok: false,
92
- status: 404,
93
- body: {
94
- error: 'This site has no organization, so it has no CRM.'
95
- }
96
- };
97
- }
98
- const org = (_ref2 = resolved == null ? void 0 : resolved.org) != null ? _ref2 : {};
99
- const membership = await resolveOrgMembership(decoded.uid, orgId).catch(()=>null);
100
- const member = membership == null ? void 0 : membership.member;
101
- if (!staff && (!member || member.orgSuspended === true || !await memberHasOrgPermission(orgId, member, 'data.manage'))) {
102
- return {
103
- ok: false,
104
- status: 403,
105
- body: {
106
- error: 'Your organization role does not allow managing contacts and ' + 'companies, so it cannot import them.'
107
- }
108
- };
109
- }
110
- // Every file this context serves — contacts, companies, deals, tasks and
111
- // leads — is the CRM's to import, whoever is asking (AGL-2787).
112
- const suite = crmSuiteRefusal(org, 'Importing from a CSV file');
113
- if (suite) return {
114
- ok: false,
115
- status: suite.status,
116
- body: suite.body
117
- };
118
- const group = await consentGroupForSite(hostId);
119
- return {
120
- ok: true,
121
- uid: decoded.uid,
122
- email: typeof decoded.email === 'string' ? decoded.email : null,
123
- hostId,
124
- orgId,
125
- org,
126
- scopeTokens: crmScopeTokens(org, group),
127
- readTokens: [
128
- ORG_SCOPE_TOKEN,
129
- ...consentGroupScope(group)
130
- ]
131
- };
132
- }
133
- /** The rows a request may carry, or the refusal to send back. */ export function readImportRows(req, limits) {
134
- var _ref;
135
- var _req_rawBody, _req_body;
136
- if (((_ref = (_req_rawBody = req.rawBody) == null ? void 0 : _req_rawBody.length) != null ? _ref : 0) > limits.maxBodyBytes) {
137
- return {
138
- status: 413,
139
- error: 'That request is too large. Import the file in smaller pieces.'
140
- };
141
- }
142
- const rows = (_req_body = req.body) == null ? void 0 : _req_body.rows;
143
- if (!Array.isArray(rows) || rows.length === 0) {
144
- return {
145
- status: 400,
146
- error: 'No rows to import.'
147
- };
148
- }
149
- if (rows.length > limits.chunkSize) {
150
- return {
151
- status: 400,
152
- error: `At most ${limits.chunkSize} rows per request.`
153
- };
154
- }
155
- if (!rows.every((row)=>row && typeof row === 'object' && !Array.isArray(row))) {
156
- return {
157
- status: 400,
158
- error: 'Every row must be an object.'
159
- };
160
- }
161
- return {
162
- rows: rows
163
- };
164
- }
165
- /**
166
- * The org's members by address, read only when a row names an owner.
167
- *
168
- * A file with no owner column costs no roster read; a file with one costs
169
- * exactly one, however many rows it has.
170
- */ export async function ownerDirectory(orgId, rows) {
171
- const directory = new Map();
172
- if (!rows.some((row)=>row.ownerEmail)) return directory;
173
- const members = await listOrgMembers(orgId);
174
- for (const member of members){
175
- var _member_email;
176
- const email = String((_member_email = member.email) != null ? _member_email : '').trim().toLowerCase();
177
- if (email && member.$id) directory.set(email, String(member.$id));
178
- }
179
- return directory;
180
- }
181
-
182
- //# sourceMappingURL=import-context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/import-context.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 * WHAT EVERY CSV IMPORT ROUTE ASKS BEFORE IT READS A ROW (AGL-2602, shared\n * by the companies import since AGL-2621).\n *\n * Who is asking and on whose behalf, whether the request is the size a\n * chunk is allowed to be, and — when a row names an owner — which member\n * of the org that address is. The contacts route settled each answer; the\n * companies route needs the same three and must not spell them\n * differently, because two gates for one permission is how one of them\n * comes to be wider.\n *\n * ## Who may call an import\n *\n * The same two gates the rules put on every CRM collection: a site role\n * on the host the request names, AND the organization's `data.manage`\n * permission resolved through the member's custom role and overrides. The\n * Admin SDK evaluates no rules, so this is the enforcement rather than an\n * echo of it — and `data.manage` rather than the raw role because an owner\n * who unticked \"Manage data\" on a custom role meant it. A staff token\n * passes both gates the way it does on every console route: support\n * importing a customer's file for them is the support act this exists for.\n */\n\nimport {\n consentGroupScope,\n crmScopeTokens,\n ORG_SCOPE_TOKEN,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport {\n consentGroupForSite,\n firebaseAdmin,\n getOrgForHost,\n listOrgMembers,\n memberHasOrgPermission,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport { crmSuiteRefusal } from './suite-gate'\n\n/** Everything an import needs about who is asking, or the refusal to send back. */\nexport type ImportContext =\n | {\n ok: true\n uid: string\n /** The importer's address, for who set off the runs it causes (AGL-3376). */\n email: string | null\n hostId: string\n orgId: string\n org: Record<string, unknown>\n /**\n * The stamp a record CREATED by this import carries — the org, when\n * the org widened its default, and otherwise the capturing group.\n */\n scopeTokens: string[]\n /**\n * The tokens a record must overlap to be SEEN from this site: the\n * group's own, plus `org`, because an org-wide record is visible to\n * every site in the account. The reader's set is wider than the\n * creator's stamp, exactly as it is on the collection listeners.\n */\n readTokens: string[]\n }\n | { ok: false; status: number; body: Record<string, unknown> }\n\nexport type ImportRequest = Parameters<PluginApiHandler>[0]\n\n/** Who is asking, and on whose behalf. */\nexport async function resolveImportContext(req: ImportRequest): Promise<ImportContext> {\n const hostId = String(req.body?.hostId ?? '')\n if (!hostId) {\n return { ok: false, status: 400, body: { error: 'Missing hostId' } }\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) {\n return { ok: false, status: 401, body: { error: 'Unauthenticated' } }\n }\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const staff = decoded['staff'] === true\n const firestore = firebaseAdmin.app().firestore()\n const hostSnapshot = await firestore.collection('hosts').doc(hostId).get()\n if (!hostSnapshot.exists) {\n return { ok: false, status: 404, body: { error: 'Unknown site' } }\n }\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (!staff && memberRole !== 'admin' && memberRole !== 'editor') {\n return {\n ok: false,\n status: 403,\n body: { error: 'Not a site admin or editor' },\n }\n }\n const resolved = await getOrgForHost(hostId).catch(() => null)\n const orgId = String(resolved?.orgId ?? '')\n if (!orgId) {\n return {\n ok: false,\n status: 404,\n body: { error: 'This site has no organization, so it has no CRM.' },\n }\n }\n const org = (resolved?.org ?? {}) as Record<string, unknown>\n const membership = await resolveOrgMembership(decoded.uid, orgId).catch(\n () => null,\n )\n const member = membership?.member\n if (\n !staff &&\n (!member ||\n (member as { orgSuspended?: boolean }).orgSuspended === true ||\n !(await memberHasOrgPermission(orgId, member, 'data.manage')))\n ) {\n return {\n ok: false,\n status: 403,\n body: {\n error:\n 'Your organization role does not allow managing contacts and ' +\n 'companies, so it cannot import them.',\n },\n }\n }\n // Every file this context serves — contacts, companies, deals, tasks and\n // leads — is the CRM's to import, whoever is asking (AGL-2787).\n const suite = crmSuiteRefusal(org, 'Importing from a CSV file')\n if (suite) return { ok: false, status: suite.status, body: suite.body }\n const group = await consentGroupForSite(hostId)\n return {\n ok: true,\n uid: decoded.uid,\n email: typeof decoded.email === 'string' ? decoded.email : null,\n hostId,\n orgId,\n org,\n scopeTokens: crmScopeTokens(org, group),\n readTokens: [ORG_SCOPE_TOKEN, ...consentGroupScope(group)],\n }\n}\n\n/** The rows a request may carry, or the refusal to send back. */\nexport function readImportRows<R>(\n req: ImportRequest,\n limits: { maxBodyBytes: number; chunkSize: number },\n): { rows: R[] } | { status: number; error: string } {\n if ((req.rawBody?.length ?? 0) > limits.maxBodyBytes) {\n return {\n status: 413,\n error: 'That request is too large. Import the file in smaller pieces.',\n }\n }\n const rows = req.body?.rows\n if (!Array.isArray(rows) || rows.length === 0) {\n return { status: 400, error: 'No rows to import.' }\n }\n if (rows.length > limits.chunkSize) {\n return {\n status: 400,\n error: `At most ${limits.chunkSize} rows per request.`,\n }\n }\n if (!rows.every((row) => row && typeof row === 'object' && !Array.isArray(row))) {\n return { status: 400, error: 'Every row must be an object.' }\n }\n return { rows: rows as R[] }\n}\n\n/**\n * The org's members by address, read only when a row names an owner.\n *\n * A file with no owner column costs no roster read; a file with one costs\n * exactly one, however many rows it has.\n */\nexport async function ownerDirectory(\n orgId: string,\n rows: readonly { ownerEmail?: string }[],\n): Promise<Map<string, string>> {\n const directory = new Map<string, string>()\n if (!rows.some((row) => row.ownerEmail)) return directory\n const members = await listOrgMembers(orgId)\n for (const member of members) {\n const email = String(member.email ?? '')\n .trim()\n .toLowerCase()\n if (email && member.$id) directory.set(email, String(member.$id))\n }\n return directory\n}\n"],"names":["consentGroupScope","crmScopeTokens","ORG_SCOPE_TOKEN","consentGroupForSite","firebaseAdmin","getOrgForHost","listOrgMembers","memberHasOrgPermission","resolveOrgMembership","crmSuiteRefusal","resolveImportContext","req","hostSnapshot","hostId","String","body","ok","status","error","authorization","headers","idToken","startsWith","slice","length","undefined","decoded","app","auth","verifyIdToken","staff","firestore","collection","doc","get","exists","memberRole","uid","resolved","catch","orgId","org","membership","member","orgSuspended","suite","group","email","scopeTokens","readTokens","readImportRows","limits","rawBody","maxBodyBytes","rows","Array","isArray","chunkSize","every","row","ownerDirectory","directory","Map","some","ownerEmail","members","trim","toLowerCase","$id","set"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;CAqBC,GAED,SACEA,iBAAiB,EACjBC,cAAc,EACdC,eAAe,QAEV,sBAAqB;AAC5B,SACEC,mBAAmB,EACnBC,aAAa,EACbC,aAAa,EACbC,cAAc,EACdC,sBAAsB,EACtBC,oBAAoB,QACf,2BAA0B;AACjC,SAASC,eAAe,QAAQ,kBAAc;AA6B9C,wCAAwC,GACxC,OAAO,eAAeC,qBAAqBC,GAAkB;cAK9BA,4BAcTC;QAlBED;IAAtB,MAAME,SAASC,gBAAOH,YAAAA,IAAII,IAAI,qBAARJ,UAAUE,MAAM,mBAAI;IAC1C,IAAI,CAACA,QAAQ;QACX,OAAO;YAAEG,IAAI;YAAOC,QAAQ;YAAKF,MAAM;gBAAEG,OAAO;YAAiB;QAAE;IACrE;IACA,MAAMC,gBAAgBL,QAAOH,6BAAAA,IAAIS,OAAO,CAACD,aAAa,YAAzBR,6BAA6B;IAC1D,MAAMU,UAAUF,cAAcG,UAAU,CAAC,aACrCH,cAAcI,KAAK,CAAC,UAAUC,MAAM,IACpCC;IACJ,IAAI,CAACJ,SAAS;QACZ,OAAO;YAAEL,IAAI;YAAOC,QAAQ;YAAKF,MAAM;gBAAEG,OAAO;YAAkB;QAAE;IACtE;IACA,MAAMQ,UAAU,MAAMtB,cAAcuB,GAAG,GAAGC,IAAI,GAAGC,aAAa,CAACR;IAC/D,MAAMS,QAAQJ,OAAO,CAAC,QAAQ,KAAK;IACnC,MAAMK,YAAY3B,cAAcuB,GAAG,GAAGI,SAAS;IAC/C,MAAMnB,eAAe,MAAMmB,UAAUC,UAAU,CAAC,SAASC,GAAG,CAACpB,QAAQqB,GAAG;IACxE,IAAI,CAACtB,aAAauB,MAAM,EAAE;QACxB,OAAO;YAAEnB,IAAI;YAAOC,QAAQ;YAAKF,MAAM;gBAAEG,OAAO;YAAe;QAAE;IACnE;IACA,MAAMkB,aAAa,EAACxB,oBAAAA,aAAasB,GAAG,CAAC,0BAAjBtB,oBAAmC,CAAC,EAAE,CAACc,QAAQW,GAAG,CAAC;IACvE,IAAI,CAACP,SAASM,eAAe,WAAWA,eAAe,UAAU;QAC/D,OAAO;YACLpB,IAAI;YACJC,QAAQ;YACRF,MAAM;gBAAEG,OAAO;YAA6B;QAC9C;IACF;IACA,MAAMoB,WAAW,MAAMjC,cAAcQ,QAAQ0B,KAAK,CAAC,IAAM;IACzD,MAAMC,QAAQ1B,gBAAOwB,4BAAAA,SAAUE,KAAK,oBAAI;IACxC,IAAI,CAACA,OAAO;QACV,OAAO;YACLxB,IAAI;YACJC,QAAQ;YACRF,MAAM;gBAAEG,OAAO;YAAmD;QACpE;IACF;IACA,MAAMuB,eAAOH,4BAAAA,SAAUG,GAAG,oBAAI,CAAC;IAC/B,MAAMC,aAAa,MAAMlC,qBAAqBkB,QAAQW,GAAG,EAAEG,OAAOD,KAAK,CACrE,IAAM;IAER,MAAMI,SAASD,8BAAAA,WAAYC,MAAM;IACjC,IACE,CAACb,SACA,CAAA,CAACa,UACA,AAACA,OAAsCC,YAAY,KAAK,QACxD,CAAE,MAAMrC,uBAAuBiC,OAAOG,QAAQ,cAAc,GAC9D;QACA,OAAO;YACL3B,IAAI;YACJC,QAAQ;YACRF,MAAM;gBACJG,OACE,iEACA;YACJ;QACF;IACF;IACA,yEAAyE;IACzE,gEAAgE;IAChE,MAAM2B,QAAQpC,gBAAgBgC,KAAK;IACnC,IAAII,OAAO,OAAO;QAAE7B,IAAI;QAAOC,QAAQ4B,MAAM5B,MAAM;QAAEF,MAAM8B,MAAM9B,IAAI;IAAC;IACtE,MAAM+B,QAAQ,MAAM3C,oBAAoBU;IACxC,OAAO;QACLG,IAAI;QACJqB,KAAKX,QAAQW,GAAG;QAChBU,OAAO,OAAOrB,QAAQqB,KAAK,KAAK,WAAWrB,QAAQqB,KAAK,GAAG;QAC3DlC;QACA2B;QACAC;QACAO,aAAa/C,eAAewC,KAAKK;QACjCG,YAAY;YAAC/C;eAAoBF,kBAAkB8C;SAAO;IAC5D;AACF;AAEA,+DAA+D,GAC/D,OAAO,SAASI,eACdvC,GAAkB,EAClBwC,MAAmD;;QAE9CxC,cAMQA;IANb,IAAI,UAACA,eAAAA,IAAIyC,OAAO,qBAAXzC,aAAaa,MAAM,mBAAI,KAAK2B,OAAOE,YAAY,EAAE;QACpD,OAAO;YACLpC,QAAQ;YACRC,OAAO;QACT;IACF;IACA,MAAMoC,QAAO3C,YAAAA,IAAII,IAAI,qBAARJ,UAAU2C,IAAI;IAC3B,IAAI,CAACC,MAAMC,OAAO,CAACF,SAASA,KAAK9B,MAAM,KAAK,GAAG;QAC7C,OAAO;YAAEP,QAAQ;YAAKC,OAAO;QAAqB;IACpD;IACA,IAAIoC,KAAK9B,MAAM,GAAG2B,OAAOM,SAAS,EAAE;QAClC,OAAO;YACLxC,QAAQ;YACRC,OAAO,CAAC,QAAQ,EAAEiC,OAAOM,SAAS,CAAC,kBAAkB,CAAC;QACxD;IACF;IACA,IAAI,CAACH,KAAKI,KAAK,CAAC,CAACC,MAAQA,OAAO,OAAOA,QAAQ,YAAY,CAACJ,MAAMC,OAAO,CAACG,OAAO;QAC/E,OAAO;YAAE1C,QAAQ;YAAKC,OAAO;QAA+B;IAC9D;IACA,OAAO;QAAEoC,MAAMA;IAAY;AAC7B;AAEA;;;;;CAKC,GACD,OAAO,eAAeM,eACpBpB,KAAa,EACbc,IAAwC;IAExC,MAAMO,YAAY,IAAIC;IACtB,IAAI,CAACR,KAAKS,IAAI,CAAC,CAACJ,MAAQA,IAAIK,UAAU,GAAG,OAAOH;IAChD,MAAMI,UAAU,MAAM3D,eAAekC;IACrC,KAAK,MAAMG,UAAUsB,QAAS;YACPtB;QAArB,MAAMI,QAAQjC,QAAO6B,gBAAAA,OAAOI,KAAK,YAAZJ,gBAAgB,IAClCuB,IAAI,GACJC,WAAW;QACd,IAAIpB,SAASJ,OAAOyB,GAAG,EAAEP,UAAUQ,GAAG,CAACtB,OAAOjC,OAAO6B,OAAOyB,GAAG;IACjE;IACA,OAAOP;AACT"}
@@ -1,50 +0,0 @@
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 PluginApiHandler } from '@aglyn/aglyn/server';
18
- /**
19
- * `POST crm/lead-source-values` — rename a lead source value, or delete one
20
- * and move its records to another (AGL-3298).
21
- *
22
- * Body: `{ hostId | orgId, action: 'rename', valueId, label }` or
23
- * `{ hostId | orgId, action: 'delete', valueId, replaceWith: label | null }`.
24
- *
25
- * Records store the value's LABEL (see the picklist block in `crm.ts`), so
26
- * both moves change records as well as the list: every lead and every
27
- * contact holding the old label is rewritten to the new one — or, for a
28
- * delete with no replacement, cleared. That is Salesforce's Replace, done
29
- * as part of the move, so a report grouped by lead source follows a rename
30
- * instead of splitting into the old name and the new.
31
- *
32
- * ## The list first, then the records
33
- *
34
- * The list is written in a transaction before any record: from that moment
35
- * no picker offers the old label and no import or API write accepts it, so
36
- * the sweep that follows cannot be outrun by a new record taking the name
37
- * it is removing. A sweep interrupted part-way leaves records holding a
38
- * label the list no longer has, which they show as "(not in the list)"; the
39
- * same request sent again finishes it, because the rename of a value to its
40
- * own current label is a no-op on the list and still sweeps.
41
- *
42
- * ## Who may
43
- *
44
- * Whoever may write the CRM — `authorizeCrmWriter`, the question every CRM
45
- * route asks — which is who the rules let edit the list itself. The sweep
46
- * reaches every lead and contact in the org whatever the caller's scope:
47
- * it changes one label to another that the same caller could already have
48
- * picked, and reveals nothing about the records but their count.
49
- */
50
- export declare const crmLeadSourceValuesHandler: PluginApiHandler;
@@ -1,191 +0,0 @@
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 { consentGroupForHost, CONTACT_FACETS_FIELD, CRM_COLLECTIONS, CRM_LEAD_SOURCE_KEY_FIELD, CRM_LEAD_SOURCE_PICKLIST, crmLeadSourceKey, effectiveCrmLeadSourcePicklist, newResourceScopeFields, normalizeCrmPicklist, ORG_SCOPE_TOKEN } from "@aglyn/aglyn/server";
18
- import { firebaseAdmin } from "@aglyn/tenant-data-admin";
19
- import { FieldPath, FieldValue } from "firebase-admin/firestore";
20
- import { deletePicklistValue, renamePicklistValue } from "../model/lead-source-values.js";
21
- import { orgHostIds, readCrmRouteScope } from "./org-caller.js";
22
- import { authorizeCrmWriter } from "./task-routes.js";
23
- /** What the suite gate names when a plan without the CRM asks. */ const SUITE_ACT = "Editing the organization's lead sources";
24
- /** Writes per batch — under Firestore's 500, with room to spare. */ const BATCH_SIZE = 400;
25
- /**
26
- * Set `field` to `to` (or remove it, for `null`) on every document `query`
27
- * finds holding `from`, a batch at a time, with `beside` written alongside.
28
- * Answers how many were written.
29
- *
30
- * The query is re-run after each batch rather than paged, because each
31
- * batch moves its documents OUT of the query's answer — the next run finds
32
- * only what is left, and a run that finds nothing is the end.
33
- */ async function replaceEverywhere(firestore, query, field, to, beside = {}) {
34
- let written = 0;
35
- for(;;){
36
- const page = await query.limit(BATCH_SIZE).get();
37
- if (page.empty) return written;
38
- const batch = firestore.batch();
39
- for (const snapshot of page.docs){
40
- batch.update(snapshot.ref, field, to != null ? to : FieldValue.delete(), 'updatedAt', FieldValue.serverTimestamp(), ...Object.entries(beside).flat());
41
- }
42
- await batch.commit();
43
- written += page.size;
44
- if (page.size < BATCH_SIZE) return written;
45
- }
46
- }
47
- /**
48
- * `POST crm/lead-source-values` — rename a lead source value, or delete one
49
- * and move its records to another (AGL-3298).
50
- *
51
- * Body: `{ hostId | orgId, action: 'rename', valueId, label }` or
52
- * `{ hostId | orgId, action: 'delete', valueId, replaceWith: label | null }`.
53
- *
54
- * Records store the value's LABEL (see the picklist block in `crm.ts`), so
55
- * both moves change records as well as the list: every lead and every
56
- * contact holding the old label is rewritten to the new one — or, for a
57
- * delete with no replacement, cleared. That is Salesforce's Replace, done
58
- * as part of the move, so a report grouped by lead source follows a rename
59
- * instead of splitting into the old name and the new.
60
- *
61
- * ## The list first, then the records
62
- *
63
- * The list is written in a transaction before any record: from that moment
64
- * no picker offers the old label and no import or API write accepts it, so
65
- * the sweep that follows cannot be outrun by a new record taking the name
66
- * it is removing. A sweep interrupted part-way leaves records holding a
67
- * label the list no longer has, which they show as "(not in the list)"; the
68
- * same request sent again finishes it, because the rename of a value to its
69
- * own current label is a no-op on the list and still sweeps.
70
- *
71
- * ## Who may
72
- *
73
- * Whoever may write the CRM — `authorizeCrmWriter`, the question every CRM
74
- * route asks — which is who the rules let edit the list itself. The sweep
75
- * reaches every lead and contact in the org whatever the caller's scope:
76
- * it changes one label to another that the same caller could already have
77
- * picked, and reveals nothing about the records but their count.
78
- */ export const crmLeadSourceValuesHandler = async (req, res)=>{
79
- var _req_body, _body_valueId;
80
- if (req.method !== 'POST') {
81
- res.setHeader('Allow', 'POST');
82
- res.status(405).json({
83
- error: 'Method not allowed'
84
- });
85
- return;
86
- }
87
- const body = (_req_body = req.body) != null ? _req_body : {};
88
- const scope = readCrmRouteScope(body);
89
- if (!scope) {
90
- res.status(400).json({
91
- error: 'Missing hostId'
92
- });
93
- return;
94
- }
95
- const action = body['action'];
96
- const valueId = String((_body_valueId = body['valueId']) != null ? _body_valueId : '').trim().slice(0, 64);
97
- if (action !== 'rename' && action !== 'delete' || !valueId) {
98
- res.status(400).json({
99
- error: 'Name a value, and whether to rename or delete it.'
100
- });
101
- return;
102
- }
103
- const replaceWith = body['replaceWith'] === null || body['replaceWith'] === undefined ? null : String(body['replaceWith']);
104
- try {
105
- const writer = await authorizeCrmWriter(req, scope, {
106
- suiteAct: SUITE_ACT
107
- });
108
- if (writer.ok === false) {
109
- res.status(writer.status).json(writer.body);
110
- return;
111
- }
112
- const firestore = firebaseAdmin.app().firestore();
113
- const orgRef = firestore.collection('orgs').doc(writer.orgId);
114
- const listRef = orgRef.collection(CRM_COLLECTIONS.picklists).doc(CRM_LEAD_SOURCE_PICKLIST);
115
- /*
116
- * THE LIST. Read and written in one transaction, so two admins editing
117
- * at once cannot each write a list missing the other's change.
118
- */ const moved = await firestore.runTransaction(async (transaction)=>{
119
- var _body_label, _ref, _ref1;
120
- var _move_picklist_values_find, _move_picklist_values_find1;
121
- const snapshot = await transaction.get(listRef);
122
- const before = effectiveCrmLeadSourcePicklist(snapshot.data());
123
- const value = before.values.find((entry)=>entry.id === valueId);
124
- if (!value) return {
125
- ok: false,
126
- error: 'That value is no longer in the list.'
127
- };
128
- const move = action === 'rename' ? renamePicklistValue(before, valueId, String((_body_label = body['label']) != null ? _body_label : '')) : deletePicklistValue(before, valueId, replaceWith);
129
- if (move.ok === false) return move;
130
- const created = normalizeCrmPicklist(snapshot.data()) === null;
131
- transaction.set(listRef, _extends({
132
- values: move.picklist.values,
133
- defaultValueId: move.picklist.defaultValueId,
134
- updatedAt: FieldValue.serverTimestamp()
135
- }, created ? _extends({
136
- hostId: scope.hostId || null,
137
- createdAt: FieldValue.serverTimestamp()
138
- }, newResourceScopeFields([
139
- ORG_SCOPE_TOKEN
140
- ])) : {}), {
141
- merge: true
142
- });
143
- const to = action === 'rename' ? (_ref = (_move_picklist_values_find = move.picklist.values.find((entry)=>entry.id === valueId)) == null ? void 0 : _move_picklist_values_find.label) != null ? _ref : null : replaceWith === null ? null : (_ref1 = (_move_picklist_values_find1 = move.picklist.values.find((entry)=>entry.label.toLowerCase() === replaceWith.trim().toLowerCase())) == null ? void 0 : _move_picklist_values_find1.label) != null ? _ref1 : null;
144
- return {
145
- ok: true,
146
- from: value.label,
147
- to
148
- };
149
- });
150
- if (moved.ok === false) {
151
- res.status(400).json({
152
- error: moved.error
153
- });
154
- return;
155
- }
156
- /*
157
- * THE RECORDS. Leads by the field itself; contacts by each holder's
158
- * facet, one query per consent group the org's sites belong to — a
159
- * contact's lead source is the holder's own, like the rest of its
160
- * profile. A rename to the same label moves nothing, and says so.
161
- */ let leads = 0;
162
- let contacts = 0;
163
- if (moved.from !== moved.to) {
164
- leads = await replaceEverywhere(firestore, orgRef.collection('leads').where('leadSource', '==', moved.from), 'leadSource', moved.to, // The key the Leads list filters by moves with the label (AGL-3321).
165
- {
166
- [CRM_LEAD_SOURCE_KEY_FIELD]: crmLeadSourceKey(moved.to)
167
- });
168
- const hostIds = await orgHostIds(firestore, writer.orgId);
169
- const groupIds = [
170
- ...new Set(hostIds.map((hostId)=>consentGroupForHost(writer.org, hostId).groupId))
171
- ].filter(Boolean);
172
- for (const groupId of groupIds){
173
- const path = new FieldPath(CONTACT_FACETS_FIELD, groupId, 'leadSource');
174
- contacts += await replaceEverywhere(firestore, orgRef.collection('contacts').where(path, '==', moved.from), path, moved.to);
175
- }
176
- }
177
- const answer = {
178
- ok: true,
179
- leads,
180
- contacts
181
- };
182
- res.status(200).json(answer);
183
- } catch (error) {
184
- console.error('[crm] lead-source-values failed', error);
185
- res.status(500).json({
186
- error: 'The lead sources could not be saved.'
187
- });
188
- }
189
- };
190
-
191
- //# sourceMappingURL=lead-source-values.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/lead-source-values.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 consentGroupForHost,\n CONTACT_FACETS_FIELD,\n CRM_COLLECTIONS,\n CRM_LEAD_SOURCE_KEY_FIELD,\n CRM_LEAD_SOURCE_PICKLIST,\n crmLeadSourceKey,\n type CrmPicklist,\n effectiveCrmLeadSourcePicklist,\n newResourceScopeFields,\n normalizeCrmPicklist,\n ORG_SCOPE_TOKEN,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { FieldPath, FieldValue } from 'firebase-admin/firestore'\nimport {\n deletePicklistValue,\n type LeadSourceValuesResponse,\n type PicklistMove,\n renamePicklistValue,\n} from '../model/lead-source-values'\nimport { orgHostIds, readCrmRouteScope } from './org-caller'\nimport { authorizeCrmWriter } from './task-routes'\n\n/** What the suite gate names when a plan without the CRM asks. */\nconst SUITE_ACT = \"Editing the organization's lead sources\"\n\n/** Writes per batch — under Firestore's 500, with room to spare. */\nconst BATCH_SIZE = 400\n\n/**\n * Set `field` to `to` (or remove it, for `null`) on every document `query`\n * finds holding `from`, a batch at a time, with `beside` written alongside.\n * Answers how many were written.\n *\n * The query is re-run after each batch rather than paged, because each\n * batch moves its documents OUT of the query's answer — the next run finds\n * only what is left, and a run that finds nothing is the end.\n */\nasync function replaceEverywhere(\n firestore: FirebaseFirestore.Firestore,\n query: FirebaseFirestore.Query,\n field: string | FirebaseFirestore.FieldPath,\n to: string | null,\n beside: Record<string, unknown> = {},\n): Promise<number> {\n let written = 0\n for (;;) {\n const page = await query.limit(BATCH_SIZE).get()\n if (page.empty) return written\n const batch = firestore.batch()\n for (const snapshot of page.docs) {\n batch.update(\n snapshot.ref,\n field,\n to ?? FieldValue.delete(),\n 'updatedAt',\n FieldValue.serverTimestamp(),\n ...Object.entries(beside).flat(),\n )\n }\n await batch.commit()\n written += page.size\n if (page.size < BATCH_SIZE) return written\n }\n}\n\n/**\n * `POST crm/lead-source-values` — rename a lead source value, or delete one\n * and move its records to another (AGL-3298).\n *\n * Body: `{ hostId | orgId, action: 'rename', valueId, label }` or\n * `{ hostId | orgId, action: 'delete', valueId, replaceWith: label | null }`.\n *\n * Records store the value's LABEL (see the picklist block in `crm.ts`), so\n * both moves change records as well as the list: every lead and every\n * contact holding the old label is rewritten to the new one — or, for a\n * delete with no replacement, cleared. That is Salesforce's Replace, done\n * as part of the move, so a report grouped by lead source follows a rename\n * instead of splitting into the old name and the new.\n *\n * ## The list first, then the records\n *\n * The list is written in a transaction before any record: from that moment\n * no picker offers the old label and no import or API write accepts it, so\n * the sweep that follows cannot be outrun by a new record taking the name\n * it is removing. A sweep interrupted part-way leaves records holding a\n * label the list no longer has, which they show as \"(not in the list)\"; the\n * same request sent again finishes it, because the rename of a value to its\n * own current label is a no-op on the list and still sweeps.\n *\n * ## Who may\n *\n * Whoever may write the CRM — `authorizeCrmWriter`, the question every CRM\n * route asks — which is who the rules let edit the list itself. The sweep\n * reaches every lead and contact in the org whatever the caller's scope:\n * it changes one label to another that the same caller could already have\n * picked, and reveals nothing about the records but their count.\n */\nexport const crmLeadSourceValuesHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const body = (req.body ?? {}) as Record<string, unknown>\n const scope = readCrmRouteScope(body)\n if (!scope) {\n res.status(400).json({ error: 'Missing hostId' })\n return\n }\n const action = body['action']\n const valueId = String(body['valueId'] ?? '').trim().slice(0, 64)\n if ((action !== 'rename' && action !== 'delete') || !valueId) {\n res.status(400).json({ error: 'Name a value, and whether to rename or delete it.' })\n return\n }\n const replaceWith =\n body['replaceWith'] === null || body['replaceWith'] === undefined\n ? null\n : String(body['replaceWith'])\n\n try {\n const writer = await authorizeCrmWriter(req, scope, { suiteAct: SUITE_ACT })\n if (writer.ok === false) {\n res.status(writer.status).json(writer.body)\n return\n }\n const firestore = firebaseAdmin.app().firestore()\n const orgRef = firestore.collection('orgs').doc(writer.orgId)\n const listRef = orgRef.collection(CRM_COLLECTIONS.picklists).doc(CRM_LEAD_SOURCE_PICKLIST)\n\n /*\n * THE LIST. Read and written in one transaction, so two admins editing\n * at once cannot each write a list missing the other's change.\n */\n const moved = await firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(listRef)\n const before: CrmPicklist = effectiveCrmLeadSourcePicklist(snapshot.data())\n const value = before.values.find((entry) => entry.id === valueId)\n if (!value) return { ok: false as const, error: 'That value is no longer in the list.' }\n const move: PicklistMove =\n action === 'rename'\n ? renamePicklistValue(before, valueId, String(body['label'] ?? ''))\n : deletePicklistValue(before, valueId, replaceWith)\n if (move.ok === false) return move\n const created = normalizeCrmPicklist(snapshot.data()) === null\n transaction.set(\n listRef,\n {\n values: move.picklist.values,\n defaultValueId: move.picklist.defaultValueId,\n updatedAt: FieldValue.serverTimestamp(),\n // The stamp every org-wide CRM document carries (see the Fields\n // page), written when this move is what creates the document.\n ...(created\n ? {\n hostId: scope.hostId || null,\n createdAt: FieldValue.serverTimestamp(),\n ...newResourceScopeFields([ORG_SCOPE_TOKEN]),\n }\n : {}),\n },\n { merge: true },\n )\n const to =\n action === 'rename'\n ? (move.picklist.values.find((entry) => entry.id === valueId)?.label ?? null)\n : replaceWith === null\n ? null\n : (move.picklist.values.find(\n (entry) => entry.label.toLowerCase() === replaceWith.trim().toLowerCase(),\n )?.label ?? null)\n return { ok: true as const, from: value.label, to }\n })\n if (moved.ok === false) {\n res.status(400).json({ error: moved.error })\n return\n }\n\n /*\n * THE RECORDS. Leads by the field itself; contacts by each holder's\n * facet, one query per consent group the org's sites belong to — a\n * contact's lead source is the holder's own, like the rest of its\n * profile. A rename to the same label moves nothing, and says so.\n */\n let leads = 0\n let contacts = 0\n if (moved.from !== moved.to) {\n leads = await replaceEverywhere(\n firestore,\n orgRef.collection('leads').where('leadSource', '==', moved.from),\n 'leadSource',\n moved.to,\n // The key the Leads list filters by moves with the label (AGL-3321).\n { [CRM_LEAD_SOURCE_KEY_FIELD]: crmLeadSourceKey(moved.to) },\n )\n const hostIds = await orgHostIds(firestore, writer.orgId)\n const groupIds = [\n ...new Set(hostIds.map((hostId) => consentGroupForHost(writer.org, hostId).groupId)),\n ].filter(Boolean)\n for (const groupId of groupIds) {\n const path = new FieldPath(CONTACT_FACETS_FIELD, groupId, 'leadSource')\n contacts += await replaceEverywhere(\n firestore,\n orgRef.collection('contacts').where(path, '==', moved.from),\n path,\n moved.to,\n )\n }\n }\n const answer: LeadSourceValuesResponse = { ok: true, leads, contacts }\n res.status(200).json(answer)\n } catch (error) {\n console.error('[crm] lead-source-values failed', error)\n res.status(500).json({ error: 'The lead sources could not be saved.' })\n }\n}\n"],"names":["consentGroupForHost","CONTACT_FACETS_FIELD","CRM_COLLECTIONS","CRM_LEAD_SOURCE_KEY_FIELD","CRM_LEAD_SOURCE_PICKLIST","crmLeadSourceKey","effectiveCrmLeadSourcePicklist","newResourceScopeFields","normalizeCrmPicklist","ORG_SCOPE_TOKEN","firebaseAdmin","FieldPath","FieldValue","deletePicklistValue","renamePicklistValue","orgHostIds","readCrmRouteScope","authorizeCrmWriter","SUITE_ACT","BATCH_SIZE","replaceEverywhere","firestore","query","field","to","beside","written","page","limit","get","empty","batch","snapshot","docs","update","ref","delete","serverTimestamp","Object","entries","flat","commit","size","crmLeadSourceValuesHandler","req","res","body","method","setHeader","status","json","error","scope","action","valueId","String","trim","slice","replaceWith","undefined","writer","suiteAct","ok","app","orgRef","collection","doc","orgId","listRef","picklists","moved","runTransaction","transaction","move","before","data","value","values","find","entry","id","created","set","picklist","defaultValueId","updatedAt","hostId","createdAt","merge","label","toLowerCase","from","leads","contacts","where","hostIds","groupIds","Set","map","org","groupId","filter","Boolean","path","answer","console"],"mappings":"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"}
@@ -1,84 +0,0 @@
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
- /**
18
- * `POST /api/crm/leads-import` — one chunk of a leads file, written
19
- * (AGL-2701).
20
- *
21
- * The browser has already read the file and applied the operator's column
22
- * mapping; what arrives here is up to {@link LEAD_IMPORT_CHUNK_SIZE} raw
23
- * rows in the vocabulary `crm-lead-import.ts` defines. This route judges
24
- * each one through the same normalizer, resolves the owner against the
25
- * org's roster, and writes it in two halves.
26
- *
27
- * ## The capture half goes through the door every capture goes through
28
- *
29
- * `addHostLead` — the ONE writer of `hosts/{hostId}/leads` for the sign-up
30
- * handler and both booking paths — is the writer here too. That is what
31
- * makes an imported row key on `personKey` the way a second form
32
- * submission does, land under the site by path with `capturedByHostIds`
33
- * naming it, count against `LEADS_MAX_PER_HOST` inside the transaction
34
- * that writes, and record NO marketing basis. A bulk path with its own
35
- * `add()` would grow an unkeyed, unbounded copy of the site's leads under
36
- * a second set of rules.
37
- *
38
- * ## A lead is host-scoped by path, so nothing here stamps `visibleTo`
39
- *
40
- * The five org collections carry `visibleTo` because they sit under
41
- * `orgs/{orgId}` and the rules decide per document which sites may read
42
- * them. A lead does not: `hosts/{hostId}/leads` is private to the site by
43
- * path, the rules admit the site's own members, and the list therefore
44
- * reads it with no scope clause. An import that stamped scope tokens onto
45
- * a lead would be writing a field nothing reads, and would suggest a
46
- * sharing decision the collection does not have. The site is the whole
47
- * scope, and the drawer's picker is where it is chosen.
48
- *
49
- * ## No file may hand a lead a consent it did not give
50
- *
51
- * A capture writes a marketing basis only when the visitor ticked a box in
52
- * front of them. A CSV row has no such event behind it, so the lead
53
- * vocabulary has no consent column and this route passes no
54
- * `marketingConsent`: an imported lead is mailable only if some earlier
55
- * capture on this site already recorded a basis, which `addHostLead`
56
- * carries forward untouched. The contacts import has a consent column
57
- * because its own export writes one and the merchant is asserting a basis
58
- * they hold per person; the leads export writes none, and a column the
59
- * file cannot fill is a column that cannot be misread.
60
- *
61
- * ## The working half is written the way the list writes it
62
- *
63
- * A status, an owner, a reason and notes are the CRM's annotations, not
64
- * the capture's, and the leads list already writes them client-direct onto
65
- * the same document. A row that carries any of them gets one merge write
66
- * after the door's, stamped `updatedAt` exactly as the list stamps it. A
67
- * row that carries none costs nothing extra.
68
- *
69
- * ## Created or merged is read once for the chunk
70
- *
71
- * The door reports only whether the lead was STORED, and the result panel
72
- * has to say how many people were added and how many updated. One
73
- * `getAll` over the chunk's document ids answers that for every row in a
74
- * single round trip, before any of them is written — which is also the
75
- * only moment the answer is still true.
76
- */
77
- import { type PluginApiHandler } from '@aglyn/aglyn/server';
78
- /**
79
- * `POST crm/leads-import` — `{ hostId, rows }` → a {@link LeadImportChunkResult}.
80
- *
81
- * Rows are written one after another so the ceiling's local count stays
82
- * honest across the request.
83
- */
84
- export declare const crmLeadsImportHandler: PluginApiHandler;