@aglyn/plugins-crm 1.0.0-beta.221 → 1.0.0-beta.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/package.json +13 -13
- package/src/lib/components/activity-list.js +12 -5
- package/src/lib/components/activity-list.js.map +1 -1
- package/src/lib/components/companies-bulk-bar.d.ts +1 -4
- package/src/lib/components/companies-bulk-bar.js +7 -20
- package/src/lib/components/companies-bulk-bar.js.map +1 -1
- package/src/lib/components/companies-section.d.ts +3 -3
- package/src/lib/components/companies-section.js +149 -46
- package/src/lib/components/companies-section.js.map +1 -1
- package/src/lib/components/company-edit-drawer.js +281 -20
- package/src/lib/components/company-edit-drawer.js.map +1 -1
- package/src/lib/components/company-properties-card.js +99 -8
- package/src/lib/components/company-properties-card.js.map +1 -1
- package/src/lib/components/contact-detail-page.js +3 -1
- package/src/lib/components/contact-detail-page.js.map +1 -1
- package/src/lib/components/contact-merge-dialog.js +33 -10
- package/src/lib/components/contact-merge-dialog.js.map +1 -1
- package/src/lib/components/contact-properties-card.d.ts +0 -69
- package/src/lib/components/contact-properties-card.js +350 -60
- package/src/lib/components/contact-properties-card.js.map +1 -1
- package/src/lib/components/contact-reports-to-field.d.ts +46 -0
- package/src/lib/components/contact-reports-to-field.js +67 -0
- package/src/lib/components/contact-reports-to-field.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +1 -8
- package/src/lib/components/contacts-bulk-bar.js +7 -19
- package/src/lib/components/contacts-bulk-bar.js.map +1 -1
- package/src/lib/components/contacts-section.js +46 -33
- package/src/lib/components/contacts-section.js.map +1 -1
- package/src/lib/components/crm-call-actions.d.ts +10 -0
- package/src/lib/components/crm-call-actions.js +26 -9
- package/src/lib/components/crm-call-actions.js.map +1 -1
- package/src/lib/components/crm-next-activity-column.js +1 -1
- package/src/lib/components/crm-next-activity-column.js.map +1 -1
- package/src/lib/components/crm-record-picker.d.ts +15 -0
- package/src/lib/components/crm-record-picker.js +16 -10
- package/src/lib/components/crm-record-picker.js.map +1 -1
- package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
- package/src/lib/components/crm-transfer-buttons.js +167 -0
- package/src/lib/components/crm-transfer-buttons.js.map +1 -0
- package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
- package/src/lib/components/deal-contact-roles-card.js +368 -0
- package/src/lib/components/deal-contact-roles-card.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +3 -2
- package/src/lib/components/deal-detail-page.js +25 -6
- package/src/lib/components/deal-detail-page.js.map +1 -1
- package/src/lib/components/deal-edit-drawer.d.ts +10 -0
- package/src/lib/components/deal-edit-drawer.js +219 -10
- package/src/lib/components/deal-edit-drawer.js.map +1 -1
- package/src/lib/components/deal-properties-card.d.ts +7 -0
- package/src/lib/components/deal-properties-card.js +34 -3
- package/src/lib/components/deal-properties-card.js.map +1 -1
- package/src/lib/components/deal-stage-card.d.ts +4 -0
- package/src/lib/components/deal-stage-card.js +10 -2
- package/src/lib/components/deal-stage-card.js.map +1 -1
- package/src/lib/components/deals-bulk-bar.d.ts +0 -3
- package/src/lib/components/deals-bulk-bar.js +7 -20
- package/src/lib/components/deals-bulk-bar.js.map +1 -1
- package/src/lib/components/deals-section.d.ts +3 -3
- package/src/lib/components/deals-section.js +123 -63
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/fields-section.js +31 -17
- package/src/lib/components/fields-section.js.map +1 -1
- package/src/lib/components/lead-convert-dialog.d.ts +4 -0
- package/src/lib/components/lead-convert-dialog.js +23 -1
- package/src/lib/components/lead-convert-dialog.js.map +1 -1
- package/src/lib/components/lead-detail-page.js +8 -2
- package/src/lib/components/lead-detail-page.js.map +1 -1
- package/src/lib/components/lead-profile-fields.d.ts +71 -0
- package/src/lib/components/lead-profile-fields.js +386 -0
- package/src/lib/components/lead-profile-fields.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +2 -1
- package/src/lib/components/lead-properties-card.js +85 -176
- package/src/lib/components/lead-properties-card.js.map +1 -1
- package/src/lib/components/lead-source-select.d.ts +4 -23
- package/src/lib/components/lead-source-select.js +9 -73
- package/src/lib/components/lead-source-select.js.map +1 -1
- package/src/lib/components/lead-status-chip.d.ts +9 -3
- package/src/lib/components/lead-status-chip.js +8 -4
- package/src/lib/components/lead-status-chip.js.map +1 -1
- package/src/lib/components/lead-status-options.d.ts +20 -0
- package/src/lib/components/lead-status-options.js +51 -0
- package/src/lib/components/lead-status-options.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
- package/src/lib/components/lead-unqualify-dialog.js +50 -18
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
- package/src/lib/components/leads-bulk-bar.d.ts +0 -3
- package/src/lib/components/leads-bulk-bar.js +29 -29
- package/src/lib/components/leads-bulk-bar.js.map +1 -1
- package/src/lib/components/leads-section.js +120 -68
- package/src/lib/components/leads-section.js.map +1 -1
- package/src/lib/components/linked-deals-card.d.ts +4 -0
- package/src/lib/components/linked-deals-card.js +47 -3
- package/src/lib/components/linked-deals-card.js.map +1 -1
- package/src/lib/components/log-activity-dialog.js +43 -3
- package/src/lib/components/log-activity-dialog.js.map +1 -1
- package/src/lib/components/new-contact-drawer.d.ts +8 -0
- package/src/lib/components/new-contact-drawer.js +108 -6
- package/src/lib/components/new-contact-drawer.js.map +1 -1
- package/src/lib/components/new-lead-drawer.d.ts +9 -2
- package/src/lib/components/new-lead-drawer.js +88 -121
- package/src/lib/components/new-lead-drawer.js.map +1 -1
- package/src/lib/components/picklist-select.d.ts +36 -0
- package/src/lib/components/picklist-select.js +114 -0
- package/src/lib/components/picklist-select.js.map +1 -0
- package/src/lib/components/picklist-values-card.d.ts +34 -0
- package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
- package/src/lib/components/picklist-values-card.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
- package/src/lib/components/pipeline-stages-dialog.js +26 -4
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
- package/src/lib/components/pipelines-dialog.js +15 -6
- package/src/lib/components/pipelines-dialog.js.map +1 -1
- package/src/lib/components/recent-activity-feed.js +20 -5
- package/src/lib/components/recent-activity-feed.js.map +1 -1
- package/src/lib/components/recipes-card.js +4 -2
- package/src/lib/components/recipes-card.js.map +1 -1
- package/src/lib/components/record-tasks-card.js +15 -8
- package/src/lib/components/record-tasks-card.js.map +1 -1
- package/src/lib/components/reports/forecast-card.d.ts +6 -0
- package/src/lib/components/reports/forecast-card.js +101 -3
- package/src/lib/components/reports/forecast-card.js.map +1 -1
- package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
- package/src/lib/components/reports/lead-funnel-card.js +9 -5
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
- package/src/lib/components/reports/lead-source-card.d.ts +23 -0
- package/src/lib/components/reports/lead-source-card.js +318 -0
- package/src/lib/components/reports/lead-source-card.js.map +1 -0
- package/src/lib/components/reports/pipeline-card.js +2 -1
- package/src/lib/components/reports/pipeline-card.js.map +1 -1
- package/src/lib/components/reports/report-export.js +1 -1
- package/src/lib/components/reports/report-export.js.map +1 -1
- package/src/lib/components/reports-section.js +8 -0
- package/src/lib/components/reports-section.js.map +1 -1
- package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
- package/src/lib/components/sharing-rule-drawer.js +108 -19
- package/src/lib/components/sharing-rule-drawer.js.map +1 -1
- package/src/lib/components/sharing-rules-card.js +11 -3
- package/src/lib/components/sharing-rules-card.js.map +1 -1
- package/src/lib/components/task-cells.d.ts +9 -1
- package/src/lib/components/task-cells.js +9 -4
- package/src/lib/components/task-cells.js.map +1 -1
- package/src/lib/components/task-edit-drawer.js +111 -43
- package/src/lib/components/task-edit-drawer.js.map +1 -1
- package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
- package/src/lib/components/tasks-bulk-bar.js +7 -20
- package/src/lib/components/tasks-bulk-bar.js.map +1 -1
- package/src/lib/components/tasks-calendar.d.ts +3 -0
- package/src/lib/components/tasks-calendar.js +8 -1
- package/src/lib/components/tasks-calendar.js.map +1 -1
- package/src/lib/components/tasks-section.d.ts +3 -3
- package/src/lib/components/tasks-section.js +58 -43
- package/src/lib/components/tasks-section.js.map +1 -1
- package/src/lib/constants/api-routes.d.ts +2 -9
- package/src/lib/constants/api-routes.js +1 -7
- package/src/lib/constants/api-routes.js.map +1 -1
- package/src/lib/constants/bundle-common.d.ts +6 -0
- package/src/lib/constants/bundle-common.js +11 -0
- package/src/lib/constants/bundle-common.js.map +1 -1
- package/src/lib/constants/company-filters.d.ts +14 -0
- package/src/lib/constants/company-filters.js +51 -1
- package/src/lib/constants/company-filters.js.map +1 -1
- package/src/lib/constants/contact-filters.d.ts +2 -0
- package/src/lib/constants/contact-filters.js +18 -1
- package/src/lib/constants/contact-filters.js.map +1 -1
- package/src/lib/constants/deal-filters.d.ts +22 -1
- package/src/lib/constants/deal-filters.js +67 -4
- package/src/lib/constants/deal-filters.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +3 -0
- package/src/lib/declarations.console-server.js +35 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +26 -0
- package/src/lib/declarations.js +35 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.d.ts +11 -0
- package/src/lib/declarations.server.js +97 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/hooks/use-company-picklists.d.ts +15 -0
- package/src/lib/hooks/use-company-picklists.js +55 -0
- package/src/lib/hooks/use-company-picklists.js.map +1 -0
- package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
- package/src/lib/hooks/use-crm-picklist.js +53 -0
- package/src/lib/hooks/use-crm-picklist.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
- package/src/lib/hooks/use-crm-record-names.js +45 -19
- package/src/lib/hooks/use-crm-record-names.js.map +1 -1
- package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
- package/src/lib/hooks/use-crm-task-picklists.js +45 -0
- package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +14 -0
- package/src/lib/hooks/use-deals.js +29 -0
- package/src/lib/hooks/use-deals.js.map +1 -1
- package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
- package/src/lib/hooks/use-lead-picklists.js +46 -0
- package/src/lib/hooks/use-lead-picklists.js.map +1 -0
- package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
- package/src/lib/hooks/use-lead-source-picklist.js +12 -22
- package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
- package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
- package/src/lib/hooks/use-lead-status-picklist.js +40 -0
- package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
- package/src/lib/model/companies.d.ts +32 -3
- package/src/lib/model/companies.js +133 -16
- package/src/lib/model/companies.js.map +1 -1
- package/src/lib/model/contact-merge.d.ts +27 -3
- package/src/lib/model/contact-merge.js +81 -7
- package/src/lib/model/contact-merge.js.map +1 -1
- package/src/lib/model/contact-record.d.ts +17 -0
- package/src/lib/model/contact-record.js +17 -2
- package/src/lib/model/contact-record.js.map +1 -1
- package/src/lib/model/contact-update.d.ts +31 -2
- package/src/lib/model/contact-update.js +14 -0
- package/src/lib/model/contact-update.js.map +1 -1
- package/src/lib/model/crm-list-query.d.ts +5 -0
- package/src/lib/model/crm-list-query.js +8 -0
- package/src/lib/model/crm-list-query.js.map +1 -1
- package/src/lib/model/crm-next-activity.d.ts +107 -0
- package/src/lib/model/crm-next-activity.js +140 -0
- package/src/lib/model/crm-next-activity.js.map +1 -0
- package/src/lib/model/crm-recipes.d.ts +40 -0
- package/src/lib/model/crm-recipes.js +259 -0
- package/src/lib/model/crm-recipes.js.map +1 -0
- package/src/lib/model/crm-record-counts.d.ts +36 -0
- package/src/lib/model/crm-record-counts.js +54 -0
- package/src/lib/model/crm-record-counts.js.map +1 -0
- package/src/lib/model/crm-record-lists.d.ts +23 -0
- package/src/lib/model/crm-record-lists.js +161 -0
- package/src/lib/model/crm-record-lists.js.map +1 -0
- package/src/lib/model/crm-reports.d.ts +73 -3
- package/src/lib/model/crm-reports.js +120 -1
- package/src/lib/model/crm-reports.js.map +1 -1
- package/src/lib/model/crm-sharing.d.ts +21 -3
- package/src/lib/model/crm-sharing.js +27 -6
- package/src/lib/model/crm-sharing.js.map +1 -1
- package/src/lib/model/crm-task-reminders.d.ts +152 -0
- package/src/lib/model/crm-task-reminders.js +161 -0
- package/src/lib/model/crm-task-reminders.js.map +1 -0
- package/src/lib/model/deal-board-model.d.ts +8 -2
- package/src/lib/model/deal-board-model.js +15 -2
- package/src/lib/model/deal-board-model.js.map +1 -1
- package/src/lib/model/deal-contact-roles.d.ts +45 -0
- package/src/lib/model/deal-contact-roles.js +73 -0
- package/src/lib/model/deal-contact-roles.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +56 -0
- package/src/lib/model/deal-form-model.js +79 -7
- package/src/lib/model/deal-form-model.js.map +1 -1
- package/src/lib/model/download-text-file.d.ts +23 -0
- package/src/lib/model/download-text-file.js +34 -0
- package/src/lib/model/download-text-file.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +36 -2
- package/src/lib/model/lead-filters.js +123 -2
- package/src/lib/model/lead-filters.js.map +1 -1
- package/src/lib/model/lead-source-direction.d.ts +38 -0
- package/src/lib/model/lead-source-direction.js +52 -0
- package/src/lib/model/lead-source-direction.js.map +1 -0
- package/src/lib/model/next-activity.d.ts +1 -1
- package/src/lib/model/next-activity.js.map +1 -1
- package/src/lib/model/picklist-values.d.ts +119 -0
- package/src/lib/model/picklist-values.js +256 -0
- package/src/lib/model/picklist-values.js.map +1 -0
- package/src/lib/model/record-facts-disclosed.d.ts +81 -0
- package/src/lib/model/record-facts-disclosed.js +281 -0
- package/src/lib/model/record-facts-disclosed.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +215 -34
- package/src/lib/model/record-facts.js +307 -128
- package/src/lib/model/record-facts.js.map +1 -1
- package/src/lib/model/task-picklists.d.ts +52 -0
- package/src/lib/model/task-picklists.js +83 -0
- package/src/lib/model/task-picklists.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +13 -0
- package/src/lib/model/task-routes.js +23 -4
- package/src/lib/model/task-routes.js.map +1 -1
- package/src/lib/plan-entitlements.js +6 -4
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +17 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/api-v1/contacts.js +220 -14
- package/src/lib/server/api-v1/contacts.js.map +1 -1
- package/src/lib/server/api-v1/crm-activities.js +14 -1
- package/src/lib/server/api-v1/crm-activities.js.map +1 -1
- package/src/lib/server/api-v1/crm-companies.js +123 -14
- package/src/lib/server/api-v1/crm-companies.js.map +1 -1
- package/src/lib/server/api-v1/crm-deals.js +291 -26
- package/src/lib/server/api-v1/crm-deals.js.map +1 -1
- package/src/lib/server/api-v1/crm-leads.js +144 -38
- package/src/lib/server/api-v1/crm-leads.js.map +1 -1
- package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
- package/src/lib/server/api-v1/crm-pipelines.js +8 -2
- package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
- package/src/lib/server/api-v1/crm-tasks.js +78 -34
- package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
- package/src/lib/server/api-v1/openapi.d.ts +0 -11
- package/src/lib/server/api-v1/openapi.js +171 -16
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/assign-contact-owner.d.ts +188 -0
- package/src/lib/server/assign-contact-owner.js +446 -0
- package/src/lib/server/assign-contact-owner.js.map +1 -0
- package/src/lib/server/associate-company-by-domain.d.ts +70 -0
- package/src/lib/server/associate-company-by-domain.js +153 -0
- package/src/lib/server/associate-company-by-domain.js.map +1 -0
- package/src/lib/server/automation-steps.d.ts +140 -0
- package/src/lib/server/automation-steps.js +669 -0
- package/src/lib/server/automation-steps.js.map +1 -0
- package/src/lib/server/capture-contact.d.ts +17 -0
- package/src/lib/server/capture-contact.js +95 -26
- package/src/lib/server/capture-contact.js.map +1 -1
- package/src/lib/server/capture-host-contact.d.ts +104 -0
- package/src/lib/server/capture-host-contact.js +151 -0
- package/src/lib/server/capture-host-contact.js.map +1 -0
- package/src/lib/server/company-children.d.ts +25 -0
- package/src/lib/server/company-children.js +42 -0
- package/src/lib/server/company-children.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +18 -0
- package/src/lib/server/company-delete.js +44 -21
- package/src/lib/server/company-delete.js.map +1 -1
- package/src/lib/server/contact-email-history.js +21 -19
- package/src/lib/server/contact-email-history.js.map +1 -1
- package/src/lib/server/contact-remove.js +27 -2
- package/src/lib/server/contact-remove.js.map +1 -1
- package/src/lib/server/contact-reports-to.d.ts +61 -0
- package/src/lib/server/contact-reports-to.js +114 -0
- package/src/lib/server/contact-reports-to.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +42 -1
- package/src/lib/server/contact-update.js +204 -23
- package/src/lib/server/contact-update.js.map +1 -1
- package/src/lib/server/convert-host-lead.d.ts +46 -1
- package/src/lib/server/convert-host-lead.js +173 -15
- package/src/lib/server/convert-host-lead.js.map +1 -1
- package/src/lib/server/convert-open-lead.js +12 -5
- package/src/lib/server/convert-open-lead.js.map +1 -1
- package/src/lib/server/crm-email-activity.d.ts +118 -0
- package/src/lib/server/crm-email-activity.js +148 -0
- package/src/lib/server/crm-email-activity.js.map +1 -0
- package/src/lib/server/crm-inbound-email.js +1 -1
- package/src/lib/server/crm-inbound-email.js.map +1 -1
- package/src/lib/server/crm-next-activity.d.ts +74 -0
- package/src/lib/server/crm-next-activity.js +194 -0
- package/src/lib/server/crm-next-activity.js.map +1 -0
- package/src/lib/server/crm-record-indexes.d.ts +45 -0
- package/src/lib/server/crm-record-indexes.js +78 -0
- package/src/lib/server/crm-record-indexes.js.map +1 -0
- package/src/lib/server/crm-sharing.d.ts +9 -1
- package/src/lib/server/crm-sharing.js +23 -3
- package/src/lib/server/crm-sharing.js.map +1 -1
- package/src/lib/server/deal-contact-roles.d.ts +27 -0
- package/src/lib/server/deal-contact-roles.js +89 -0
- package/src/lib/server/deal-contact-roles.js.map +1 -0
- package/src/lib/server/email-send.js +2 -1
- package/src/lib/server/email-send.js.map +1 -1
- package/src/lib/server/lead-convert.d.ts +5 -0
- package/src/lib/server/lead-convert.js +19 -3
- package/src/lib/server/lead-convert.js.map +1 -1
- package/src/lib/server/lead-create.d.ts +17 -2
- package/src/lib/server/lead-create.js +46 -14
- package/src/lib/server/lead-create.js.map +1 -1
- package/src/lib/server/lead-nurturing.d.ts +6 -0
- package/src/lib/server/lead-nurturing.js +57 -20
- package/src/lib/server/lead-nurturing.js.map +1 -1
- package/src/lib/server/lead-picklists.d.ts +35 -0
- package/src/lib/server/lead-picklists.js +52 -0
- package/src/lib/server/lead-picklists.js.map +1 -0
- package/src/lib/server/lead-source-picklist.d.ts +1 -13
- package/src/lib/server/lead-source-picklist.js +6 -33
- package/src/lib/server/lead-source-picklist.js.map +1 -1
- package/src/lib/server/merge-contacts.js +23 -5
- package/src/lib/server/merge-contacts.js.map +1 -1
- package/src/lib/server/next-activity-routes.js +2 -1
- package/src/lib/server/next-activity-routes.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +53 -0
- package/src/lib/server/person-eraser.js +149 -0
- package/src/lib/server/person-eraser.js.map +1 -0
- package/src/lib/server/person-records.d.ts +69 -0
- package/src/lib/server/person-records.js +317 -0
- package/src/lib/server/person-records.js.map +1 -0
- package/src/lib/server/person-refund.d.ts +97 -0
- package/src/lib/server/person-refund.js +221 -0
- package/src/lib/server/person-refund.js.map +1 -0
- package/src/lib/server/picklist-values.d.ts +21 -0
- package/src/lib/server/picklist-values.js +308 -0
- package/src/lib/server/picklist-values.js.map +1 -0
- package/src/lib/server/read-picklist.d.ts +44 -0
- package/src/lib/server/read-picklist.js +65 -0
- package/src/lib/server/read-picklist.js.map +1 -0
- package/src/lib/server/recipe-routes.js +22 -8
- package/src/lib/server/recipe-routes.js.map +1 -1
- package/src/lib/server/record-email-state.js +7 -1
- package/src/lib/server/record-email-state.js.map +1 -1
- package/src/lib/server/record-facts.d.ts +3 -0
- package/src/lib/server/record-facts.js +233 -31
- package/src/lib/server/record-facts.js.map +1 -1
- package/src/lib/server/record-origin.d.ts +27 -0
- package/src/lib/server/record-origin.js +104 -0
- package/src/lib/server/record-origin.js.map +1 -0
- package/src/lib/server/record-timeline.js +40 -3
- package/src/lib/server/record-timeline.js.map +1 -1
- package/src/lib/server/suite-gate.js +4 -3
- package/src/lib/server/suite-gate.js.map +1 -1
- package/src/lib/server/task-reminders-route.js +2 -1
- package/src/lib/server/task-reminders-route.js.map +1 -1
- package/src/lib/server/task-routes.js +33 -17
- package/src/lib/server/task-routes.js.map +1 -1
- package/src/lib/server-deal-stage.d.ts +5 -1
- package/src/lib/server-deal-stage.js +10 -5
- package/src/lib/server-deal-stage.js.map +1 -1
- package/src/lib/server.d.ts +8 -3
- package/src/lib/server.js +47 -43
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/memory-firestore.d.ts +55 -0
- package/src/lib/testing/memory-firestore.js +335 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
- package/src/lib/testing/stand-in-email-send-index.js +49 -0
- package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
- package/src/lib/transfer/activities.d.ts +30 -0
- package/src/lib/transfer/activities.js +132 -0
- package/src/lib/transfer/activities.js.map +1 -0
- package/src/lib/transfer/aliases.d.ts +22 -0
- package/src/lib/transfer/aliases.js +1342 -0
- package/src/lib/transfer/aliases.js.map +1 -0
- package/src/lib/transfer/common.d.ts +222 -0
- package/src/lib/transfer/common.js +832 -0
- package/src/lib/transfer/common.js.map +1 -0
- package/src/lib/transfer/companies.d.ts +43 -0
- package/src/lib/transfer/companies.js +549 -0
- package/src/lib/transfer/companies.js.map +1 -0
- package/src/lib/transfer/contacts.d.ts +31 -0
- package/src/lib/transfer/contacts.js +727 -0
- package/src/lib/transfer/contacts.js.map +1 -0
- package/src/lib/transfer/deals.d.ts +66 -0
- package/src/lib/transfer/deals.js +698 -0
- package/src/lib/transfer/deals.js.map +1 -0
- package/src/lib/transfer/email-templates-package.d.ts +40 -0
- package/src/lib/transfer/email-templates-package.js +76 -0
- package/src/lib/transfer/email-templates-package.js.map +1 -0
- package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
- package/src/lib/transfer/email-templates-package.server.js +176 -0
- package/src/lib/transfer/email-templates-package.server.js.map +1 -0
- package/src/lib/transfer/fields.d.ts +102 -0
- package/src/lib/transfer/fields.js +1755 -0
- package/src/lib/transfer/fields.js.map +1 -0
- package/src/lib/transfer/leads.d.ts +30 -0
- package/src/lib/transfer/leads.js +582 -0
- package/src/lib/transfer/leads.js.map +1 -0
- package/src/lib/transfer/pipelines.d.ts +21 -0
- package/src/lib/transfer/pipelines.js +172 -0
- package/src/lib/transfer/pipelines.js.map +1 -0
- package/src/lib/transfer/register.d.ts +47 -0
- package/src/lib/transfer/register.js +277 -0
- package/src/lib/transfer/register.js.map +1 -0
- package/src/lib/transfer/tasks.d.ts +30 -0
- package/src/lib/transfer/tasks.js +418 -0
- package/src/lib/transfer/tasks.js.map +1 -0
- package/src/lib/transfer/ui.d.ts +20 -0
- package/src/lib/transfer/ui.js +42 -0
- package/src/lib/transfer/ui.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +0 -42
- package/src/lib/components/company-import-drawer.js +0 -99
- package/src/lib/components/company-import-drawer.js.map +0 -1
- package/src/lib/components/contact-import-drawer.d.ts +0 -46
- package/src/lib/components/contact-import-drawer.js +0 -149
- package/src/lib/components/contact-import-drawer.js.map +0 -1
- package/src/lib/components/crm-export-all-button.d.ts +0 -39
- package/src/lib/components/crm-export-all-button.js +0 -98
- package/src/lib/components/crm-export-all-button.js.map +0 -1
- package/src/lib/components/csv-import-drawer.d.ts +0 -139
- package/src/lib/components/csv-import-drawer.js +0 -757
- package/src/lib/components/csv-import-drawer.js.map +0 -1
- package/src/lib/components/deal-import-drawer.d.ts +0 -24
- package/src/lib/components/deal-import-drawer.js +0 -83
- package/src/lib/components/deal-import-drawer.js.map +0 -1
- package/src/lib/components/lead-import-drawer.d.ts +0 -27
- package/src/lib/components/lead-import-drawer.js +0 -118
- package/src/lib/components/lead-import-drawer.js.map +0 -1
- package/src/lib/components/lead-source-values-card.d.ts +0 -27
- package/src/lib/components/lead-source-values-card.js.map +0 -1
- package/src/lib/components/task-import-drawer.d.ts +0 -24
- package/src/lib/components/task-import-drawer.js +0 -82
- package/src/lib/components/task-import-drawer.js.map +0 -1
- package/src/lib/model/companies-csv.d.ts +0 -36
- package/src/lib/model/companies-csv.js +0 -38
- package/src/lib/model/companies-csv.js.map +0 -1
- package/src/lib/model/contacts-csv.d.ts +0 -54
- package/src/lib/model/contacts-csv.js +0 -67
- package/src/lib/model/contacts-csv.js.map +0 -1
- package/src/lib/model/crm-company-import.d.ts +0 -154
- package/src/lib/model/crm-company-import.js +0 -331
- package/src/lib/model/crm-company-import.js.map +0 -1
- package/src/lib/model/crm-csv.d.ts +0 -239
- package/src/lib/model/crm-csv.js +0 -467
- package/src/lib/model/crm-csv.js.map +0 -1
- package/src/lib/model/crm-deal-import.d.ts +0 -112
- package/src/lib/model/crm-deal-import.js +0 -263
- package/src/lib/model/crm-deal-import.js.map +0 -1
- package/src/lib/model/crm-import.d.ts +0 -250
- package/src/lib/model/crm-import.js +0 -467
- package/src/lib/model/crm-import.js.map +0 -1
- package/src/lib/model/crm-lead-import.d.ts +0 -138
- package/src/lib/model/crm-lead-import.js +0 -446
- package/src/lib/model/crm-lead-import.js.map +0 -1
- package/src/lib/model/crm-task-import.d.ts +0 -94
- package/src/lib/model/crm-task-import.js +0 -275
- package/src/lib/model/crm-task-import.js.map +0 -1
- package/src/lib/model/deals-csv.d.ts +0 -32
- package/src/lib/model/deals-csv.js +0 -36
- package/src/lib/model/deals-csv.js.map +0 -1
- package/src/lib/model/import-column-shapes.d.ts +0 -36
- package/src/lib/model/import-column-shapes.js +0 -76
- package/src/lib/model/import-column-shapes.js.map +0 -1
- package/src/lib/model/lead-source-values.d.ts +0 -82
- package/src/lib/model/lead-source-values.js +0 -149
- package/src/lib/model/lead-source-values.js.map +0 -1
- package/src/lib/model/leads-csv.d.ts +0 -40
- package/src/lib/model/leads-csv.js +0 -40
- package/src/lib/model/leads-csv.js.map +0 -1
- package/src/lib/model/tasks-csv.d.ts +0 -28
- package/src/lib/model/tasks-csv.js +0 -28
- package/src/lib/model/tasks-csv.js.map +0 -1
- package/src/lib/server/companies-import.d.ts +0 -57
- package/src/lib/server/companies-import.js +0 -242
- package/src/lib/server/companies-import.js.map +0 -1
- package/src/lib/server/contacts-import.d.ts +0 -109
- package/src/lib/server/contacts-import.js +0 -393
- package/src/lib/server/contacts-import.js.map +0 -1
- package/src/lib/server/deals-import.d.ts +0 -76
- package/src/lib/server/deals-import.js +0 -248
- package/src/lib/server/deals-import.js.map +0 -1
- package/src/lib/server/export-route.d.ts +0 -60
- package/src/lib/server/export-route.js +0 -447
- package/src/lib/server/export-route.js.map +0 -1
- package/src/lib/server/import-context.d.ts +0 -87
- package/src/lib/server/import-context.js +0 -182
- package/src/lib/server/import-context.js.map +0 -1
- package/src/lib/server/lead-source-values.d.ts +0 -50
- package/src/lib/server/lead-source-values.js +0 -191
- package/src/lib/server/lead-source-values.js.map +0 -1
- package/src/lib/server/leads-import.d.ts +0 -84
- package/src/lib/server/leads-import.js +0 -367
- package/src/lib/server/leads-import.js.map +0 -1
- package/src/lib/server/tasks-import.d.ts +0 -54
- package/src/lib/server/tasks-import.js +0 -160
- 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":"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"}
|
|
@@ -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;
|