@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,248 +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
|
-
*/ /**
|
|
18
|
-
* `POST /api/crm/deals-import` — one chunk of a deals file, written
|
|
19
|
-
* (AGL-2662).
|
|
20
|
-
*
|
|
21
|
-
* The browser has already read the file and applied the operator's column
|
|
22
|
-
* mapping; what arrives here is up to {@link DEAL_IMPORT_CHUNK_SIZE} raw
|
|
23
|
-
* rows in the vocabulary `crm-deal-import.ts` defines. This route judges
|
|
24
|
-
* each one through the same normalizer, resolves the two names a deal is
|
|
25
|
-
* filed under, and writes it with the stamp every CRM creator writes.
|
|
26
|
-
*
|
|
27
|
-
* ## The pipeline and the stage are looked up ONCE per request
|
|
28
|
-
*
|
|
29
|
-
* The org's pipelines are read once — the active ones the caller's scope
|
|
30
|
-
* can see — and every row resolves against that list by name. A row
|
|
31
|
-
* naming no pipeline lands in the default one (or the only one); a row
|
|
32
|
-
* naming no stage lands in its pipeline's first open stage. A name that
|
|
33
|
-
* matches nothing refuses the row by name, before any write, so the
|
|
34
|
-
* operator's skipped file says exactly which cell to fix.
|
|
35
|
-
*
|
|
36
|
-
* ## A deal's status is its stage's kind
|
|
37
|
-
*
|
|
38
|
-
* Landing in a `won` or `lost` stage closes the deal on creation, stamped
|
|
39
|
-
* with the same clock as `stageChangedAtMs`, the way the REST create does.
|
|
40
|
-
* No `dealWon` event fires for an imported win: the deal was won before it
|
|
41
|
-
* was filed here, and an automation on the event is for what happens next.
|
|
42
|
-
*
|
|
43
|
-
* ## The band is counted once per request
|
|
44
|
-
*
|
|
45
|
-
* A deal is a CRM record and meets the records band (AGL-2611), judged
|
|
46
|
-
* the way the companies import judges it: one aggregate at the first
|
|
47
|
-
* create, re-judged locally as the request creates more.
|
|
48
|
-
*/ import { checkCrmRecordsQuota, CRM_COLLECTIONS, crmNewRecordListFields, isPipelineArchived, nameSearchKey, visibleToTokens } from "@aglyn/aglyn/server";
|
|
49
|
-
import { DEAL_IMPORT_CHUNK_SIZE, DEAL_IMPORT_MAX_BODY_BYTES, dealImportNameKey, normalizeDealImportRow } from "../model/crm-deal-import.js";
|
|
50
|
-
import { crmRecordsQuotaForOrg, firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
51
|
-
import { FieldValue } from "firebase-admin/firestore";
|
|
52
|
-
import { ownerDirectory, readImportRows, resolveImportContext } from "./import-context.js";
|
|
53
|
-
/** The default currency a deal carries when the file names none. */ const DEFAULT_CURRENCY = 'usd';
|
|
54
|
-
/** The active pipelines the caller's scope can see, in read order. */ async function readPipelines(orgRef, readTokens) {
|
|
55
|
-
const snapshot = await orgRef.collection(CRM_COLLECTIONS.pipelines).get();
|
|
56
|
-
const choices = [];
|
|
57
|
-
for (const doc of snapshot.docs){
|
|
58
|
-
var _pipeline_stages;
|
|
59
|
-
const pipeline = doc.data();
|
|
60
|
-
if (isPipelineArchived(pipeline)) continue;
|
|
61
|
-
if (!visibleToTokens(pipeline.visibleTo, readTokens)) continue;
|
|
62
|
-
const stages = [
|
|
63
|
-
...(_pipeline_stages = pipeline.stages) != null ? _pipeline_stages : []
|
|
64
|
-
].sort((a, b)=>a.order - b.order);
|
|
65
|
-
choices.push({
|
|
66
|
-
id: doc.id,
|
|
67
|
-
pipeline,
|
|
68
|
-
stages
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return choices;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Where one row lands: its pipeline and its stage, or which of the two
|
|
75
|
-
* names the org does not have.
|
|
76
|
-
*/ export function placeDealImportRow(choices, row) {
|
|
77
|
-
let pipeline;
|
|
78
|
-
if (row.pipeline) {
|
|
79
|
-
const wanted = dealImportNameKey(row.pipeline);
|
|
80
|
-
pipeline = choices.find((choice)=>{
|
|
81
|
-
var _choice_pipeline_name;
|
|
82
|
-
return dealImportNameKey((_choice_pipeline_name = choice.pipeline.name) != null ? _choice_pipeline_name : '') === wanted;
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
var _choices_find;
|
|
86
|
-
pipeline = (_choices_find = choices.find((choice)=>choice.pipeline.isDefault)) != null ? _choices_find : choices.length === 1 ? choices[0] : undefined;
|
|
87
|
-
}
|
|
88
|
-
if (!pipeline) return {
|
|
89
|
-
ok: false,
|
|
90
|
-
reason: 'unknown-pipeline'
|
|
91
|
-
};
|
|
92
|
-
let stage;
|
|
93
|
-
if (row.stage) {
|
|
94
|
-
const wanted = dealImportNameKey(row.stage);
|
|
95
|
-
stage = pipeline.stages.find((candidate)=>{
|
|
96
|
-
var _candidate_name;
|
|
97
|
-
return dealImportNameKey((_candidate_name = candidate.name) != null ? _candidate_name : '') === wanted;
|
|
98
|
-
});
|
|
99
|
-
} else {
|
|
100
|
-
var _pipeline_stages_find;
|
|
101
|
-
stage = (_pipeline_stages_find = pipeline.stages.find((candidate)=>candidate.kind === 'open')) != null ? _pipeline_stages_find : pipeline.stages[0];
|
|
102
|
-
}
|
|
103
|
-
if (!stage) return {
|
|
104
|
-
ok: false,
|
|
105
|
-
reason: 'unknown-stage'
|
|
106
|
-
};
|
|
107
|
-
return {
|
|
108
|
-
ok: true,
|
|
109
|
-
pipeline,
|
|
110
|
-
stage
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* `POST crm/deals-import` — `{ hostId, rows }` → a {@link DealImportChunkResult}.
|
|
115
|
-
*
|
|
116
|
-
* Rows are written one after another so the band's local count stays
|
|
117
|
-
* honest across the request.
|
|
118
|
-
*/ export const crmDealsImportHandler = async (req, res)=>{
|
|
119
|
-
if (req.method !== 'POST') {
|
|
120
|
-
res.setHeader('Allow', 'POST');
|
|
121
|
-
return res.status(405).json({
|
|
122
|
-
error: 'Method not allowed'
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
const read = readImportRows(req, {
|
|
126
|
-
maxBodyBytes: DEAL_IMPORT_MAX_BODY_BYTES,
|
|
127
|
-
chunkSize: DEAL_IMPORT_CHUNK_SIZE
|
|
128
|
-
});
|
|
129
|
-
if ('error' in read) return res.status(read.status).json({
|
|
130
|
-
error: read.error
|
|
131
|
-
});
|
|
132
|
-
try {
|
|
133
|
-
const context = await resolveImportContext(req);
|
|
134
|
-
if (context.ok === false) return res.status(context.status).json(context.body);
|
|
135
|
-
const skipped = [];
|
|
136
|
-
const dropped = {};
|
|
137
|
-
const normalized = [];
|
|
138
|
-
read.rows.forEach((raw, index)=>{
|
|
139
|
-
const verdict = normalizeDealImportRow(raw);
|
|
140
|
-
if (verdict.ok === false) {
|
|
141
|
-
skipped.push({
|
|
142
|
-
index,
|
|
143
|
-
title: verdict.input,
|
|
144
|
-
reason: verdict.reason
|
|
145
|
-
});
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
for (const entry of verdict.row.dropped){
|
|
149
|
-
var _dropped_entry_field;
|
|
150
|
-
dropped[entry.field] = ((_dropped_entry_field = dropped[entry.field]) != null ? _dropped_entry_field : 0) + 1;
|
|
151
|
-
}
|
|
152
|
-
normalized.push({
|
|
153
|
-
index,
|
|
154
|
-
row: verdict.row
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
const orgRef = firebaseAdmin.app().firestore().collection('orgs').doc(context.orgId);
|
|
158
|
-
const [owners, pipelines] = await Promise.all([
|
|
159
|
-
ownerDirectory(context.orgId, normalized.map((entry)=>entry.row)),
|
|
160
|
-
normalized.length ? readPipelines(orgRef, context.readTokens) : Promise.resolve([])
|
|
161
|
-
]);
|
|
162
|
-
const ownersUnresolved = new Set();
|
|
163
|
-
const deals = orgRef.collection(CRM_COLLECTIONS.deals);
|
|
164
|
-
const stamp = creationStamp(context);
|
|
165
|
-
let counted = null;
|
|
166
|
-
let createdHere = 0;
|
|
167
|
-
let created = 0;
|
|
168
|
-
for (const { index, row } of normalized){
|
|
169
|
-
var _row_currency;
|
|
170
|
-
const placed = placeDealImportRow(pipelines, row);
|
|
171
|
-
if (placed.ok === false) {
|
|
172
|
-
skipped.push({
|
|
173
|
-
index,
|
|
174
|
-
title: row.title,
|
|
175
|
-
reason: placed.reason
|
|
176
|
-
});
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
let ownerUid;
|
|
180
|
-
if (row.ownerEmail) {
|
|
181
|
-
ownerUid = owners.get(row.ownerEmail);
|
|
182
|
-
if (!ownerUid) ownersUnresolved.add(row.ownerEmail);
|
|
183
|
-
}
|
|
184
|
-
if (!counted) counted = await crmRecordsQuotaForOrg(context.org, orgRef);
|
|
185
|
-
const room = checkCrmRecordsQuota(context.org, counted.crmRecordsCount + createdHere);
|
|
186
|
-
if (!room.allowed) {
|
|
187
|
-
skipped.push({
|
|
188
|
-
index,
|
|
189
|
-
title: row.title,
|
|
190
|
-
reason: 'records-band'
|
|
191
|
-
});
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const nowMs = Date.now();
|
|
195
|
-
const { stage } = placed;
|
|
196
|
-
const status = stage.kind === 'won' ? 'won' : stage.kind === 'lost' ? 'lost' : 'open';
|
|
197
|
-
await deals.add(_extends({
|
|
198
|
-
title: row.title,
|
|
199
|
-
titleLower: nameSearchKey(row.title),
|
|
200
|
-
pipelineId: placed.pipeline.id,
|
|
201
|
-
stageId: stage.id,
|
|
202
|
-
status,
|
|
203
|
-
stageChangedAtMs: nowMs,
|
|
204
|
-
closedAtMs: status === 'open' ? null : nowMs,
|
|
205
|
-
currency: (_row_currency = row.currency) != null ? _row_currency : DEFAULT_CURRENCY
|
|
206
|
-
}, typeof row.amountCents === 'number' ? {
|
|
207
|
-
amountCents: row.amountCents
|
|
208
|
-
} : {}, ownerUid ? {
|
|
209
|
-
ownerUid
|
|
210
|
-
} : {}, typeof row.expectedCloseAtMs === 'number' ? {
|
|
211
|
-
expectedCloseAtMs: row.expectedCloseAtMs
|
|
212
|
-
} : {}, row.notes ? {
|
|
213
|
-
notes: row.notes
|
|
214
|
-
} : {}, stamp, crmNewRecordListFields('deals', _extends({
|
|
215
|
-
title: row.title
|
|
216
|
-
}, stamp))));
|
|
217
|
-
createdHere += 1;
|
|
218
|
-
created += 1;
|
|
219
|
-
}
|
|
220
|
-
const result = {
|
|
221
|
-
received: read.rows.length,
|
|
222
|
-
created,
|
|
223
|
-
merged: 0,
|
|
224
|
-
skipped: skipped.sort((a, b)=>a.index - b.index),
|
|
225
|
-
dropped,
|
|
226
|
-
ownersUnresolved: [
|
|
227
|
-
...ownersUnresolved
|
|
228
|
-
]
|
|
229
|
-
};
|
|
230
|
-
return res.status(200).json(result);
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.error('crm/deals-import failed', error);
|
|
233
|
-
return res.status(500).json({
|
|
234
|
-
error: 'The import could not continue.'
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
/** The scope, the site, the importer and the clocks every created deal carries. */ function creationStamp(context) {
|
|
239
|
-
return {
|
|
240
|
-
hostId: context.hostId,
|
|
241
|
-
visibleTo: context.scopeTokens,
|
|
242
|
-
createdByUid: context.uid,
|
|
243
|
-
createdAt: FieldValue.serverTimestamp(),
|
|
244
|
-
updatedAt: FieldValue.serverTimestamp()
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
//# sourceMappingURL=deals-import.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/deals-import.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 * `POST /api/crm/deals-import` — one chunk of a deals file, written\n * (AGL-2662).\n *\n * The browser has already read the file and applied the operator's column\n * mapping; what arrives here is up to {@link DEAL_IMPORT_CHUNK_SIZE} raw\n * rows in the vocabulary `crm-deal-import.ts` defines. This route judges\n * each one through the same normalizer, resolves the two names a deal is\n * filed under, and writes it with the stamp every CRM creator writes.\n *\n * ## The pipeline and the stage are looked up ONCE per request\n *\n * The org's pipelines are read once — the active ones the caller's scope\n * can see — and every row resolves against that list by name. A row\n * naming no pipeline lands in the default one (or the only one); a row\n * naming no stage lands in its pipeline's first open stage. A name that\n * matches nothing refuses the row by name, before any write, so the\n * operator's skipped file says exactly which cell to fix.\n *\n * ## A deal's status is its stage's kind\n *\n * Landing in a `won` or `lost` stage closes the deal on creation, stamped\n * with the same clock as `stageChangedAtMs`, the way the REST create does.\n * No `dealWon` event fires for an imported win: the deal was won before it\n * was filed here, and an automation on the event is for what happens next.\n *\n * ## The band is counted once per request\n *\n * A deal is a CRM record and meets the records band (AGL-2611), judged\n * the way the companies import judges it: one aggregate at the first\n * create, re-judged locally as the request creates more.\n */\n\nimport {\n checkCrmRecordsQuota,\n CRM_COLLECTIONS,\n type CrmDealStage,\n crmNewRecordListFields,\n type CrmPipeline,\n isPipelineArchived,\n nameSearchKey,\n type PluginApiHandler,\n visibleToTokens,\n} from '@aglyn/aglyn/server'\nimport {\n DEAL_IMPORT_CHUNK_SIZE,\n DEAL_IMPORT_MAX_BODY_BYTES,\n type DealImportChunkResult,\n type DealImportRawRow,\n type DealImportRow,\n type DealImportSkippedRow,\n dealImportNameKey,\n normalizeDealImportRow,\n} from '../model/crm-deal-import'\nimport { crmRecordsQuotaForOrg, firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n type ImportContext,\n ownerDirectory,\n readImportRows,\n resolveImportContext,\n} from './import-context'\n\n/** The default currency a deal carries when the file names none. */\nconst DEFAULT_CURRENCY = 'usd'\n\n/** One pipeline the request can file into, with its stages by name. */\ninterface PipelineChoice {\n id: string\n pipeline: CrmPipeline\n stages: CrmDealStage[]\n}\n\n/** The active pipelines the caller's scope can see, in read order. */\nasync function readPipelines(\n orgRef: FirebaseFirestore.DocumentReference,\n readTokens: readonly string[],\n): Promise<PipelineChoice[]> {\n const snapshot = await orgRef.collection(CRM_COLLECTIONS.pipelines).get()\n const choices: PipelineChoice[] = []\n for (const doc of snapshot.docs) {\n const pipeline = doc.data() as CrmPipeline\n if (isPipelineArchived(pipeline)) continue\n if (!visibleToTokens(pipeline.visibleTo, readTokens)) continue\n const stages = [...(pipeline.stages ?? [])].sort((a, b) => a.order - b.order)\n choices.push({ id: doc.id, pipeline, stages })\n }\n return choices\n}\n\n/**\n * Where one row lands: its pipeline and its stage, or which of the two\n * names the org does not have.\n */\nexport function placeDealImportRow(\n choices: readonly PipelineChoice[],\n row: Pick<DealImportRow, 'pipeline' | 'stage'>,\n):\n | { ok: true; pipeline: PipelineChoice; stage: CrmDealStage }\n | { ok: false; reason: 'unknown-pipeline' | 'unknown-stage' } {\n let pipeline: PipelineChoice | undefined\n if (row.pipeline) {\n const wanted = dealImportNameKey(row.pipeline)\n pipeline = choices.find((choice) => dealImportNameKey(choice.pipeline.name ?? '') === wanted)\n } else {\n pipeline = choices.find((choice) => choice.pipeline.isDefault) ?? (choices.length === 1 ? choices[0] : undefined)\n }\n if (!pipeline) return { ok: false, reason: 'unknown-pipeline' }\n let stage: CrmDealStage | undefined\n if (row.stage) {\n const wanted = dealImportNameKey(row.stage)\n stage = pipeline.stages.find((candidate) => dealImportNameKey(candidate.name ?? '') === wanted)\n } else {\n stage = pipeline.stages.find((candidate) => candidate.kind === 'open') ?? pipeline.stages[0]\n }\n if (!stage) return { ok: false, reason: 'unknown-stage' }\n return { ok: true, pipeline, stage }\n}\n\n/**\n * `POST crm/deals-import` — `{ hostId, rows }` → a {@link DealImportChunkResult}.\n *\n * Rows are written one after another so the band's local count stays\n * honest across the request.\n */\nexport const crmDealsImportHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const read = readImportRows<DealImportRawRow>(req, {\n maxBodyBytes: DEAL_IMPORT_MAX_BODY_BYTES,\n chunkSize: DEAL_IMPORT_CHUNK_SIZE,\n })\n if ('error' in read) return res.status(read.status).json({ error: read.error })\n try {\n const context = await resolveImportContext(req)\n if (context.ok === false) return res.status(context.status).json(context.body)\n\n const skipped: DealImportSkippedRow[] = []\n const dropped: Record<string, number> = {}\n const normalized: { index: number; row: DealImportRow }[] = []\n read.rows.forEach((raw, index) => {\n const verdict = normalizeDealImportRow(raw)\n if (verdict.ok === false) {\n skipped.push({ index, title: verdict.input, reason: verdict.reason })\n return\n }\n for (const entry of verdict.row.dropped) {\n dropped[entry.field] = (dropped[entry.field] ?? 0) + 1\n }\n normalized.push({ index, row: verdict.row })\n })\n\n const orgRef = firebaseAdmin.app().firestore().collection('orgs').doc(context.orgId)\n const [owners, pipelines] = await Promise.all([\n ownerDirectory(\n context.orgId,\n normalized.map((entry) => entry.row),\n ),\n normalized.length ? readPipelines(orgRef, context.readTokens) : Promise.resolve([]),\n ])\n const ownersUnresolved = new Set<string>()\n const deals = orgRef.collection(CRM_COLLECTIONS.deals)\n const stamp = creationStamp(context)\n let counted: { crmRecordsCount: number } | null = null\n let createdHere = 0\n let created = 0\n\n for (const { index, row } of normalized) {\n const placed = placeDealImportRow(pipelines, row)\n if (placed.ok === false) {\n skipped.push({ index, title: row.title, reason: placed.reason })\n continue\n }\n let ownerUid: string | undefined\n if (row.ownerEmail) {\n ownerUid = owners.get(row.ownerEmail)\n if (!ownerUid) ownersUnresolved.add(row.ownerEmail)\n }\n if (!counted) counted = await crmRecordsQuotaForOrg(context.org as never, orgRef)\n const room = checkCrmRecordsQuota(\n context.org as never,\n counted.crmRecordsCount + createdHere,\n )\n if (!room.allowed) {\n skipped.push({ index, title: row.title, reason: 'records-band' })\n continue\n }\n const nowMs = Date.now()\n const { stage } = placed\n const status = stage.kind === 'won' ? 'won' : stage.kind === 'lost' ? 'lost' : 'open'\n await deals.add({\n title: row.title,\n titleLower: nameSearchKey(row.title),\n pipelineId: placed.pipeline.id,\n stageId: stage.id,\n status,\n stageChangedAtMs: nowMs,\n closedAtMs: status === 'open' ? null : nowMs,\n currency: row.currency ?? DEFAULT_CURRENCY,\n ...(typeof row.amountCents === 'number' ? { amountCents: row.amountCents } : {}),\n ...(ownerUid ? { ownerUid } : {}),\n ...(typeof row.expectedCloseAtMs === 'number'\n ? { expectedCloseAtMs: row.expectedCloseAtMs }\n : {}),\n ...(row.notes ? { notes: row.notes } : {}),\n ...stamp,\n // What the Deals list searches and filters by (AGL-3321).\n ...crmNewRecordListFields('deals', { title: row.title, ...stamp }),\n })\n createdHere += 1\n created += 1\n }\n\n const result: DealImportChunkResult = {\n received: read.rows.length,\n created,\n merged: 0,\n skipped: skipped.sort((a, b) => a.index - b.index),\n dropped,\n ownersUnresolved: [...ownersUnresolved],\n }\n return res.status(200).json(result)\n } catch (error) {\n console.error('crm/deals-import failed', error)\n return res.status(500).json({ error: 'The import could not continue.' })\n }\n}\n\n/** The scope, the site, the importer and the clocks every created deal carries. */\nfunction creationStamp(\n context: Extract<ImportContext, { ok: true }>,\n): Record<string, unknown> {\n return {\n hostId: context.hostId,\n visibleTo: context.scopeTokens,\n createdByUid: context.uid,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n }\n}\n"],"names":["checkCrmRecordsQuota","CRM_COLLECTIONS","crmNewRecordListFields","isPipelineArchived","nameSearchKey","visibleToTokens","DEAL_IMPORT_CHUNK_SIZE","DEAL_IMPORT_MAX_BODY_BYTES","dealImportNameKey","normalizeDealImportRow","crmRecordsQuotaForOrg","firebaseAdmin","FieldValue","ownerDirectory","readImportRows","resolveImportContext","DEFAULT_CURRENCY","readPipelines","orgRef","readTokens","snapshot","collection","pipelines","get","choices","doc","docs","pipeline","data","visibleTo","stages","sort","a","b","order","push","id","placeDealImportRow","row","wanted","find","choice","name","isDefault","length","undefined","ok","reason","stage","candidate","kind","crmDealsImportHandler","req","res","method","setHeader","status","json","error","read","maxBodyBytes","chunkSize","context","body","skipped","dropped","normalized","rows","forEach","raw","index","verdict","title","input","entry","field","app","firestore","orgId","owners","Promise","all","map","resolve","ownersUnresolved","Set","deals","stamp","creationStamp","counted","createdHere","created","placed","ownerUid","ownerEmail","add","org","room","crmRecordsCount","allowed","nowMs","Date","now","titleLower","pipelineId","stageId","stageChangedAtMs","closedAtMs","currency","amountCents","expectedCloseAtMs","notes","result","received","merged","console","hostId","scopeTokens","createdByUid","uid","createdAt","serverTimestamp","updatedAt"],"mappings":"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"}
|
|
@@ -1,60 +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
|
-
/** Header naming the row count the server undertook to send. */
|
|
18
|
-
export declare const EXPORT_ROWS_HEADER = "X-Aglyn-Export-Rows";
|
|
19
|
-
/**
|
|
20
|
-
* THE WHOLE COLLECTION, STREAMED (AGL-2662).
|
|
21
|
-
*
|
|
22
|
-
* ## What this fixes
|
|
23
|
-
*
|
|
24
|
-
* Every CRM export until now wrote the rows the LIST had loaded — a
|
|
25
|
-
* `limit(1000)` window for contacts, a `limit(200)` view for tasks — and
|
|
26
|
-
* called the file `contacts.csv`. A merchant with 40,000 contacts got 1,000
|
|
27
|
-
* of them and nothing on screen or in the file said so. `docs/bulk-actions.md`
|
|
28
|
-
* said it in prose, which is not where somebody opening a spreadsheet looks.
|
|
29
|
-
* This is the same file over every row there is, and it carries
|
|
30
|
-
* `X-Aglyn-Export-Rows` so the client can prove what it received is whole —
|
|
31
|
-
* a stream that dies halfway yields a perfectly well-formed shorter file, and
|
|
32
|
-
* nothing about the bytes says they are short.
|
|
33
|
-
*
|
|
34
|
-
* ## Why it is a route and not a bigger client read
|
|
35
|
-
*
|
|
36
|
-
* A browser cannot hold an unbounded collection, and the tier that most
|
|
37
|
-
* needs a complete export is the tier with the most rows. Cursor-paged and
|
|
38
|
-
* streamed, completeness costs neither a hang nor a buffer.
|
|
39
|
-
*
|
|
40
|
-
* ## The columns are the client's columns
|
|
41
|
-
*
|
|
42
|
-
* `crmExportHeader` and `crmExportCells` (`model/crm-csv.ts`) are the
|
|
43
|
-
* writers each section's Export button uses. One feature, one file format.
|
|
44
|
-
*
|
|
45
|
-
* ## The order is by document id, deliberately
|
|
46
|
-
*
|
|
47
|
-
* Firestore DROPS every document missing the ordered field, so ordering by
|
|
48
|
-
* `updatedAt` or `dueAtMs` would silently omit rows written before that
|
|
49
|
-
* field existed — the exact silent loss an export must not have. A document
|
|
50
|
-
* id is on every document and is a total order.
|
|
51
|
-
*
|
|
52
|
-
* ## Scope is enforced here, because nothing else can
|
|
53
|
-
*
|
|
54
|
-
* The Admin SDK bypasses the rules, so `memberScopeTokens` IS the
|
|
55
|
-
* enforcement: an org-wide member reads every row, and a scoped
|
|
56
|
-
* collaborator's query carries their own tokens as the `array-contains-any`
|
|
57
|
-
* the rules would have evaluated. Leads are host data by path, so the
|
|
58
|
-
* equivalent check is whether the caller reaches the site.
|
|
59
|
-
*/
|
|
60
|
-
export declare function crmExportRoute(request: Request): Promise<Response>;
|