@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
|
@@ -25,7 +25,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
25
25
|
* holds, what it is stored as, and the company an address suggests — each
|
|
26
26
|
* rule once, as a function of the document and nothing else, so a spec can
|
|
27
27
|
* pin it without mounting a card.
|
|
28
|
-
*/ import { CONTACT_COMPANY_IDS_FIELD, companyDomainForEmail, isBlankAddress, nameSearchFields, normalizeAddress, normalizeCompanyDomain, normalizeCompanyWebsite, normalizePhone } from "@aglyn/aglyn";
|
|
28
|
+
*/ import { CONTACT_COMPANY_IDS_FIELD, CRM_COMPANY_EMPLOYEES_MAX, CRM_COMPANY_PICKLIST_FIELDS, companyDomainForEmail, isBlankAddress, judgeCrmCompanyPicklists, nameSearchFields, normalizeAddress, normalizeCompanyDomain, normalizeCompanyWebsite, normalizePhone, readCrmCompanyAccountFields } from "@aglyn/aglyn";
|
|
29
|
+
import { amountInputValue, DEFAULT_DEAL_CURRENCY, parseAmountInput } from "./deal-board-model.js";
|
|
29
30
|
/*
|
|
30
31
|
* The mirror field lives with the planner in `@aglyn/aglyn` now, because the
|
|
31
32
|
* server doors that link on capture read it too; it is re-exported here so
|
|
@@ -56,12 +57,37 @@ export const EMPTY_COMPANY_DRAFT = {
|
|
|
56
57
|
domain: '',
|
|
57
58
|
website: '',
|
|
58
59
|
phone: '',
|
|
60
|
+
type: '',
|
|
59
61
|
industry: '',
|
|
62
|
+
rating: '',
|
|
63
|
+
ownership: '',
|
|
64
|
+
accountSource: '',
|
|
60
65
|
ownerUid: '',
|
|
61
66
|
address: {},
|
|
67
|
+
shippingAddress: {},
|
|
68
|
+
annualRevenue: '',
|
|
69
|
+
currency: DEFAULT_DEAL_CURRENCY,
|
|
70
|
+
numberOfEmployees: '',
|
|
71
|
+
fax: '',
|
|
72
|
+
accountNumber: '',
|
|
73
|
+
site: '',
|
|
74
|
+
tickerSymbol: '',
|
|
75
|
+
sicCode: '',
|
|
76
|
+
parentCompanyId: '',
|
|
62
77
|
tags: '',
|
|
63
78
|
notes: ''
|
|
64
79
|
};
|
|
80
|
+
/** How each account field is named in a refusal the drawer shows. */ const ACCOUNT_FIELD_LABELS = {
|
|
81
|
+
annualRevenueCents: 'Annual revenue',
|
|
82
|
+
currency: 'The currency',
|
|
83
|
+
numberOfEmployees: 'Employees',
|
|
84
|
+
fax: 'The fax number',
|
|
85
|
+
accountNumber: 'The account number',
|
|
86
|
+
site: 'The account site',
|
|
87
|
+
tickerSymbol: 'The ticker symbol',
|
|
88
|
+
sicCode: 'The SIC code',
|
|
89
|
+
shippingAddress: 'The shipping address'
|
|
90
|
+
};
|
|
65
91
|
/** The drawer's cap on a company's tags — a contact's, so the two agree. */ export const COMPANY_TAGS_MAX = 20;
|
|
66
92
|
/**
|
|
67
93
|
* A typed tag list as the document stores one: split on `,` or `|`,
|
|
@@ -74,20 +100,41 @@ export const EMPTY_COMPANY_DRAFT = {
|
|
|
74
100
|
].slice(0, COMPANY_TAGS_MAX);
|
|
75
101
|
}
|
|
76
102
|
/** A stored company, as the form should start from it. */ export function companyDraftFrom(company) {
|
|
77
|
-
var _ref, _ref1, _ref2
|
|
103
|
+
var _ref, _ref1, _ref2;
|
|
104
|
+
const text = (value)=>value === null || value === undefined ? '' : String(value);
|
|
78
105
|
return {
|
|
79
|
-
name:
|
|
80
|
-
domain:
|
|
81
|
-
website:
|
|
82
|
-
phone:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
106
|
+
name: text(company == null ? void 0 : company.name),
|
|
107
|
+
domain: text(company == null ? void 0 : company.domain),
|
|
108
|
+
website: text(company == null ? void 0 : company.website),
|
|
109
|
+
phone: text(company == null ? void 0 : company.phone),
|
|
110
|
+
type: text(company == null ? void 0 : company.type),
|
|
111
|
+
industry: text(company == null ? void 0 : company.industry),
|
|
112
|
+
rating: text(company == null ? void 0 : company.rating),
|
|
113
|
+
ownership: text(company == null ? void 0 : company.ownership),
|
|
114
|
+
accountSource: text(company == null ? void 0 : company.accountSource),
|
|
115
|
+
ownerUid: text(company == null ? void 0 : company.ownerUid),
|
|
116
|
+
address: _extends({}, (_ref = company == null ? void 0 : company.address) != null ? _ref : {}),
|
|
117
|
+
shippingAddress: _extends({}, (_ref1 = company == null ? void 0 : company.shippingAddress) != null ? _ref1 : {}),
|
|
118
|
+
annualRevenue: amountInputValue(company == null ? void 0 : company.annualRevenueCents),
|
|
119
|
+
currency: text(company == null ? void 0 : company.currency).toLowerCase() || DEFAULT_DEAL_CURRENCY,
|
|
120
|
+
numberOfEmployees: typeof (company == null ? void 0 : company.numberOfEmployees) === 'number' ? String(company.numberOfEmployees) : '',
|
|
121
|
+
fax: text(company == null ? void 0 : company.fax),
|
|
122
|
+
accountNumber: text(company == null ? void 0 : company.accountNumber),
|
|
123
|
+
site: text(company == null ? void 0 : company.site),
|
|
124
|
+
tickerSymbol: text(company == null ? void 0 : company.tickerSymbol),
|
|
125
|
+
sicCode: text(company == null ? void 0 : company.sicCode),
|
|
126
|
+
parentCompanyId: text(company == null ? void 0 : company.parentCompanyId),
|
|
127
|
+
tags: ((_ref2 = company == null ? void 0 : company.tags) != null ? _ref2 : []).join(', '),
|
|
128
|
+
notes: text(company == null ? void 0 : company.notes)
|
|
88
129
|
};
|
|
89
130
|
}
|
|
90
|
-
|
|
131
|
+
/** A typed head count, as the whole number stored, `null` for blank, `undefined` for unreadable. */ function parseEmployees(input) {
|
|
132
|
+
const cleaned = input.replace(/[\s,]/g, '');
|
|
133
|
+
if (!cleaned) return null;
|
|
134
|
+
if (!/^\d+$/.test(cleaned)) return undefined;
|
|
135
|
+
const value = Number(cleaned);
|
|
136
|
+
return value <= CRM_COMPANY_EMPLOYEES_MAX ? value : undefined;
|
|
137
|
+
}
|
|
91
138
|
const NOTES_MAX = 4000;
|
|
92
139
|
/**
|
|
93
140
|
* The document a draft becomes — normalized, keyed for search, and refused
|
|
@@ -99,7 +146,10 @@ const NOTES_MAX = 4000;
|
|
|
99
146
|
* the one required field, and it is spread through `nameSearchFields` so the
|
|
100
147
|
* list's name filter — a range over `nameLower` — can find the record; a
|
|
101
148
|
* company written without those keys still lists and cannot be searched.
|
|
102
|
-
*/ export function companyDraftFields(draft
|
|
149
|
+
*/ export function companyDraftFields(draft, picklists = {
|
|
150
|
+
lists: {}
|
|
151
|
+
}) {
|
|
152
|
+
var _picklists_current;
|
|
103
153
|
const name = draft.name.trim().replace(/\s+/g, ' ');
|
|
104
154
|
if (!name) return {
|
|
105
155
|
ok: false,
|
|
@@ -142,9 +192,26 @@ const NOTES_MAX = 4000;
|
|
|
142
192
|
} else {
|
|
143
193
|
cleared.push('phone');
|
|
144
194
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
195
|
+
/*
|
|
196
|
+
* THE PICKLISTS (AGL-3514): each a label of the org's list, or the value
|
|
197
|
+
* the company already holds — an industry typed before Industry was a
|
|
198
|
+
* picklist stays until it is changed.
|
|
199
|
+
*/ const judged = judgeCrmCompanyPicklists(picklists.lists, Object.fromEntries(CRM_COMPANY_PICKLIST_FIELDS.map(({ field })=>[
|
|
200
|
+
field,
|
|
201
|
+
draft[field]
|
|
202
|
+
])), {
|
|
203
|
+
current: (_picklists_current = picklists.current) != null ? _picklists_current : {}
|
|
204
|
+
});
|
|
205
|
+
const picklistError = Object.values(judged.errors)[0];
|
|
206
|
+
if (picklistError) return {
|
|
207
|
+
ok: false,
|
|
208
|
+
error: picklistError
|
|
209
|
+
};
|
|
210
|
+
for (const { field } of CRM_COMPANY_PICKLIST_FIELDS){
|
|
211
|
+
const value = judged.values[field];
|
|
212
|
+
if (value) set[field] = value;
|
|
213
|
+
else cleared.push(field);
|
|
214
|
+
}
|
|
148
215
|
const ownerUid = draft.ownerUid.trim();
|
|
149
216
|
if (ownerUid) set['ownerUid'] = ownerUid;
|
|
150
217
|
else cleared.push('ownerUid');
|
|
@@ -152,6 +219,56 @@ const NOTES_MAX = 4000;
|
|
|
152
219
|
// an edit that clears it does not need a delete: `normalizeAddress`
|
|
153
220
|
// already answers `null` for a form with nothing in it.
|
|
154
221
|
set['address'] = isBlankAddress(draft.address) ? null : normalizeAddress(draft.address);
|
|
222
|
+
/*
|
|
223
|
+
* THE ACCOUNT FIELDS (AGL-3514), through the reader every door uses. The
|
|
224
|
+
* revenue is typed in the currency's major unit and stored in its minor
|
|
225
|
+
* one, with the currency beside it; without a revenue there is nothing
|
|
226
|
+
* for a currency to describe.
|
|
227
|
+
*/ const revenueText = draft.annualRevenue.trim();
|
|
228
|
+
const revenue = revenueText ? parseAmountInput(revenueText) : null;
|
|
229
|
+
if (revenueText && revenue === null) {
|
|
230
|
+
return {
|
|
231
|
+
ok: false,
|
|
232
|
+
error: 'The annual revenue is not an amount.'
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const employees = parseEmployees(draft.numberOfEmployees);
|
|
236
|
+
if (employees === undefined) {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
error: `Employees must be a whole number from 0 to ${CRM_COMPANY_EMPLOYEES_MAX.toLocaleString()}.`
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const account = readCrmCompanyAccountFields({
|
|
243
|
+
annualRevenueCents: revenue,
|
|
244
|
+
currency: revenue === null ? null : draft.currency,
|
|
245
|
+
numberOfEmployees: employees,
|
|
246
|
+
fax: draft.fax,
|
|
247
|
+
accountNumber: draft.accountNumber,
|
|
248
|
+
site: draft.site,
|
|
249
|
+
tickerSymbol: draft.tickerSymbol,
|
|
250
|
+
sicCode: draft.sicCode,
|
|
251
|
+
shippingAddress: isBlankAddress(draft.shippingAddress) ? null : draft.shippingAddress
|
|
252
|
+
});
|
|
253
|
+
const [failed] = Object.entries(account.errors);
|
|
254
|
+
if (failed) {
|
|
255
|
+
var _ACCOUNT_FIELD_LABELS_field;
|
|
256
|
+
const [field, message] = failed;
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
error: `${(_ACCOUNT_FIELD_LABELS_field = ACCOUNT_FIELD_LABELS[field]) != null ? _ACCOUNT_FIELD_LABELS_field : field} ${message.charAt(0).toLowerCase()}${message.slice(1)}.`
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
for (const [field, value] of Object.entries(account.values)){
|
|
263
|
+
// The shipping address is nullable like the billing one; the rest are
|
|
264
|
+
// absent when blank, so an edit deletes them.
|
|
265
|
+
if (field === 'shippingAddress') set[field] = value != null ? value : null;
|
|
266
|
+
else if (value === null || value === undefined) cleared.push(field);
|
|
267
|
+
else set[field] = value;
|
|
268
|
+
}
|
|
269
|
+
const parentCompanyId = draft.parentCompanyId.trim();
|
|
270
|
+
if (parentCompanyId) set['parentCompanyId'] = parentCompanyId;
|
|
271
|
+
else cleared.push('parentCompanyId');
|
|
155
272
|
const tags = normalizeCompanyTags(draft.tags);
|
|
156
273
|
if (tags.length) set['tags'] = tags;
|
|
157
274
|
else cleared.push('tags');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/companies.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 * Companies (AGL-2597): the rules every surface that writes a company, or\n * suggests one for a contact, has to agree on.\n *\n * A contact's link to a company is the server's to write (AGL-2804): the\n * link lives in a holder's facet, `crm/contact-update` plans it with\n * `planContactCompanyLink`, and `crm/company-delete` unlinks a company being\n * deleted. What stays here is the company's own document — the draft a form\n * holds, what it is stored as, and the company an address suggests — each\n * rule once, as a function of the document and nothing else, so a spec can\n * pin it without mounting a card.\n */\n\nimport {\n type AglynPostalAddress,\n CONTACT_COMPANY_IDS_FIELD,\n type CrmCompany,\n companyDomainForEmail,\n isBlankAddress,\n nameSearchFields,\n normalizeAddress,\n normalizeCompanyDomain,\n normalizeCompanyWebsite,\n normalizePhone,\n} from '@aglyn/aglyn'\n\n/*\n * The mirror field lives with the planner in `@aglyn/aglyn` now, because the\n * server doors that link on capture read it too; it is re-exported here so\n * the surfaces that always imported it from this module keep one name.\n */\nexport { CONTACT_COMPANY_IDS_FIELD }\n\n/*\n * The delete pass's bound lives with `crm/company-delete`'s contract, which\n * the route and the console both import; re-exported here for the surfaces\n * that say it.\n */\nexport { COMPANY_DETACH_LIMIT } from './company-delete-route'\n\n/** One company as a picker or a suggestion needs it. */\nexport interface CompanyOption {\n id: string\n name: string\n domain?: string | null\n}\n\n/**\n * The company a contact's email address implies, from a list the caller\n * already holds, or `null`.\n *\n * `companyDomainForEmail` already refuses the public mailbox providers, so\n * `jane@gmail.com` suggests nothing rather than whichever company somebody\n * once filed under \"gmail.com\". The match is on the normalized domain both\n * sides store, so `www.acme.com` on the company and `jane@ACME.com` on the\n * contact still meet.\n */\nexport function suggestCompanyForEmail(\n email: unknown,\n companies: readonly CompanyOption[],\n): CompanyOption | null {\n const domain = companyDomainForEmail(email)\n if (!domain) return null\n return companies.find((company) => company.domain === domain) ?? null\n}\n\n/** What the form holds — every field as text, the address as its parts. */\nexport interface CompanyDraft {\n name: string\n domain: string\n website: string\n phone: string\n industry: string\n ownerUid: string\n address: AglynPostalAddress\n /** Comma-separated as typed; stored as the list `companyDraftFields` reads. */\n tags: string\n notes: string\n}\n\nexport const EMPTY_COMPANY_DRAFT: CompanyDraft = {\n name: '',\n domain: '',\n website: '',\n phone: '',\n industry: '',\n ownerUid: '',\n address: {},\n tags: '',\n notes: '',\n}\n\n/** The drawer's cap on a company's tags — a contact's, so the two agree. */\nexport const COMPANY_TAGS_MAX = 20\n\n/**\n * A typed tag list as the document stores one: split on `,` or `|`,\n * lowercased and trimmed, deduplicated, capped — the same shape a contact's\n * tags take, so the bulk bar's \"Add tag\" over companies and the drawer's\n * field write one kind of value.\n */\nexport function normalizeCompanyTags(input: string): string[] {\n return [\n ...new Set(\n String(input ?? '')\n .split(/[|,]/)\n .map((tag) => tag.trim().toLowerCase().slice(0, 60))\n .filter(Boolean),\n ),\n ].slice(0, COMPANY_TAGS_MAX)\n}\n\n/** A stored company, as the form should start from it. */\nexport function companyDraftFrom(\n company: Partial<CrmCompany> | null | undefined,\n): CompanyDraft {\n return {\n name: String(company?.name ?? ''),\n domain: String(company?.domain ?? ''),\n website: String(company?.website ?? ''),\n phone: String(company?.phone ?? ''),\n industry: String(company?.industry ?? ''),\n ownerUid: String(company?.ownerUid ?? ''),\n address: { ...(company?.address ?? {}) },\n tags: (company?.tags ?? []).join(', '),\n notes: String(company?.notes ?? ''),\n }\n}\n\nexport type CompanyDraftResult =\n | {\n ok: true\n /** Fields with a value, ready to `setDoc` or `updateDoc`. */\n set: Record<string, unknown>\n /**\n * Optional fields the draft left blank. A create omits them; an edit\n * has to DELETE them, or clearing the domain would leave the old one\n * stored and still matching contacts by email.\n */\n cleared: string[]\n }\n | { ok: false; error: string }\n\nconst INDUSTRY_MAX = 80\nconst NOTES_MAX = 4000\n\n/**\n * The document a draft becomes — normalized, keyed for search, and refused\n * as a whole when one field cannot be stored honestly.\n *\n * Refused rather than silently dropped: a domain typed as `acme` is not a\n * domain, and storing nothing while the form said something is how a company\n * comes to match no contact by email with no indication why. The name is\n * the one required field, and it is spread through `nameSearchFields` so the\n * list's name filter — a range over `nameLower` — can find the record; a\n * company written without those keys still lists and cannot be searched.\n */\nexport function companyDraftFields(draft: CompanyDraft): CompanyDraftResult {\n const name = draft.name.trim().replace(/\\s+/g, ' ')\n if (!name) return { ok: false, error: 'A company needs a name.' }\n const set: Record<string, unknown> = { ...nameSearchFields(name) }\n const cleared: string[] = []\n\n const rawDomain = draft.domain.trim()\n if (rawDomain) {\n const domain = normalizeCompanyDomain(rawDomain)\n if (!domain) {\n return {\n ok: false,\n error:\n 'The domain should be a bare hostname such as acme.com — a ' +\n 'single word or an IP address is not one.',\n }\n }\n set['domain'] = domain\n } else {\n cleared.push('domain')\n }\n\n const website = normalizeCompanyWebsite(draft.website)\n if (website === null) {\n return { ok: false, error: 'The website is not a web address.' }\n }\n if (website) set['website'] = website\n else cleared.push('website')\n\n const rawPhone = draft.phone.trim()\n if (rawPhone) {\n const phone = normalizePhone(rawPhone)\n if (!phone) {\n return {\n ok: false,\n error:\n 'The phone number could not be read. Include the country code, ' +\n 'as in +1 512 555 0123.',\n }\n }\n set['phone'] = phone\n } else {\n cleared.push('phone')\n }\n\n const industry = draft.industry.trim().slice(0, INDUSTRY_MAX)\n if (industry) set['industry'] = industry\n else cleared.push('industry')\n\n const ownerUid = draft.ownerUid.trim()\n if (ownerUid) set['ownerUid'] = ownerUid\n else cleared.push('ownerUid')\n\n // Nullable rather than absent, so \"no address\" has one stored shape and\n // an edit that clears it does not need a delete: `normalizeAddress`\n // already answers `null` for a form with nothing in it.\n set['address'] = isBlankAddress(draft.address)\n ? null\n : normalizeAddress(draft.address)\n\n const tags = normalizeCompanyTags(draft.tags)\n if (tags.length) set['tags'] = tags\n else cleared.push('tags')\n\n const notes = draft.notes.trim().slice(0, NOTES_MAX)\n if (notes) set['notes'] = notes\n else cleared.push('notes')\n\n return { ok: true, set, cleared }\n}\n"],"names":["CONTACT_COMPANY_IDS_FIELD","companyDomainForEmail","isBlankAddress","nameSearchFields","normalizeAddress","normalizeCompanyDomain","normalizeCompanyWebsite","normalizePhone","COMPANY_DETACH_LIMIT","suggestCompanyForEmail","email","companies","domain","find","company","EMPTY_COMPANY_DRAFT","name","website","phone","industry","ownerUid","address","tags","notes","COMPANY_TAGS_MAX","normalizeCompanyTags","input","Set","String","split","map","tag","trim","toLowerCase","slice","filter","Boolean","companyDraftFrom","join","INDUSTRY_MAX","NOTES_MAX","companyDraftFields","draft","replace","ok","error","set","cleared","rawDomain","push","rawPhone","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/companies.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 * Companies (AGL-2597): the rules every surface that writes a company, or\n * suggests one for a contact, has to agree on.\n *\n * A contact's link to a company is the server's to write (AGL-2804): the\n * link lives in a holder's facet, `crm/contact-update` plans it with\n * `planContactCompanyLink`, and `crm/company-delete` unlinks a company being\n * deleted. What stays here is the company's own document — the draft a form\n * holds, what it is stored as, and the company an address suggests — each\n * rule once, as a function of the document and nothing else, so a spec can\n * pin it without mounting a card.\n */\n\nimport {\n type AglynPostalAddress,\n CONTACT_COMPANY_IDS_FIELD,\n CRM_COMPANY_EMPLOYEES_MAX,\n CRM_COMPANY_PICKLIST_FIELDS,\n type CrmCompany,\n type CrmCompanyPicklistField,\n type CrmPicklist,\n type CrmPicklistId,\n companyDomainForEmail,\n isBlankAddress,\n judgeCrmCompanyPicklists,\n nameSearchFields,\n normalizeAddress,\n normalizeCompanyDomain,\n normalizeCompanyWebsite,\n normalizePhone,\n readCrmCompanyAccountFields,\n} from '@aglyn/aglyn'\nimport { amountInputValue, DEFAULT_DEAL_CURRENCY, parseAmountInput } from './deal-board-model'\n\n/*\n * The mirror field lives with the planner in `@aglyn/aglyn` now, because the\n * server doors that link on capture read it too; it is re-exported here so\n * the surfaces that always imported it from this module keep one name.\n */\nexport { CONTACT_COMPANY_IDS_FIELD }\n\n/*\n * The delete pass's bound lives with `crm/company-delete`'s contract, which\n * the route and the console both import; re-exported here for the surfaces\n * that say it.\n */\nexport { COMPANY_DETACH_LIMIT } from './company-delete-route'\n\n/** One company as a picker or a suggestion needs it. */\nexport interface CompanyOption {\n id: string\n name: string\n domain?: string | null\n}\n\n/**\n * The company a contact's email address implies, from a list the caller\n * already holds, or `null`.\n *\n * `companyDomainForEmail` already refuses the public mailbox providers, so\n * `jane@gmail.com` suggests nothing rather than whichever company somebody\n * once filed under \"gmail.com\". The match is on the normalized domain both\n * sides store, so `www.acme.com` on the company and `jane@ACME.com` on the\n * contact still meet.\n */\nexport function suggestCompanyForEmail(\n email: unknown,\n companies: readonly CompanyOption[],\n): CompanyOption | null {\n const domain = companyDomainForEmail(email)\n if (!domain) return null\n return companies.find((company) => company.domain === domain) ?? null\n}\n\n/** What the form holds — every field as text, the addresses as their parts. */\nexport interface CompanyDraft {\n name: string\n domain: string\n website: string\n phone: string\n /** Each company picklist's label (AGL-3514), `''` for none. */\n type: string\n industry: string\n rating: string\n ownership: string\n accountSource: string\n ownerUid: string\n /** The billing address. */\n address: AglynPostalAddress\n shippingAddress: AglynPostalAddress\n /** As typed, in the currency's major unit — `parseAmountInput` reads it. */\n annualRevenue: string\n /** Lowercase ISO 4217. */\n currency: string\n numberOfEmployees: string\n fax: string\n accountNumber: string\n site: string\n tickerSymbol: string\n sicCode: string\n /** The parent company's id, `''` for none. */\n parentCompanyId: string\n /** Comma-separated as typed; stored as the list `companyDraftFields` reads. */\n tags: string\n notes: string\n}\n\nexport const EMPTY_COMPANY_DRAFT: CompanyDraft = {\n name: '',\n domain: '',\n website: '',\n phone: '',\n type: '',\n industry: '',\n rating: '',\n ownership: '',\n accountSource: '',\n ownerUid: '',\n address: {},\n shippingAddress: {},\n annualRevenue: '',\n currency: DEFAULT_DEAL_CURRENCY,\n numberOfEmployees: '',\n fax: '',\n accountNumber: '',\n site: '',\n tickerSymbol: '',\n sicCode: '',\n parentCompanyId: '',\n tags: '',\n notes: '',\n}\n\n/** How each account field is named in a refusal the drawer shows. */\nconst ACCOUNT_FIELD_LABELS: Record<string, string> = {\n annualRevenueCents: 'Annual revenue',\n currency: 'The currency',\n numberOfEmployees: 'Employees',\n fax: 'The fax number',\n accountNumber: 'The account number',\n site: 'The account site',\n tickerSymbol: 'The ticker symbol',\n sicCode: 'The SIC code',\n shippingAddress: 'The shipping address',\n}\n\n/** The drawer's cap on a company's tags — a contact's, so the two agree. */\nexport const COMPANY_TAGS_MAX = 20\n\n/**\n * A typed tag list as the document stores one: split on `,` or `|`,\n * lowercased and trimmed, deduplicated, capped — the same shape a contact's\n * tags take, so the bulk bar's \"Add tag\" over companies and the drawer's\n * field write one kind of value.\n */\nexport function normalizeCompanyTags(input: string): string[] {\n return [\n ...new Set(\n String(input ?? '')\n .split(/[|,]/)\n .map((tag) => tag.trim().toLowerCase().slice(0, 60))\n .filter(Boolean),\n ),\n ].slice(0, COMPANY_TAGS_MAX)\n}\n\n/** A stored company, as the form should start from it. */\nexport function companyDraftFrom(\n company: Partial<CrmCompany> | null | undefined,\n): CompanyDraft {\n const text = (value: unknown) => (value === null || value === undefined ? '' : String(value))\n return {\n name: text(company?.name),\n domain: text(company?.domain),\n website: text(company?.website),\n phone: text(company?.phone),\n type: text(company?.type),\n industry: text(company?.industry),\n rating: text(company?.rating),\n ownership: text(company?.ownership),\n accountSource: text(company?.accountSource),\n ownerUid: text(company?.ownerUid),\n address: { ...(company?.address ?? {}) },\n shippingAddress: { ...(company?.shippingAddress ?? {}) },\n annualRevenue: amountInputValue(company?.annualRevenueCents),\n currency: text(company?.currency).toLowerCase() || DEFAULT_DEAL_CURRENCY,\n numberOfEmployees:\n typeof company?.numberOfEmployees === 'number' ? String(company.numberOfEmployees) : '',\n fax: text(company?.fax),\n accountNumber: text(company?.accountNumber),\n site: text(company?.site),\n tickerSymbol: text(company?.tickerSymbol),\n sicCode: text(company?.sicCode),\n parentCompanyId: text(company?.parentCompanyId),\n tags: (company?.tags ?? []).join(', '),\n notes: text(company?.notes),\n }\n}\n\n/**\n * What a draft's picklist fields are judged against (AGL-3514): the org's\n * lists the form read, and the company as stored — whose own values are\n * kept even when a list no longer offers them. Absent, each field is\n * judged against its standard values alone.\n */\nexport interface CompanyDraftPicklists {\n lists: Readonly<Partial<Record<CrmPicklistId, CrmPicklist>>>\n current?: Partial<CrmCompany> | null\n}\n\n/** A typed head count, as the whole number stored, `null` for blank, `undefined` for unreadable. */\nfunction parseEmployees(input: string): number | null | undefined {\n const cleaned = input.replace(/[\\s,]/g, '')\n if (!cleaned) return null\n if (!/^\\d+$/.test(cleaned)) return undefined\n const value = Number(cleaned)\n return value <= CRM_COMPANY_EMPLOYEES_MAX ? value : undefined\n}\n\nexport type CompanyDraftResult =\n | {\n ok: true\n /** Fields with a value, ready to `setDoc` or `updateDoc`. */\n set: Record<string, unknown>\n /**\n * Optional fields the draft left blank. A create omits them; an edit\n * has to DELETE them, or clearing the domain would leave the old one\n * stored and still matching contacts by email.\n */\n cleared: string[]\n }\n | { ok: false; error: string }\n\nconst NOTES_MAX = 4000\n\n/**\n * The document a draft becomes — normalized, keyed for search, and refused\n * as a whole when one field cannot be stored honestly.\n *\n * Refused rather than silently dropped: a domain typed as `acme` is not a\n * domain, and storing nothing while the form said something is how a company\n * comes to match no contact by email with no indication why. The name is\n * the one required field, and it is spread through `nameSearchFields` so the\n * list's name filter — a range over `nameLower` — can find the record; a\n * company written without those keys still lists and cannot be searched.\n */\nexport function companyDraftFields(\n draft: CompanyDraft,\n picklists: CompanyDraftPicklists = { lists: {} },\n): CompanyDraftResult {\n const name = draft.name.trim().replace(/\\s+/g, ' ')\n if (!name) return { ok: false, error: 'A company needs a name.' }\n const set: Record<string, unknown> = { ...nameSearchFields(name) }\n const cleared: string[] = []\n\n const rawDomain = draft.domain.trim()\n if (rawDomain) {\n const domain = normalizeCompanyDomain(rawDomain)\n if (!domain) {\n return {\n ok: false,\n error:\n 'The domain should be a bare hostname such as acme.com — a ' +\n 'single word or an IP address is not one.',\n }\n }\n set['domain'] = domain\n } else {\n cleared.push('domain')\n }\n\n const website = normalizeCompanyWebsite(draft.website)\n if (website === null) {\n return { ok: false, error: 'The website is not a web address.' }\n }\n if (website) set['website'] = website\n else cleared.push('website')\n\n const rawPhone = draft.phone.trim()\n if (rawPhone) {\n const phone = normalizePhone(rawPhone)\n if (!phone) {\n return {\n ok: false,\n error:\n 'The phone number could not be read. Include the country code, ' +\n 'as in +1 512 555 0123.',\n }\n }\n set['phone'] = phone\n } else {\n cleared.push('phone')\n }\n\n /*\n * THE PICKLISTS (AGL-3514): each a label of the org's list, or the value\n * the company already holds — an industry typed before Industry was a\n * picklist stays until it is changed.\n */\n const judged = judgeCrmCompanyPicklists(\n picklists.lists,\n Object.fromEntries(\n CRM_COMPANY_PICKLIST_FIELDS.map(({ field }) => [field, draft[field]]),\n ),\n { current: picklists.current ?? {} },\n )\n const picklistError = Object.values(judged.errors)[0]\n if (picklistError) return { ok: false, error: picklistError }\n for (const { field } of CRM_COMPANY_PICKLIST_FIELDS) {\n const value = judged.values[field as CrmCompanyPicklistField]\n if (value) set[field] = value\n else cleared.push(field)\n }\n\n const ownerUid = draft.ownerUid.trim()\n if (ownerUid) set['ownerUid'] = ownerUid\n else cleared.push('ownerUid')\n\n // Nullable rather than absent, so \"no address\" has one stored shape and\n // an edit that clears it does not need a delete: `normalizeAddress`\n // already answers `null` for a form with nothing in it.\n set['address'] = isBlankAddress(draft.address)\n ? null\n : normalizeAddress(draft.address)\n\n /*\n * THE ACCOUNT FIELDS (AGL-3514), through the reader every door uses. The\n * revenue is typed in the currency's major unit and stored in its minor\n * one, with the currency beside it; without a revenue there is nothing\n * for a currency to describe.\n */\n const revenueText = draft.annualRevenue.trim()\n const revenue = revenueText ? parseAmountInput(revenueText) : null\n if (revenueText && revenue === null) {\n return { ok: false, error: 'The annual revenue is not an amount.' }\n }\n const employees = parseEmployees(draft.numberOfEmployees)\n if (employees === undefined) {\n return {\n ok: false,\n error: `Employees must be a whole number from 0 to ${CRM_COMPANY_EMPLOYEES_MAX.toLocaleString()}.`,\n }\n }\n const account = readCrmCompanyAccountFields({\n annualRevenueCents: revenue,\n currency: revenue === null ? null : draft.currency,\n numberOfEmployees: employees,\n fax: draft.fax,\n accountNumber: draft.accountNumber,\n site: draft.site,\n tickerSymbol: draft.tickerSymbol,\n sicCode: draft.sicCode,\n shippingAddress: isBlankAddress(draft.shippingAddress) ? null : draft.shippingAddress,\n })\n const [failed] = Object.entries(account.errors)\n if (failed) {\n const [field, message] = failed\n return {\n ok: false,\n error: `${ACCOUNT_FIELD_LABELS[field] ?? field} ${message.charAt(0).toLowerCase()}${message.slice(1)}.`,\n }\n }\n for (const [field, value] of Object.entries(account.values)) {\n // The shipping address is nullable like the billing one; the rest are\n // absent when blank, so an edit deletes them.\n if (field === 'shippingAddress') set[field] = value ?? null\n else if (value === null || value === undefined) cleared.push(field)\n else set[field] = value\n }\n\n const parentCompanyId = draft.parentCompanyId.trim()\n if (parentCompanyId) set['parentCompanyId'] = parentCompanyId\n else cleared.push('parentCompanyId')\n\n const tags = normalizeCompanyTags(draft.tags)\n if (tags.length) set['tags'] = tags\n else cleared.push('tags')\n\n const notes = draft.notes.trim().slice(0, NOTES_MAX)\n if (notes) set['notes'] = notes\n else cleared.push('notes')\n\n return { ok: true, set, cleared }\n}\n"],"names":["CONTACT_COMPANY_IDS_FIELD","CRM_COMPANY_EMPLOYEES_MAX","CRM_COMPANY_PICKLIST_FIELDS","companyDomainForEmail","isBlankAddress","judgeCrmCompanyPicklists","nameSearchFields","normalizeAddress","normalizeCompanyDomain","normalizeCompanyWebsite","normalizePhone","readCrmCompanyAccountFields","amountInputValue","DEFAULT_DEAL_CURRENCY","parseAmountInput","COMPANY_DETACH_LIMIT","suggestCompanyForEmail","email","companies","domain","find","company","EMPTY_COMPANY_DRAFT","name","website","phone","type","industry","rating","ownership","accountSource","ownerUid","address","shippingAddress","annualRevenue","currency","numberOfEmployees","fax","accountNumber","site","tickerSymbol","sicCode","parentCompanyId","tags","notes","ACCOUNT_FIELD_LABELS","annualRevenueCents","COMPANY_TAGS_MAX","normalizeCompanyTags","input","Set","String","split","map","tag","trim","toLowerCase","slice","filter","Boolean","companyDraftFrom","text","value","undefined","join","parseEmployees","cleaned","replace","test","Number","NOTES_MAX","companyDraftFields","draft","picklists","lists","ok","error","set","cleared","rawDomain","push","rawPhone","judged","Object","fromEntries","field","current","picklistError","values","errors","revenueText","revenue","employees","toLocaleString","account","failed","entries","message","charAt","length"],"mappings":"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"}
|
|
@@ -40,7 +40,16 @@
|
|
|
40
40
|
* that is otherwise lossless.
|
|
41
41
|
* - A recorded REFUSAL of marketing mail on either record stands on the
|
|
42
42
|
* survivor. The merge asserts the two are one person, and that person
|
|
43
|
-
* said no.
|
|
43
|
+
* said no. So does a do-not-call (AGL-3515), for the same reason.
|
|
44
|
+
* - The name is ONE value made of three fields (AGL-3515): a holder's
|
|
45
|
+
* `name`, `firstName` and `lastName` come across together, from the
|
|
46
|
+
* survivor when it names the person at all and from the merged record
|
|
47
|
+
* only when it does not — so a survivor called "Bob" never gains a first
|
|
48
|
+
* name of "Robert" that its name does not show.
|
|
49
|
+
* - A reports-to that would point at the survivor itself — the survivor's
|
|
50
|
+
* pointing at the merged record, or the merged record's at the survivor —
|
|
51
|
+
* is cleared (`null`) rather than kept as a person reporting to
|
|
52
|
+
* themselves.
|
|
44
53
|
*
|
|
45
54
|
* Applied per holder: each group's facet on the merged record is folded
|
|
46
55
|
* into the same group's facet on the survivor, and a group only the merged
|
|
@@ -58,6 +67,11 @@
|
|
|
58
67
|
* the rule above needs.
|
|
59
68
|
*/
|
|
60
69
|
import { type ContactInteraction } from '@aglyn/aglyn/app-utils/contacts';
|
|
70
|
+
/** The two records' ids, when the caller knows them — what a self-pointer is judged by. */
|
|
71
|
+
export interface ContactMergeIds {
|
|
72
|
+
survivorId: string;
|
|
73
|
+
mergedId: string;
|
|
74
|
+
}
|
|
61
75
|
export interface ContactMergePlan {
|
|
62
76
|
/**
|
|
63
77
|
* What the survivor becomes, as a merge-`set` payload: every derived
|
|
@@ -97,14 +111,14 @@ export declare function mergeInteractions(survivor: readonly ContactInteraction[
|
|
|
97
111
|
* absent, when only the merged record was held by this group — and `merged`
|
|
98
112
|
* the same holder's facet on the record being folded in.
|
|
99
113
|
*/
|
|
100
|
-
export declare function mergeContactFacet(survivor: Doc | undefined, merged: Doc | undefined): Doc;
|
|
114
|
+
export declare function mergeContactFacet(survivor: Doc | undefined, merged: Doc | undefined, ids?: ContactMergeIds): Doc;
|
|
101
115
|
/**
|
|
102
116
|
* The plan for folding `merged` into `survivor`.
|
|
103
117
|
*
|
|
104
118
|
* Both are the stored documents as read. The survivor's `email` is the
|
|
105
119
|
* identity that stands; the merged record's becomes an alternate.
|
|
106
120
|
*/
|
|
107
|
-
export declare function planContactMerge(survivor: Doc, merged: Doc): ContactMergePlan;
|
|
121
|
+
export declare function planContactMerge(survivor: Doc, merged: Doc, ids?: ContactMergeIds): ContactMergePlan;
|
|
108
122
|
/** Where a previewed value comes from. */
|
|
109
123
|
export type ContactMergeSource = 'survivor' | 'merged' | 'both' | 'none';
|
|
110
124
|
/** One line of the side-by-side the dialog shows. */
|
|
@@ -122,7 +136,17 @@ export interface ContactMergePreviewRow {
|
|
|
122
136
|
export interface ContactMergePreviewOptions {
|
|
123
137
|
/** The owner's display name for a uid; the uid itself when absent. */
|
|
124
138
|
memberName?: (uid: string) => string;
|
|
139
|
+
/**
|
|
140
|
+
* The two records' ids, so the preview applies the rule the write does
|
|
141
|
+
* to a reports-to (AGL-3537): a pointer at either record would be the
|
|
142
|
+
* person reporting to themselves, and becomes nobody.
|
|
143
|
+
*/
|
|
144
|
+
ids?: ContactMergeIds;
|
|
145
|
+
/** A contact's name for an id, for Reports to; the id itself when absent. */
|
|
146
|
+
contactName?: (contactId: string) => string;
|
|
125
147
|
}
|
|
148
|
+
/** What Reports to reads when the merge leaves the person reporting to nobody because the pointer was at themselves. */
|
|
149
|
+
export declare const CONTACT_MERGE_REPORTS_TO_SELF = "Nobody \u2014 it named one of these two records";
|
|
126
150
|
/**
|
|
127
151
|
* The merge as the viewing group would see it, field by field.
|
|
128
152
|
*
|
|
@@ -40,7 +40,16 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
40
40
|
* that is otherwise lossless.
|
|
41
41
|
* - A recorded REFUSAL of marketing mail on either record stands on the
|
|
42
42
|
* survivor. The merge asserts the two are one person, and that person
|
|
43
|
-
* said no.
|
|
43
|
+
* said no. So does a do-not-call (AGL-3515), for the same reason.
|
|
44
|
+
* - The name is ONE value made of three fields (AGL-3515): a holder's
|
|
45
|
+
* `name`, `firstName` and `lastName` come across together, from the
|
|
46
|
+
* survivor when it names the person at all and from the merged record
|
|
47
|
+
* only when it does not — so a survivor called "Bob" never gains a first
|
|
48
|
+
* name of "Robert" that its name does not show.
|
|
49
|
+
* - A reports-to that would point at the survivor itself — the survivor's
|
|
50
|
+
* pointing at the merged record, or the merged record's at the survivor —
|
|
51
|
+
* is cleared (`null`) rather than kept as a person reporting to
|
|
52
|
+
* themselves.
|
|
44
53
|
*
|
|
45
54
|
* Applied per holder: each group's facet on the merged record is folded
|
|
46
55
|
* into the same group's facet on the survivor, and a group only the merged
|
|
@@ -76,6 +85,11 @@ import { nameSearchFields } from "@aglyn/aglyn/app-utils/name-search";
|
|
|
76
85
|
/** Facet instants where the earlier one is the truth. */ const EARLIEST_FIELDS = [
|
|
77
86
|
'firstPurchaseAtMs'
|
|
78
87
|
];
|
|
88
|
+
/** The facet fields that together name the person, merged as one (AGL-3515). */ const NAME_FIELDS = [
|
|
89
|
+
'name',
|
|
90
|
+
'firstName',
|
|
91
|
+
'lastName'
|
|
92
|
+
];
|
|
79
93
|
/** Whether a value counts as "nothing here" for the fill rule. */ function isEmpty(value) {
|
|
80
94
|
if (value === undefined || value === null) return true;
|
|
81
95
|
if (typeof value === 'string') return value.trim() === '';
|
|
@@ -157,7 +171,21 @@ function number(value) {
|
|
|
157
171
|
* `survivor` is that holder's facet on the surviving record — possibly
|
|
158
172
|
* absent, when only the merged record was held by this group — and `merged`
|
|
159
173
|
* the same holder's facet on the record being folded in.
|
|
160
|
-
*/ export function mergeContactFacet(survivor, merged) {
|
|
174
|
+
*/ export function mergeContactFacet(survivor, merged, ids) {
|
|
175
|
+
const out = foldContactFacet(survivor, merged);
|
|
176
|
+
/*
|
|
177
|
+
* Nobody reports to themselves — see the rule above. Written as `null`
|
|
178
|
+
* rather than left out, because the plan is a merge-`set`, which keeps a
|
|
179
|
+
* stored key it is not given.
|
|
180
|
+
*/ if (ids && [
|
|
181
|
+
ids.survivorId,
|
|
182
|
+
ids.mergedId
|
|
183
|
+
].includes(text(out['reportsToContactId']))) {
|
|
184
|
+
out['reportsToContactId'] = null;
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
function foldContactFacet(survivor, merged) {
|
|
161
189
|
var _b_sources, _a_sources;
|
|
162
190
|
if (!merged) return _extends({}, survivor != null ? survivor : {});
|
|
163
191
|
if (!survivor) return _extends({}, merged);
|
|
@@ -166,6 +194,14 @@ function number(value) {
|
|
|
166
194
|
for (const [key, value] of Object.entries(survivor)){
|
|
167
195
|
out[key] = fill(value, merged[key]);
|
|
168
196
|
}
|
|
197
|
+
// The name's three fields, from one record — see the rule above.
|
|
198
|
+
const named = NAME_FIELDS.some((key)=>!isEmpty(survivor[key]));
|
|
199
|
+
for (const key of NAME_FIELDS){
|
|
200
|
+
const value = named ? survivor[key] : merged[key];
|
|
201
|
+
if (isEmpty(value)) delete out[key];
|
|
202
|
+
else out[key] = value;
|
|
203
|
+
}
|
|
204
|
+
if (survivor['doNotCall'] === true || merged['doNotCall'] === true) out['doNotCall'] = true;
|
|
169
205
|
const a = survivor;
|
|
170
206
|
const b = merged;
|
|
171
207
|
out['sources'] = _extends({}, (_b_sources = b.sources) != null ? _b_sources : {}, (_a_sources = a.sources) != null ? _a_sources : {});
|
|
@@ -234,7 +270,7 @@ function mapOf(value) {
|
|
|
234
270
|
*
|
|
235
271
|
* Both are the stored documents as read. The survivor's `email` is the
|
|
236
272
|
* identity that stands; the merged record's becomes an alternate.
|
|
237
|
-
*/ export function planContactMerge(survivor, merged) {
|
|
273
|
+
*/ export function planContactMerge(survivor, merged, ids) {
|
|
238
274
|
var _normalizeContactEmail;
|
|
239
275
|
const survivorEmail = (_normalizeContactEmail = normalizeContactEmail(survivor['email'])) != null ? _normalizeContactEmail : '';
|
|
240
276
|
const mergedEmails = contactEmails(merged);
|
|
@@ -313,7 +349,14 @@ function mapOf(value) {
|
|
|
313
349
|
const mergedFacets = facetsOf(merged);
|
|
314
350
|
const facets = {};
|
|
315
351
|
for (const [groupId, facet] of Object.entries(mergedFacets)){
|
|
316
|
-
facets[groupId] = mergeContactFacet(survivorFacets[groupId], facet);
|
|
352
|
+
facets[groupId] = mergeContactFacet(survivorFacets[groupId], facet, ids);
|
|
353
|
+
}
|
|
354
|
+
// A survivor facet the merged record did not hold may still point at it.
|
|
355
|
+
if (ids) {
|
|
356
|
+
for (const [groupId, facet] of Object.entries(survivorFacets)){
|
|
357
|
+
if (groupId in facets || facet['reportsToContactId'] !== ids.mergedId) continue;
|
|
358
|
+
facets[groupId] = mergeContactFacet(facet, undefined, ids);
|
|
359
|
+
}
|
|
317
360
|
}
|
|
318
361
|
if (Object.keys(facets).length) out[CONTACT_FACETS_FIELD] = facets;
|
|
319
362
|
return {
|
|
@@ -327,6 +370,7 @@ function mapOf(value) {
|
|
|
327
370
|
visibleTo
|
|
328
371
|
};
|
|
329
372
|
}
|
|
373
|
+
/** What Reports to reads when the merge leaves the person reporting to nobody because the pointer was at themselves. */ export const CONTACT_MERGE_REPORTS_TO_SELF = 'Nobody — it named one of these two records';
|
|
330
374
|
function addressLine(value) {
|
|
331
375
|
const address = mapOf(value);
|
|
332
376
|
return [
|
|
@@ -355,8 +399,8 @@ function sourceOf(survivor, merged, result) {
|
|
|
355
399
|
* result the plan would write, so the dialog is a rendering and not a second
|
|
356
400
|
* opinion.
|
|
357
401
|
*/ export function contactMergePreview(survivor, merged, groupId, options = {}) {
|
|
358
|
-
var _options_memberName;
|
|
359
|
-
const plan = planContactMerge(survivor, merged);
|
|
402
|
+
var _options_memberName, _options_contactName;
|
|
403
|
+
const plan = planContactMerge(survivor, merged, options.ids);
|
|
360
404
|
const after = _extends({}, survivor, plan.survivor, {
|
|
361
405
|
[CONTACT_FACETS_FIELD]: _extends({}, facetsOf(survivor), mapOf(plan.survivor[CONTACT_FACETS_FIELD]))
|
|
362
406
|
});
|
|
@@ -366,7 +410,23 @@ function sourceOf(survivor, merged, result) {
|
|
|
366
410
|
const memberName = (_options_memberName = options.memberName) != null ? _options_memberName : (uid)=>uid;
|
|
367
411
|
const stage = (value)=>typeof value === 'string' && value in CONTACT_LIFECYCLE_STAGE_LABELS ? CONTACT_LIFECYCLE_STAGE_LABELS[value] : '';
|
|
368
412
|
const owner = (value)=>text(value) ? memberName(text(value)) : '';
|
|
413
|
+
const contactName = (_options_contactName = options.contactName) != null ? _options_contactName : (contactId)=>contactId;
|
|
414
|
+
const person = (value)=>text(value) ? contactName(text(value)) : '';
|
|
415
|
+
/*
|
|
416
|
+
* Reports to (AGL-3537), as the write leaves it: the kept record's own
|
|
417
|
+
* manager stands, an empty one fills from the other record's, and a
|
|
418
|
+
* pointer at either of the two records — which after the merge are one
|
|
419
|
+
* person — becomes nobody, and says why.
|
|
420
|
+
*/ const pair = options.ids ? [
|
|
421
|
+
options.ids.survivorId,
|
|
422
|
+
options.ids.mergedId
|
|
423
|
+
] : [];
|
|
424
|
+
const reportsToResult = person(c.reportsToContactId) || ([
|
|
425
|
+
a.reportsToContactId,
|
|
426
|
+
b.reportsToContactId
|
|
427
|
+
].some((id)=>pair.includes(text(id))) ? CONTACT_MERGE_REPORTS_TO_SELF : '');
|
|
369
428
|
const list = (value)=>strings(value).join(', ');
|
|
429
|
+
const flag = (value)=>value === true ? 'Yes' : '';
|
|
370
430
|
const money = (facet)=>{
|
|
371
431
|
var _facet_ltvCents;
|
|
372
432
|
return facet.ordersCount ? `${facet.ordersCount} · $${(((_facet_ltvCents = facet.ltvCents) != null ? _facet_ltvCents : 0) / 100).toFixed(2)}` : '';
|
|
@@ -382,14 +442,28 @@ function sourceOf(survivor, merged, result) {
|
|
|
382
442
|
const rows = [
|
|
383
443
|
row('name', 'Name', contactDisplayName(survivor, groupId), contactDisplayName(merged, groupId), contactDisplayName(after, groupId)),
|
|
384
444
|
row('email', 'Email', contactEmails(survivor).join(', '), contactEmails(merged).join(', '), plan.emails.join(', ')),
|
|
445
|
+
row('salutation', 'Salutation', text(a.salutation), text(b.salutation), text(c.salutation)),
|
|
385
446
|
row('phone', 'Phone', text(a.phone), text(b.phone), text(c.phone)),
|
|
447
|
+
row('mobilePhone', 'Mobile phone', text(a.mobilePhone), text(b.mobilePhone), text(c.mobilePhone)),
|
|
448
|
+
row('homePhone', 'Home phone', text(a.homePhone), text(b.homePhone), text(c.homePhone)),
|
|
449
|
+
row('otherPhone', 'Other phone', text(a.otherPhone), text(b.otherPhone), text(c.otherPhone)),
|
|
450
|
+
row('fax', 'Fax', text(a.fax), text(b.fax), text(c.fax)),
|
|
451
|
+
row('doNotCall', 'Do not call', flag(a.doNotCall), flag(b.doNotCall), flag(c.doNotCall)),
|
|
386
452
|
row('jobTitle', 'Job title', text(a.jobTitle), text(b.jobTitle), text(c.jobTitle)),
|
|
453
|
+
row('department', 'Department', text(a.department), text(b.department), text(c.department)),
|
|
454
|
+
_extends({}, row('reportsTo', 'Reports to', person(a.reportsToContactId), person(b.reportsToContactId), reportsToResult), reportsToResult === CONTACT_MERGE_REPORTS_TO_SELF ? {
|
|
455
|
+
from: 'none'
|
|
456
|
+
} : {}),
|
|
387
457
|
row('company', 'Company', text(a.companyName), text(b.companyName), text(c.companyName)),
|
|
388
458
|
row('lifecycleStage', 'Stage', stage(a.lifecycleStage), stage(b.lifecycleStage), stage(c.lifecycleStage)),
|
|
389
459
|
row('owner', 'Owner', owner(a.ownerUid), owner(b.ownerUid), owner(c.ownerUid)),
|
|
390
460
|
row('tags', 'Tags', list(a.tags), list(b.tags), list(c.tags)),
|
|
391
461
|
row('notes', 'Notes', text(a.notes), text(b.notes), text(c.notes)),
|
|
392
|
-
row('address', '
|
|
462
|
+
row('address', 'Mailing address', addressLine(a.address), addressLine(b.address), addressLine(c.address)),
|
|
463
|
+
row('otherAddress', 'Other address', addressLine(a.otherAddress), addressLine(b.otherAddress), addressLine(c.otherAddress)),
|
|
464
|
+
row('birthdate', 'Birthdate', text(a.birthdate), text(b.birthdate), text(c.birthdate)),
|
|
465
|
+
row('assistant', 'Assistant', text(a.assistantName), text(b.assistantName), text(c.assistantName)),
|
|
466
|
+
row('assistantPhone', 'Assistant phone', text(a.assistantPhone), text(b.assistantPhone), text(c.assistantPhone)),
|
|
393
467
|
row('orders', 'Orders', money(a), money(b), money(c)),
|
|
394
468
|
row('timeline', 'Timeline entries', a.interactions.length ? String(a.interactions.length) : '', b.interactions.length ? String(b.interactions.length) : '', c.interactions.length ? String(c.interactions.length) : '')
|
|
395
469
|
];
|