@aglyn/plugins-crm 1.0.0-beta.296 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/package.json +14 -14
  2. package/src/lib/components/activity-link-fields.d.ts +34 -0
  3. package/src/lib/components/activity-link-fields.js +141 -0
  4. package/src/lib/components/activity-link-fields.js.map +1 -0
  5. package/src/lib/components/activity-list.d.ts +8 -0
  6. package/src/lib/components/activity-list.js +4 -2
  7. package/src/lib/components/activity-list.js.map +1 -1
  8. package/src/lib/components/activity-queries.d.ts +7 -2
  9. package/src/lib/components/activity-queries.js +7 -3
  10. package/src/lib/components/activity-queries.js.map +1 -1
  11. package/src/lib/components/company-contact-roles-card.d.ts +42 -0
  12. package/src/lib/components/company-contact-roles-card.js +135 -0
  13. package/src/lib/components/company-contact-roles-card.js.map +1 -0
  14. package/src/lib/components/company-detail-page.js +12 -1
  15. package/src/lib/components/company-detail-page.js.map +1 -1
  16. package/src/lib/components/company-edit-drawer.js +9 -6
  17. package/src/lib/components/company-edit-drawer.js.map +1 -1
  18. package/src/lib/components/company-picker.js +4 -2
  19. package/src/lib/components/company-picker.js.map +1 -1
  20. package/src/lib/components/contact-associations-card.js +15 -1
  21. package/src/lib/components/contact-associations-card.js.map +1 -1
  22. package/src/lib/components/contact-detail-page.js +13 -14
  23. package/src/lib/components/contact-detail-page.js.map +1 -1
  24. package/src/lib/components/contact-list-columns.js +5 -4
  25. package/src/lib/components/contact-list-columns.js.map +1 -1
  26. package/src/lib/components/contact-properties-card.js +41 -12
  27. package/src/lib/components/contact-properties-card.js.map +1 -1
  28. package/src/lib/components/contact-roles-editor.d.ts +54 -0
  29. package/src/lib/components/contact-roles-editor.js +270 -0
  30. package/src/lib/components/contact-roles-editor.js.map +1 -0
  31. package/src/lib/components/contacts-section.js +6 -4
  32. package/src/lib/components/contacts-section.js.map +1 -1
  33. package/src/lib/components/crm-console-sections.js +1 -1
  34. package/src/lib/components/crm-console-sections.js.map +1 -1
  35. package/src/lib/components/crm-record-picker.d.ts +19 -0
  36. package/src/lib/components/crm-record-picker.js +40 -11
  37. package/src/lib/components/crm-record-picker.js.map +1 -1
  38. package/src/lib/components/crm-related-to-picker.d.ts +33 -0
  39. package/src/lib/components/crm-related-to-picker.js +135 -0
  40. package/src/lib/components/crm-related-to-picker.js.map +1 -0
  41. package/src/lib/components/crm-site-picker.d.ts +13 -1
  42. package/src/lib/components/crm-site-picker.js +15 -6
  43. package/src/lib/components/crm-site-picker.js.map +1 -1
  44. package/src/lib/components/crm-transfer-buttons.d.ts +5 -0
  45. package/src/lib/components/crm-transfer-buttons.js +9 -6
  46. package/src/lib/components/crm-transfer-buttons.js.map +1 -1
  47. package/src/lib/components/deal-contact-roles-card.js +21 -239
  48. package/src/lib/components/deal-contact-roles-card.js.map +1 -1
  49. package/src/lib/components/deal-detail-page.js +2 -1
  50. package/src/lib/components/deal-detail-page.js.map +1 -1
  51. package/src/lib/components/deal-edit-drawer.js +11 -7
  52. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  53. package/src/lib/components/deals-section.js +5 -4
  54. package/src/lib/components/deals-section.js.map +1 -1
  55. package/src/lib/components/lead-campaigns-card.js +26 -14
  56. package/src/lib/components/lead-campaigns-card.js.map +1 -1
  57. package/src/lib/components/log-activity-dialog.d.ts +27 -9
  58. package/src/lib/components/log-activity-dialog.js +96 -32
  59. package/src/lib/components/log-activity-dialog.js.map +1 -1
  60. package/src/lib/components/new-contact-drawer.d.ts +7 -3
  61. package/src/lib/components/new-contact-drawer.js +27 -15
  62. package/src/lib/components/new-contact-drawer.js.map +1 -1
  63. package/src/lib/components/picklist-values-card.js +7 -4
  64. package/src/lib/components/picklist-values-card.js.map +1 -1
  65. package/src/lib/components/pipeline-stages-dialog.js +5 -1
  66. package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
  67. package/src/lib/components/record-activity-card.d.ts +11 -2
  68. package/src/lib/components/record-activity-card.js +90 -5
  69. package/src/lib/components/record-activity-card.js.map +1 -1
  70. package/src/lib/components/settings-section.d.ts +4 -2
  71. package/src/lib/components/settings-section.js +7 -5
  72. package/src/lib/components/settings-section.js.map +1 -1
  73. package/src/lib/hooks/crm-activity-client-reads.d.ts +8 -0
  74. package/src/lib/hooks/crm-activity-client-reads.js +50 -0
  75. package/src/lib/hooks/crm-activity-client-reads.js.map +1 -0
  76. package/src/lib/hooks/use-campaign-filing-log.d.ts +2 -1
  77. package/src/lib/hooks/use-campaign-filing-log.js +10 -3
  78. package/src/lib/hooks/use-campaign-filing-log.js.map +1 -1
  79. package/src/lib/hooks/use-crm-org-mount.d.ts +8 -6
  80. package/src/lib/hooks/use-crm-org-mount.js +2 -1
  81. package/src/lib/hooks/use-crm-org-mount.js.map +1 -1
  82. package/src/lib/hooks/use-crm-scope.d.ts +17 -5
  83. package/src/lib/hooks/use-crm-scope.js +12 -5
  84. package/src/lib/hooks/use-crm-scope.js.map +1 -1
  85. package/src/lib/model/contact-holder.d.ts +12 -0
  86. package/src/lib/model/contact-holder.js +20 -1
  87. package/src/lib/model/contact-holder.js.map +1 -1
  88. package/src/lib/model/contact-record.d.ts +5 -0
  89. package/src/lib/model/contact-record.js +7 -1
  90. package/src/lib/model/contact-record.js.map +1 -1
  91. package/src/lib/model/contact-update.d.ts +6 -0
  92. package/src/lib/model/contact-update.js +1 -0
  93. package/src/lib/model/contact-update.js.map +1 -1
  94. package/src/lib/model/crm-activity-link.d.ts +117 -0
  95. package/src/lib/model/crm-activity-link.js +113 -0
  96. package/src/lib/model/crm-activity-link.js.map +1 -0
  97. package/src/lib/model/crm-api.d.ts +4 -1
  98. package/src/lib/model/crm-api.js +9 -5
  99. package/src/lib/model/crm-api.js.map +1 -1
  100. package/src/lib/model/deal-board-model.js +5 -3
  101. package/src/lib/model/deal-board-model.js.map +1 -1
  102. package/src/lib/model/deal-contact-roles.d.ts +15 -1
  103. package/src/lib/model/deal-contact-roles.js +17 -3
  104. package/src/lib/model/deal-contact-roles.js.map +1 -1
  105. package/src/lib/model/settle-stage-names.d.ts +25 -0
  106. package/src/lib/model/settle-stage-names.js +29 -0
  107. package/src/lib/model/settle-stage-names.js.map +1 -0
  108. package/src/lib/native-nav.js +1 -1
  109. package/src/lib/native-nav.js.map +1 -1
  110. package/src/lib/server/add-contact-by-hand.d.ts +84 -0
  111. package/src/lib/server/add-contact-by-hand.js +212 -0
  112. package/src/lib/server/add-contact-by-hand.js.map +1 -0
  113. package/src/lib/server/api-v1/contacts.js +61 -23
  114. package/src/lib/server/api-v1/contacts.js.map +1 -1
  115. package/src/lib/server/api-v1/crm-activities.js +13 -3
  116. package/src/lib/server/api-v1/crm-activities.js.map +1 -1
  117. package/src/lib/server/api-v1/crm-pipelines.d.ts +1 -1
  118. package/src/lib/server/api-v1/crm-pipelines.js +8 -2
  119. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
  120. package/src/lib/server/api-v1/crm-shared.d.ts +16 -9
  121. package/src/lib/server/api-v1/crm-shared.js +23 -11
  122. package/src/lib/server/api-v1/crm-shared.js.map +1 -1
  123. package/src/lib/server/api-v1/openapi.js +3 -3
  124. package/src/lib/server/api-v1/openapi.js.map +1 -1
  125. package/src/lib/server/automation-steps.js +11 -9
  126. package/src/lib/server/automation-steps.js.map +1 -1
  127. package/src/lib/server/campaign-filing-activity.js +7 -2
  128. package/src/lib/server/campaign-filing-activity.js.map +1 -1
  129. package/src/lib/server/contact-update.d.ts +4 -0
  130. package/src/lib/server/contact-update.js +71 -10
  131. package/src/lib/server/contact-update.js.map +1 -1
  132. package/src/lib/server/convert-host-lead.js +13 -2
  133. package/src/lib/server/convert-host-lead.js.map +1 -1
  134. package/src/lib/server/convert-open-lead.js +8 -1
  135. package/src/lib/server/convert-open-lead.js.map +1 -1
  136. package/src/lib/server/crm-activity-company.d.ts +25 -0
  137. package/src/lib/server/crm-activity-company.js +34 -0
  138. package/src/lib/server/crm-activity-company.js.map +1 -0
  139. package/src/lib/server/crm-inbound-email.js +21 -15
  140. package/src/lib/server/crm-inbound-email.js.map +1 -1
  141. package/src/lib/server/crm-sharing.js +9 -2
  142. package/src/lib/server/crm-sharing.js.map +1 -1
  143. package/src/lib/server/email-send.js +10 -8
  144. package/src/lib/server/email-send.js.map +1 -1
  145. package/src/lib/server/merge-contacts.js +6 -2
  146. package/src/lib/server/merge-contacts.js.map +1 -1
  147. package/src/lib/server/org-caller.d.ts +8 -0
  148. package/src/lib/server/org-caller.js +10 -0
  149. package/src/lib/server/org-caller.js.map +1 -1
  150. package/src/lib/server/record-timeline.d.ts +3 -1
  151. package/src/lib/server/record-timeline.js +11 -3
  152. package/src/lib/server/record-timeline.js.map +1 -1
  153. package/src/lib/server/task-reminders-route.d.ts +7 -5
  154. package/src/lib/server/task-reminders-route.js +82 -15
  155. package/src/lib/server/task-reminders-route.js.map +1 -1
  156. package/src/lib/server.js +152 -43
  157. package/src/lib/server.js.map +1 -1
  158. package/src/lib/transfer/contacts.js +83 -48
  159. package/src/lib/transfer/contacts.js.map +1 -1
  160. package/src/lib/transfer/fields.js +1 -2
  161. package/src/lib/transfer/fields.js.map +1 -1
@@ -43,14 +43,14 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
43
43
  * turned on by a file and never off; the full name follows the first and
44
44
  * last names once either is set. The plan holds each one back by row and
45
45
  * names it, so the person acknowledges it before anything is written.
46
- *=========================================*/ import { composeContactName, contactDisplayName, contactFacetPath, CRM_COLLECTIONS, interactionsForGroup, isContactLifecycleStage, normalizeContactBirthdate, normalizePhone, planContactDetach, readContactFacet, readMarketingBasis } from "@aglyn/aglyn/server";
46
+ *=========================================*/ import { composeContactName, crmDefaultScopeOf, ORG_CONTACT_HOLDER_ID, contactDisplayName, contactFacetPath, CRM_COLLECTIONS, interactionsForGroup, isContactLifecycleStage, normalizeContactBirthdate, normalizePhone, planContactDetach, readContactFacet, readMarketingBasis } from "@aglyn/aglyn/server";
47
47
  import { CONTACT_LIFECYCLE_STAGE_LABELS } from "@aglyn/aglyn/app-utils/crm";
48
48
  import { CONTACT_EXTRA_PHONE_FIELDS as PHONE_FIELDS } from "@aglyn/aglyn/app-utils/contacts";
49
49
  import { buildTransferPlan, planTransferUndo, rankTransferLookupSuggestions, TRANSFER_ID_FIELD, transferLookupNewName } from "@aglyn/aglyn/data-transfer";
50
50
  import { prepareContactCaptureBatch, removeContactKeepingRefusals, restampCrmListFieldsOf } from "@aglyn/tenant-data-admin";
51
51
  import { emitHostEvent } from "@aglyn/tenant-runtime";
52
52
  import { FieldValue } from "firebase-admin/firestore";
53
- import { contactPrimaryGroup } from "../model/contact-holder.js";
53
+ import { contactPrimaryGroup, ORG_HOLDER_GROUP } from "../model/contact-holder.js";
54
54
  import { captureHostContact } from "../server/capture-host-contact.js";
55
55
  import { CONTACT_NOTES_MAX } from "../server/contact-profile.js";
56
56
  import { cachedContactReader, clearReportsToOf, CONTACT_REPORTS_TO_LOOP_REFUSAL, CONTACT_REPORTS_TO_SELF_REFUSAL, contactFacetHolders, contactReportsToLoops } from "../server/contact-reports-to.js";
@@ -59,6 +59,7 @@ import { sweepDealContactRoles } from "../server/deal-contact-roles.js";
59
59
  import { CONTACT_ALIASES } from "./aliases.js";
60
60
  import { addCrmPicklistValues, addressFromValues, addressValues, countCrmExport, crmCustomDefinitions, crmMemberEmails, crmMembersTarget, crmPicklistLists, crmRecordsRoom, crmTransferEnv, customTransferValues, customWriteValues, eachWithinBudget, failureMessage, holdBackChanges, IN_LIMIT, isoOf, lookupByField, lookupById, lookupResult, pickValues, plannedValues, readCrmExportPage, requireCrmSuite, requireSite, textOf, TransferEngineError, visibleIn } from "./common.js";
61
61
  import { companyForName } from "./companies.js";
62
+ import { addContactByHand } from "../server/add-contact-by-hand.js";
62
63
  import { CONTACT_TRANSFER_DERIVED, CONTACT_TRANSFER_FIELDS, CONTACT_TRANSFER_GROUPS, CRM_MEMBERS_TARGET, CRM_TIMESTAMP_FIELDS, crmCustomTransferFields, crmLifecycleStageOf, crmLifecycleStageRank } from "./fields.js";
63
64
  /** The sentence an imported person's timeline opens with. */ export const CONTACT_TRANSFER_INTERACTION_SUMMARY = 'Imported from a file';
64
65
  const SUITE_ACT = 'Importing contacts';
@@ -230,7 +231,8 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
230
231
  if (!snapshot.exists || !contact || !visibleIn(run.env, contact['visibleTo'])) {
231
232
  throw new TransferEngineError('notFound', 404, 'That contact no longer exists.');
232
233
  }
233
- const groupId = run.group.groupId;
234
+ // With no site the row is written to the holder the person already has.
235
+ const groupId = (run.hostId ? run.group : contactPrimaryGroup(contact, run.env.org)).groupId || ORG_CONTACT_HOLDER_ID;
234
236
  const stored = readContactFacet(contact, groupId);
235
237
  const { fields, skipped } = profileFields(values, stored);
236
238
  let linkedCompanyName = '';
@@ -276,7 +278,7 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
276
278
  counts: patch.counts
277
279
  }
278
280
  ]);
279
- if (movedFrom !== null) {
281
+ if (movedFrom !== null && run.hostId) {
280
282
  await emitHostEvent(run.hostId, 'contactStageChanged', {
281
283
  contactId,
282
284
  email: textOf(contact['email']),
@@ -294,8 +296,21 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
294
296
  skipped
295
297
  };
296
298
  }
299
+ /**
300
+ * The site a contact import is filed under. None, when the org's records belong
301
+ * to every site (AGL-4122): the file then has no Source site, and the people in
302
+ * it are the organization's. An org that keeps its sites apart must name one.
303
+ */ function contactImportSite(env) {
304
+ if (!env.hostId && crmDefaultScopeOf(env.org) === 'org') {
305
+ return {
306
+ hostId: null,
307
+ group: ORG_HOLDER_GROUP
308
+ };
309
+ }
310
+ return requireSite(env, 'contacts');
311
+ }
297
312
  /** Creates one person through the capture door, then what the door does not take. */ async function createContact(run, values, batch) {
298
- var _fields_tags;
313
+ var _fields_tags, _fields_tags1;
299
314
  const email = textOf(values['email']);
300
315
  const { fields, skipped } = profileFields(values, {
301
316
  sources: {},
@@ -305,7 +320,52 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
305
320
  const custom = customWriteValues(values, run.definitions, 'contact');
306
321
  if ('error' in custom) throw new TransferEngineError('invalid', 422, custom.error);
307
322
  const stage = crmLifecycleStageOf(values['lifecycleStage']);
308
- const verdict = await captureHostContact(_extends({
323
+ const facet = _extends({}, fields.phone ? {
324
+ phone: fields.phone
325
+ } : {}, fields.jobTitle ? {
326
+ jobTitle: fields.jobTitle
327
+ } : {}, fields.salutation ? {
328
+ salutation: fields.salutation
329
+ } : {}, fields.firstName ? {
330
+ firstName: fields.firstName
331
+ } : {}, fields.lastName ? {
332
+ lastName: fields.lastName
333
+ } : {}, fields.department ? {
334
+ department: fields.department
335
+ } : {}, Object.fromEntries(PHONE_FIELDS.filter((key)=>fields[key]).map((key)=>[
336
+ key,
337
+ fields[key]
338
+ ])), fields.birthdate ? {
339
+ birthdate: fields.birthdate
340
+ } : {}, fields.assistantName ? {
341
+ assistantName: fields.assistantName
342
+ } : {}, fields.leadSource ? {
343
+ leadSource: fields.leadSource
344
+ } : {}, fields.doNotCall ? {
345
+ doNotCall: true
346
+ } : {}, fields.address ? {
347
+ address: fields.address
348
+ } : {}, fields.otherAddress ? {
349
+ otherAddress: fields.otherAddress
350
+ } : {}, (link == null ? void 0 : link.id) ? {
351
+ companyId: link.id,
352
+ companyName: link.name
353
+ } : {}, fields.ownerUid ? {
354
+ ownerUid: fields.ownerUid
355
+ } : {}, Object.keys(custom.values).length ? {
356
+ custom: custom.values
357
+ } : {});
358
+ const staged = stage ? {
359
+ lifecycleStage: stage
360
+ } : {};
361
+ /*
362
+ * A SITE AND AN ADDRESS ARE A CAPTURE. Anything less is a person no capture
363
+ * door could take, so a file can hold people with no email and be imported
364
+ * with no site (AGL-4122). A row still has to say who it is about.
365
+ */ if (!email && !fields.name && !facet.phone && !facet.firstName && !facet.lastName && !fields['mobilePhone']) {
366
+ throw new TransferEngineError('invalid', 422, 'A row needs a name, an email or a phone number.');
367
+ }
368
+ const verdict = run.hostId && email ? await captureHostContact(_extends({
309
369
  hostId: run.hostId,
310
370
  email
311
371
  }, fields.name ? {
@@ -323,48 +383,23 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
323
383
  summary: CONTACT_TRANSFER_INTERACTION_SUMMARY
324
384
  },
325
385
  tags: (_fields_tags = fields.tags) != null ? _fields_tags : [],
326
- facet: _extends({}, fields.phone ? {
327
- phone: fields.phone
328
- } : {}, fields.jobTitle ? {
329
- jobTitle: fields.jobTitle
330
- } : {}, fields.salutation ? {
331
- salutation: fields.salutation
332
- } : {}, fields.firstName ? {
333
- firstName: fields.firstName
334
- } : {}, fields.lastName ? {
335
- lastName: fields.lastName
336
- } : {}, fields.department ? {
337
- department: fields.department
338
- } : {}, Object.fromEntries(PHONE_FIELDS.filter((key)=>fields[key]).map((key)=>[
339
- key,
340
- fields[key]
341
- ])), fields.birthdate ? {
342
- birthdate: fields.birthdate
343
- } : {}, fields.assistantName ? {
344
- assistantName: fields.assistantName
345
- } : {}, fields.leadSource ? {
346
- leadSource: fields.leadSource
347
- } : {}, fields.doNotCall ? {
348
- doNotCall: true
349
- } : {}, fields.address ? {
350
- address: fields.address
351
- } : {}, fields.otherAddress ? {
352
- otherAddress: fields.otherAddress
353
- } : {}, (link == null ? void 0 : link.id) ? {
354
- companyId: link.id,
355
- companyName: link.name
356
- } : {}, fields.ownerUid ? {
357
- ownerUid: fields.ownerUid
358
- } : {}, stage ? {
359
- lifecycleStage: stage
360
- } : {}, Object.keys(custom.values).length ? {
361
- custom: custom.values
362
- } : {}),
386
+ // The file's own stage, or none: `lifecycleStage` from the column.
387
+ facet: _extends({}, facet, staged),
363
388
  // An import files nobody under a campaign; the record's picker does.
364
389
  campaignIds: []
365
390
  }, batch ? {
366
391
  batch
367
- } : {}));
392
+ } : {})) : await addContactByHand(_extends({
393
+ orgId: run.env.orgId,
394
+ org: run.env.org,
395
+ sourceHostId: run.hostId,
396
+ email: email || null
397
+ }, fields.name ? {
398
+ name: fields.name
399
+ } : {}, {
400
+ facet: _extends({}, facet, staged),
401
+ tags: (_fields_tags1 = fields.tags) != null ? _fields_tags1 : []
402
+ }));
368
403
  if ('refused' in verdict) {
369
404
  var _CAPTURE_REFUSALS_verdict_refused;
370
405
  throw new TransferEngineError('invalid', 422, (_CAPTURE_REFUSALS_verdict_refused = CAPTURE_REFUSALS[verdict.refused]) != null ? _CAPTURE_REFUSALS_verdict_refused : CAPTURE_REFUSALS['error']);
@@ -520,7 +555,7 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
520
555
  async plan (ctx, input) {
521
556
  const env = await crmTransferEnv(ctx);
522
557
  requireCrmSuite(env, SUITE_ACT);
523
- requireSite(env, 'contacts');
558
+ contactImportSite(env);
524
559
  const plan = buildTransferPlan(input);
525
560
  return holdBackChanges(plan, (row, change)=>{
526
561
  const before = row.recordId ? input.existing.get(row.recordId) : undefined;
@@ -547,7 +582,7 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
547
582
  async apply (ctx, chunk, writer) {
548
583
  const env = await crmTransferEnv(ctx);
549
584
  requireCrmSuite(env, SUITE_ACT);
550
- const { hostId, group } = requireSite(env, 'contacts');
585
+ const { hostId, group } = contactImportSite(env);
551
586
  const run = {
552
587
  env,
553
588
  hostId,
@@ -567,7 +602,7 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
567
602
  }
568
603
  }
569
604
  // The capture door's lookups for every new person at once (AGL-3423).
570
- const batch = creating.length ? await prepareContactCaptureBatch(hostId, creating) : undefined;
605
+ const batch = hostId && creating.length ? await prepareContactCaptureBatch(hostId, creating) : undefined;
571
606
  const results = [];
572
607
  const undo = [];
573
608
  await eachWithinBudget(rows, ()=>writer.timeLeftMs(), async (row)=>{
@@ -651,7 +686,7 @@ const contactsOf = (env)=>env.orgRef.collection('contacts');
651
686
  },
652
687
  async revert (ctx, snapshot, decisions) {
653
688
  const env = await crmTransferEnv(ctx);
654
- const { hostId, group } = requireSite(env, 'contacts');
689
+ const { hostId, group } = contactImportSite(env);
655
690
  const run = {
656
691
  env,
657
692
  hostId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/transfer/contacts.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 * CONTACTS ON THE TRANSFER FRAMEWORK (AGL-3527) — the server half of\n * `crm.contacts`.\n *\n * A contact is one person shared by every site of the workspace, and each\n * site's consent group keeps its own profile of them — its facet. So a\n * transfer reads and writes the facet of the site it was opened under: an\n * export at the organization level reads each person's primary holder, as\n * the org-level list does; an import always names a site, because a\n * contact is a person some site met.\n *\n * ## The writes are the CRM's own\n *\n * A new person goes through `captureHostContact`, the door every capture\n * goes through — the records band, the erasure check, the consent group's\n * facet, `contactCreated` for the automations, the company by the email's\n * domain and the owner by the assignment rules when the row names neither.\n * A matched person is changed through the contact profile route's own\n * patch (`contactPatch`): the composed name, the company link and its\n * contact counts, the list's filter keys. A stage that moves raises\n * `contactStageChanged`, as the stage route does.\n *\n * ## What a file may not do\n *\n * The locked rules: an existing contact's email is who they are; consent is\n * never taken from a file; a stage moves forward only; Do not call is\n * turned on by a file and never off; the full name follows the first and\n * last names once either is set. The plan holds each one back by row and\n * names it, so the person acknowledges it before anything is written.\n *=========================================*/\n\nimport {\n composeContactName,\n type ConsentGroup,\n contactDisplayName,\n contactFacetPath,\n type ContactFieldDefinition,\n CRM_COLLECTIONS,\n interactionsForGroup,\n isContactLifecycleStage,\n normalizeContactBirthdate,\n normalizePhone,\n planContactDetach,\n readContactFacet,\n readMarketingBasis,\n} from '@aglyn/aglyn/server'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS } from '@aglyn/aglyn/app-utils/crm'\nimport { CONTACT_EXTRA_PHONE_FIELDS as PHONE_FIELDS } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n buildTransferPlan,\n planTransferUndo,\n rankTransferLookupSuggestions,\n TRANSFER_ID_FIELD,\n transferLookupNewName,\n type PlannedTransferRow,\n type TransferRowResult,\n type TransferUndoEntry,\n type TransferUndoStep,\n} from '@aglyn/aglyn/data-transfer'\nimport type { TransferRecordsHooks } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport {\n prepareContactCaptureBatch,\n removeContactKeepingRefusals,\n restampCrmListFieldsOf,\n} from '@aglyn/tenant-data-admin'\nimport { emitHostEvent } from '@aglyn/tenant-runtime'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { contactPrimaryGroup } from '../model/contact-holder'\nimport type { ContactUpdateFields } from '../model/contact-update'\nimport { captureHostContact } from '../server/capture-host-contact'\nimport { CONTACT_NOTES_MAX } from '../server/contact-profile'\nimport {\n cachedContactReader,\n clearReportsToOf,\n CONTACT_REPORTS_TO_LOOP_REFUSAL,\n CONTACT_REPORTS_TO_SELF_REFUSAL,\n contactFacetHolders,\n contactReportsToLoops,\n} from '../server/contact-reports-to'\nimport { contactPatch, settleCompanyCounts } from '../server/contact-update'\nimport { sweepDealContactRoles } from '../server/deal-contact-roles'\nimport { CONTACT_ALIASES } from './aliases'\nimport {\n addCrmPicklistValues,\n addressFromValues,\n addressValues,\n countCrmExport,\n crmCustomDefinitions,\n crmMemberEmails,\n crmMembersTarget,\n crmPicklistLists,\n crmRecordsRoom,\n crmTransferEnv,\n type CrmTransferEnv,\n customTransferValues,\n customWriteValues,\n eachWithinBudget,\n failureMessage,\n holdBackChanges,\n IN_LIMIT,\n isoOf,\n lookupByField,\n lookupById,\n lookupResult,\n pickValues,\n plannedValues,\n readCrmExportPage,\n requireCrmSuite,\n requireSite,\n textOf,\n TransferEngineError,\n visibleIn,\n} from './common'\nimport { companyForName } from './companies'\nimport {\n CONTACT_TRANSFER_DERIVED,\n CONTACT_TRANSFER_FIELDS,\n CONTACT_TRANSFER_GROUPS,\n CRM_MEMBERS_TARGET,\n CRM_TIMESTAMP_FIELDS,\n crmCustomTransferFields,\n crmLifecycleStageOf,\n crmLifecycleStageRank,\n} from './fields'\n\n/** The sentence an imported person's timeline opens with. */\nexport const CONTACT_TRANSFER_INTERACTION_SUMMARY = 'Imported from a file'\n\nconst SUITE_ACT = 'Importing contacts'\n\nconst contactsOf = (env: CrmTransferEnv) => env.orgRef.collection('contacts')\n\n/** The holder a contact is read through: the site's group, else the person's primary holder. */\nfunction holderOf(env: CrmTransferEnv, contact: Record<string, unknown>): ConsentGroup {\n return env.group ?? contactPrimaryGroup(contact, env.org)\n}\n\n/** Why a capture refused a person, in the result's words. */\nconst CAPTURE_REFUSALS: Readonly<Record<string, string>> = {\n band: 'The contact limit is reached.',\n 'invalid-email': 'Not a valid email address.',\n erased: 'Erased from this workspace at their request.',\n error: 'The contact could not be saved.',\n}\n\n/**\n * One contact as values by field id, read through one holder's facet. A\n * lookup and an undo read links by id (`ids`), so the plan compares what is\n * stored; an export writes names a file can carry back (`names`).\n */\nexport function contactTransferValues(\n id: string,\n contact: Record<string, unknown>,\n group: ConsentGroup,\n definitions: readonly ContactFieldDefinition[],\n names?: { owner: (uid: string) => string | undefined; manager: (id: string) => string | undefined },\n): Record<string, unknown> {\n const facet = readContactFacet(contact, group.groupId)\n const text = (value: unknown) => textOf(value) || null\n const stage = isContactLifecycleStage(facet.lifecycleStage) ? CONTACT_LIFECYCLE_STAGE_LABELS[facet.lifecycleStage] : null\n const basis = group.groupId ? readMarketingBasis(contact, group).basis : 'unrecorded'\n const ownerUid = text(facet.ownerUid)\n const managerId = text(facet.reportsToContactId)\n return {\n // The name first: a record's label is the first text it holds.\n name: text(contactDisplayName(contact, group.groupId)),\n email: text(contact['email']),\n salutation: text(facet.salutation),\n firstName: text(facet.firstName),\n lastName: text(facet.lastName),\n birthdate: text(facet.birthdate),\n phone: text(facet.phone),\n mobilePhone: text(facet.mobilePhone),\n homePhone: text(facet.homePhone),\n otherPhone: text(facet.otherPhone),\n fax: text(facet.fax),\n doNotCall: facet.doNotCall === true,\n assistantName: text(facet.assistantName),\n assistantPhone: text(facet.assistantPhone),\n jobTitle: text(facet.jobTitle),\n department: text(facet.department),\n company: names ? text(facet.companyName) : text(facet.companyId),\n reportsTo: managerId ? (names ? (names.manager(managerId) ?? managerId) : managerId) : null,\n ...addressValues('mailing', facet.address),\n ...addressValues('other', facet.otherAddress),\n owner: ownerUid ? (names ? (names.owner(ownerUid) ?? ownerUid) : ownerUid) : null,\n lifecycleStage: stage,\n leadSource: text(facet.leadSource),\n tags: Array.isArray(facet.tags) ? facet.tags : [],\n notes: text(facet.notes),\n marketingConsent: basis === 'granted' ? true : basis === 'declined' ? false : null,\n sources: Object.keys(facet.sources ?? {}),\n lastInteractionAt: isoOf(interactionsForGroup(facet.interactions, group.hostIds)[0]?.atMs),\n lastEngagedAt: isoOf(facet.lastEmailEngagementAtMs),\n createdAt: isoOf(contact['createdAt']),\n updatedAt: isoOf(contact['updatedAt']),\n ...customTransferValues(definitions, facet.custom),\n [TRANSFER_ID_FIELD]: id,\n }\n}\n\n/** The contacts these ids name, as values through the site's facet. */\nasync function readContacts(\n env: CrmTransferEnv,\n ids: readonly string[],\n definitions: readonly ContactFieldDefinition[],\n): Promise<Map<string, Record<string, unknown>>> {\n const out = new Map<string, Record<string, unknown>>()\n for (let at = 0; at < ids.length; at += 500) {\n const page = ids.slice(at, at + 500).filter((id) => id && !id.includes('/'))\n if (!page.length) continue\n for (const snapshot of await env.firestore.getAll(...page.map((id) => contactsOf(env).doc(id)))) {\n const data = snapshot.data()\n if (snapshot.exists && data && visibleIn(env, data['visibleTo'])) {\n out.set(snapshot.id, contactTransferValues(snapshot.id, data, holderOf(env, data), definitions))\n }\n }\n }\n return out\n}\n\n/** A company link a row names: the company and its name, or `null` to unlink. */\ninterface CompanyLink {\n id: string | null\n name: string\n}\n\n/** Everything one apply call shares across its rows. */\ninterface ApplyRun {\n env: CrmTransferEnv\n hostId: string\n group: ConsentGroup\n actorUid: string | null\n definitions: ContactFieldDefinition[]\n companies: Map<string, Promise<{ id: string; name: string } | null>>\n admit: () => Promise<boolean>\n readContact: ReturnType<typeof cachedContactReader>\n}\n\n/** The company a row's company column names, resolved: an id the engine found, or one created by name. */\nasync function companyLinkFor(run: ApplyRun, value: unknown): Promise<CompanyLink> {\n if (value === null || value === undefined || value === '') return { id: null, name: '' }\n const create = transferLookupNewName(value)\n if (create) {\n const company = await companyForName(run.env, create, run.actorUid, run.companies, run.admit)\n if (!company) throw new TransferEngineError('invalid', 422, 'The CRM records limit has no room for the company.')\n return company\n }\n const snapshot = await run.env.orgRef.collection(CRM_COLLECTIONS.companies).doc(String(value)).get()\n if (!snapshot.exists || !visibleIn(run.env, snapshot.get('visibleTo'))) {\n throw new TransferEngineError('notFound', 404, 'The company this row names no longer exists.')\n }\n return { id: snapshot.id, name: textOf(snapshot.get('name')).slice(0, 120) }\n}\n\n/**\n * A row's values as the profile route's fields: each one normalized the way\n * that route reads it, a blank clearing it. Fields a value could not be\n * stored as are left out and named in `skipped`.\n */\nfunction profileFields(\n values: Readonly<Record<string, unknown>>,\n stored: ReturnType<typeof readContactFacet>,\n): { fields: ContactUpdateFields; skipped: string[] } {\n const fields: ContactUpdateFields = {}\n const skipped: string[] = []\n const text = (key: string, max = 120) => textOf(values[key]).slice(0, max)\n for (const key of ['jobTitle', 'department', 'assistantName', 'salutation', 'leadSource'] as const) {\n if (key in values) fields[key] = text(key)\n }\n if ('firstName' in values) fields.firstName = composeContactName(text('firstName'), '')\n if ('lastName' in values) fields.lastName = composeContactName('', text('lastName'))\n for (const key of ['phone', ...PHONE_FIELDS] as const) {\n if (!(key in values)) continue\n const typed = text(key, 40)\n const phone = typed ? normalizePhone(typed) : ''\n if (phone === null) skipped.push(key)\n else fields[key] = phone\n }\n if ('birthdate' in values) {\n const birthdate = normalizeContactBirthdate(text('birthdate', 20))\n if (birthdate === null) skipped.push('birthdate')\n else fields.birthdate = birthdate\n }\n if ('doNotCall' in values) fields.doNotCall = values['doNotCall'] === true\n if ('notes' in values) fields.notes = textOf(values['notes']).slice(0, CONTACT_NOTES_MAX)\n if ('tags' in values) fields.tags = Array.isArray(values['tags']) ? (values['tags'] as string[]).slice(0, 20) : []\n if ('owner' in values) fields.ownerUid = text('owner', 128)\n if ('reportsTo' in values) fields.reportsToContactId = text('reportsTo', 200) || null\n const mailing = addressFromValues('mailing', values, stored.address)\n if (mailing !== undefined) fields.address = mailing\n const other = addressFromValues('other', values, stored.otherAddress)\n if (other !== undefined) fields.otherAddress = other\n // The name follows the first and last names once either is set; only a\n // holder with neither keeps a name of its own.\n const composed = composeContactName(fields.firstName ?? stored.firstName, fields.lastName ?? stored.lastName)\n if ('name' in values && !composed) fields.name = text('name')\n return { fields, skipped }\n}\n\n/** The sentence a row's result carries for fields it could not store. */\nfunction skippedMessage(skipped: readonly string[]): string | undefined {\n if (!skipped.length) return undefined\n const labels = skipped.map((id) => CONTACT_TRANSFER_FIELDS.find((field) => field.id === id)?.label ?? id)\n return `Not written, as the value could not be read: ${labels.join(', ')}.`\n}\n\n/**\n * Writes one existing contact's values through the profile route's patch,\n * and the stage beside it. Answers the stage it replaced when it moved one,\n * and the fields left out.\n */\nasync function writeContact(\n run: ApplyRun,\n contactId: string,\n values: Readonly<Record<string, unknown>>,\n): Promise<{ skipped: string[] }> {\n const ref = contactsOf(run.env).doc(contactId)\n const snapshot = await ref.get()\n const contact = snapshot.data()\n if (!snapshot.exists || !contact || !visibleIn(run.env, contact['visibleTo'])) {\n throw new TransferEngineError('notFound', 404, 'That contact no longer exists.')\n }\n const groupId = run.group.groupId\n const stored = readContactFacet(contact, groupId)\n const { fields, skipped } = profileFields(values, stored)\n let linkedCompanyName = ''\n if ('company' in values) {\n const link = await companyLinkFor(run, values['company'])\n fields.companyId = link.id\n linkedCompanyName = link.name\n }\n if (fields.reportsToContactId) {\n const managerId = fields.reportsToContactId\n if (managerId === contactId) throw new TransferEngineError('invalid', 422, CONTACT_REPORTS_TO_SELF_REFUSAL)\n if (await contactReportsToLoops(run.readContact, contactId, managerId, groupId)) {\n throw new TransferEngineError('invalid', 422, CONTACT_REPORTS_TO_LOOP_REFUSAL)\n }\n }\n const custom = customWriteValues(values, run.definitions, 'contact')\n if ('error' in custom) throw new TransferEngineError('invalid', 422, custom.error)\n const patch = contactPatch(contact, groupId, fields, custom.values, linkedCompanyName)\n if ('refused' in patch) throw new TransferEngineError('invalid', 422, patch.refused)\n const update: Record<string, unknown> = { ...(patch.update ?? {}) }\n\n let movedFrom: string | null = null\n if ('lifecycleStage' in values) {\n const stage = crmLifecycleStageOf(values['lifecycleStage'])\n const previous = isContactLifecycleStage(stored.lifecycleStage) ? stored.lifecycleStage : ''\n if ((stage ?? '') !== previous) {\n update[contactFacetPath(groupId, 'lifecycleStage')] = stage ?? FieldValue.delete()\n if (stage) movedFrom = previous\n }\n }\n if (!Object.keys(update).length) return { skipped }\n update['updatedAt'] = FieldValue.serverTimestamp()\n await ref.update(update)\n await restampCrmListFieldsOf([ref], 'contacts')\n await settleCompanyCounts(run.env.orgRef.collection(CRM_COLLECTIONS.companies), [\n { contactId, ref, update, counts: patch.counts },\n ])\n if (movedFrom !== null) {\n await emitHostEvent(\n run.hostId,\n 'contactStageChanged',\n {\n contactId,\n email: textOf(contact['email']),\n lifecycleStage: String(crmLifecycleStageOf(values['lifecycleStage'])),\n previousStage: movedFrom,\n },\n run.actorUid ? { actor: { kind: 'member', uid: run.actorUid, email: null } } : {},\n ).catch((error: unknown) => console.error('[crm] transfer stage event failed', contactId, error))\n }\n return { skipped }\n}\n\n/** Creates one person through the capture door, then what the door does not take. */\nasync function createContact(\n run: ApplyRun,\n values: Readonly<Record<string, unknown>>,\n batch: Awaited<ReturnType<typeof prepareContactCaptureBatch>> | undefined,\n): Promise<{ contactId: string; created: boolean; skipped: string[] }> {\n const email = textOf(values['email'])\n const { fields, skipped } = profileFields(values, { sources: {}, interactions: [] })\n const link = 'company' in values ? await companyLinkFor(run, values['company']) : null\n const custom = customWriteValues(values, run.definitions, 'contact')\n if ('error' in custom) throw new TransferEngineError('invalid', 422, custom.error)\n const stage = crmLifecycleStageOf(values['lifecycleStage'])\n const verdict = await captureHostContact({\n hostId: run.hostId,\n email,\n ...(fields.name ? { name: fields.name } : {}),\n source: 'import',\n ...(run.actorUid ? { actor: { kind: 'member' as const, uid: run.actorUid, email: null } } : {}),\n interaction: { summary: CONTACT_TRANSFER_INTERACTION_SUMMARY },\n tags: fields.tags ?? [],\n facet: {\n ...(fields.phone ? { phone: fields.phone } : {}),\n ...(fields.jobTitle ? { jobTitle: fields.jobTitle } : {}),\n ...(fields.salutation ? { salutation: fields.salutation } : {}),\n ...(fields.firstName ? { firstName: fields.firstName } : {}),\n ...(fields.lastName ? { lastName: fields.lastName } : {}),\n ...(fields.department ? { department: fields.department } : {}),\n ...Object.fromEntries(PHONE_FIELDS.filter((key) => fields[key]).map((key) => [key, fields[key]])),\n ...(fields.birthdate ? { birthdate: fields.birthdate } : {}),\n ...(fields.assistantName ? { assistantName: fields.assistantName } : {}),\n ...(fields.leadSource ? { leadSource: fields.leadSource } : {}),\n ...(fields.doNotCall ? { doNotCall: true } : {}),\n ...(fields.address ? { address: fields.address } : {}),\n ...(fields.otherAddress ? { otherAddress: fields.otherAddress } : {}),\n // The link AND the name: the merge fields read the facet's name.\n ...(link?.id ? { companyId: link.id, companyName: link.name } : {}),\n ...(fields.ownerUid ? { ownerUid: fields.ownerUid } : {}),\n ...(stage ? { lifecycleStage: stage } : {}),\n ...(Object.keys(custom.values).length ? { custom: custom.values } : {}),\n },\n // An import files nobody under a campaign; the record's picker does.\n campaignIds: [],\n ...(batch ? { batch } : {}),\n })\n if ('refused' in verdict) {\n throw new TransferEngineError('invalid', 422, CAPTURE_REFUSALS[verdict.refused] ?? CAPTURE_REFUSALS['error'] as string)\n }\n // What the capture door does not write: who they report to, and the notes.\n const extra: Record<string, unknown> = {}\n if (fields.reportsToContactId) extra['reportsTo'] = fields.reportsToContactId\n if (fields.notes) extra['notes'] = fields.notes\n if (Object.keys(extra).length) await writeContact(run, verdict.contactId, extra)\n return { contactId: verdict.contactId, created: verdict.created, skipped }\n}\n\n/** The `crm.contacts` hooks. */\nexport function contactsTransferResource(): TransferRecordsHooks {\n return {\n async fields(ctx) {\n const env = await crmTransferEnv(ctx)\n return {\n standard: CONTACT_TRANSFER_FIELDS,\n derived: CONTACT_TRANSFER_DERIVED,\n system: CRM_TIMESTAMP_FIELDS,\n custom: crmCustomTransferFields(await crmCustomDefinitions(env, 'contact'), 'contact'),\n groups: CONTACT_TRANSFER_GROUPS,\n }\n },\n matchKeys: [\n { fieldId: TRANSFER_ID_FIELD, normalizer: 'aglynId' },\n { fieldId: 'email', normalizer: 'email' },\n ],\n aliases: CONTACT_ALIASES,\n lookupTargets: { [CRM_MEMBERS_TARGET]: crmMembersTarget() },\n\n // The people a workspace holds are its own on every plan (AGL-2839):\n // exporting them asks nothing of the plan.\n async count(ctx, options) {\n const env = await crmTransferEnv(ctx)\n return countCrmExport(env, contactsOf(env), options, (data) => visibleIn(env, data['visibleTo'], options.scopeTokens))\n },\n\n async readPage(ctx, cursor, fieldIds, options) {\n const env = await crmTransferEnv(ctx)\n const definitions = await crmCustomDefinitions(env, 'contact')\n const emails = fieldIds.includes('owner') ? await crmMemberEmails(env.orgId) : new Map<string, string>()\n return readCrmExportPage(\n env,\n contactsOf(env),\n cursor,\n options,\n (data) => visibleIn(env, data['visibleTo'], options?.scopeTokens),\n async (docs) => {\n const managers = new Map<string, string>()\n if (fieldIds.includes('reportsTo')) {\n const ids = [\n ...new Set(\n docs\n .map((doc) => textOf(readContactFacet(doc.data, holderOf(env, doc.data).groupId).reportsToContactId))\n .filter((id) => id && !id.includes('/')),\n ),\n ]\n for (let at = 0; at < ids.length; at += 500) {\n const page = ids.slice(at, at + 500)\n for (const snapshot of await env.firestore.getAll(...page.map((id) => contactsOf(env).doc(id)))) {\n if (snapshot.exists) managers.set(snapshot.id, textOf(snapshot.get('email')))\n }\n }\n }\n return docs.map((doc) =>\n pickValues(\n contactTransferValues(doc.id, doc.data, holderOf(env, doc.data), definitions, {\n owner: (uid) => emails.get(uid),\n manager: (id) => managers.get(id),\n }),\n fieldIds,\n ),\n )\n },\n )\n },\n\n async lookup(ctx, requests) {\n const env = await crmTransferEnv(ctx)\n const definitions = await crmCustomDefinitions(env, 'contact')\n const visible = (data: Record<string, unknown>) => visibleIn(env, data['visibleTo'])\n const found = { lookup: new Map<string, string[]>(), docs: new Map<string, Record<string, unknown>>() }\n for (const request of requests) {\n if (request.fieldId === TRANSFER_ID_FIELD) await lookupById(env, contactsOf(env), request, visible, found)\n else if (request.fieldId === 'email') {\n await lookupByField(contactsOf(env), request, 'email', (value) => value.trim().toLowerCase(), visible, found)\n }\n }\n return lookupResult(found, (id, data) => contactTransferValues(id, data, holderOf(env, data), definitions))\n },\n\n async suggest(ctx, request) {\n const env = await crmTransferEnv(ctx)\n const answer: Record<string, ReturnType<typeof rankTransferLookupSuggestions>> = {}\n for (const value of request.values) {\n // People sharing the value's first word: the token the list's own\n // search reads, so a mistyped domain still finds the person.\n const word = value.trim().toLowerCase().split(/[\\s@.+_-]+/)[0]?.slice(0, 12) ?? ''\n if (word.length < 2) {\n answer[value] = []\n continue\n }\n const snapshot = await contactsOf(env).where('searchTokens', 'array-contains', word).limit(IN_LIMIT).get()\n const candidates = snapshot.docs\n .filter((doc) => visibleIn(env, doc.get('visibleTo')))\n .flatMap((doc) => {\n const email = textOf(doc.get('email'))\n return email ? [{ recordId: doc.id, label: email }] : []\n })\n answer[value] = rankTransferLookupSuggestions(value, candidates)\n }\n return answer\n },\n\n async picklists(ctx, ids) {\n const env = await crmTransferEnv(ctx)\n return crmPicklistLists(env, ids, await crmCustomDefinitions(env, 'contact'))\n },\n\n async addPicklistValues(ctx, picklistId, values) {\n await addCrmPicklistValues(await crmTransferEnv(ctx), picklistId, values)\n },\n\n lockedRules: () => [\n {\n fieldId: 'email',\n reason: 'An existing contact’s email is who they are; change it on the contact’s page.',\n forced: { mode: 'keepExisting' },\n },\n {\n fieldId: 'marketingConsent',\n reason:\n 'Consent is never taken from a file: a person gives it on a form, or a team member records how it was given on the contact’s page.',\n refuseValues: true,\n },\n {\n fieldId: 'lifecycleStage',\n reason: 'A stage moves forward only: a later stage in the file advances the contact, an earlier one is held back.',\n forced: { mode: 'overwrite' },\n },\n {\n fieldId: 'doNotCall',\n reason: 'A file can say a person asked not to be called, never that they took it back.',\n forced: { mode: 'overwrite' },\n },\n {\n fieldId: 'name',\n reason: 'The full name is built from the first and last names once either is set.',\n },\n ],\n\n async plan(ctx, input) {\n const env = await crmTransferEnv(ctx)\n requireCrmSuite(env, SUITE_ACT)\n requireSite(env, 'contacts')\n const plan = buildTransferPlan(input)\n return holdBackChanges(plan, (row, change) => {\n const before = row.recordId ? input.existing.get(row.recordId) : undefined\n if (change.fieldId === 'lifecycleStage' && before) {\n const from = crmLifecycleStageRank(before['lifecycleStage'])\n const to = crmLifecycleStageRank(change.after)\n if (from >= 0 && (to < from || to < 0)) return 'A stage moves forward only.'\n }\n if (change.fieldId === 'doNotCall' && before?.['doNotCall'] === true && change.after !== true) {\n return 'Do not call is turned off on the contact’s page, never by a file.'\n }\n if (change.fieldId === 'name') {\n const values = Object.fromEntries(row.diff.map((entry) => [entry.fieldId, entry.after]))\n const first = 'firstName' in values ? values['firstName'] : before?.['firstName']\n const last = 'lastName' in values ? values['lastName'] : before?.['lastName']\n if (textOf(first) || textOf(last)) return 'Built from the first and last names.'\n }\n return null\n })\n },\n\n async apply(ctx, chunk, writer) {\n const env = await crmTransferEnv(ctx)\n requireCrmSuite(env, SUITE_ACT)\n const { hostId, group } = requireSite(env, 'contacts')\n const run: ApplyRun = {\n env,\n hostId,\n group,\n actorUid: ctx.actorUid,\n definitions: await crmCustomDefinitions(env, 'contact'),\n companies: new Map(),\n admit: crmRecordsRoom(env),\n readContact: cachedContactReader(contactsOf(env)),\n }\n const rows = chunk.rows as PlannedTransferRow[]\n const creating: string[] = []\n for (const row of rows) {\n if (row.verdict === 'create' && !(await writer.alreadyApplied(row.index))) {\n const email = textOf(plannedValues(row)['email'])\n if (email) creating.push(email)\n }\n }\n // The capture door's lookups for every new person at once (AGL-3423).\n const batch = creating.length ? await prepareContactCaptureBatch(hostId, creating) : undefined\n const results: TransferRowResult[] = []\n const undo: TransferUndoEntry[] = []\n\n await eachWithinBudget(rows, () => writer.timeLeftMs(), async (row) => {\n const earlier = await writer.alreadyApplied(row.index)\n if (earlier) {\n results.push(earlier)\n return\n }\n const changed = row.diff.map((change) => change.fieldId)\n let result: TransferRowResult\n let entry: TransferUndoEntry | undefined\n try {\n const values = plannedValues(row)\n if (row.verdict === 'update' && row.recordId) {\n const before = (await readContacts(env, [row.recordId], run.definitions)).get(row.recordId)\n if (!before) throw new TransferEngineError('notFound', 404, 'That contact no longer exists.')\n const { skipped } = await writeContact(run, row.recordId, values)\n const after = (await readContacts(env, [row.recordId], run.definitions)).get(row.recordId) ?? {}\n const message = skippedMessage(skipped)\n result = { row: row.index, outcome: 'updated', recordId: row.recordId, ...(message ? { message } : {}) }\n entry = {\n row: row.index,\n recordId: row.recordId,\n action: 'updated',\n previous: pickValues(before, changed),\n written: pickValues(after, changed),\n }\n } else {\n const made = await createContact(run, values, batch)\n const after = (await readContacts(env, [made.contactId], run.definitions)).get(made.contactId) ?? {}\n const message = made.created\n ? skippedMessage(made.skipped)\n : 'Merged into the contact already holding this email.'\n result = {\n row: row.index,\n outcome: made.created ? 'created' : 'updated',\n recordId: made.contactId,\n ...(message ? { message } : {}),\n }\n // A merge into a person somebody else created meanwhile has no\n // \"before\" to put back, so undo leaves it alone.\n entry = made.created\n ? { row: row.index, recordId: made.contactId, action: 'created', written: pickValues(after, changed) }\n : undefined\n }\n } catch (error) {\n result = {\n row: row.index,\n outcome: 'failed',\n ...(row.recordId ? { recordId: row.recordId } : {}),\n message: failureMessage(error),\n }\n entry = undefined\n }\n await writer.markApplied(result, entry)\n results.push(result)\n if (entry) undo.push(entry)\n })\n return { results, undo }\n },\n\n async revert(ctx, snapshot, decisions) {\n const env = await crmTransferEnv(ctx)\n const { hostId, group } = requireSite(env, 'contacts')\n const run: ApplyRun = {\n env,\n hostId,\n group,\n actorUid: ctx.actorUid,\n definitions: await crmCustomDefinitions(env, 'contact'),\n companies: new Map(),\n admit: crmRecordsRoom(env),\n readContact: cachedContactReader(contactsOf(env)),\n }\n const current = await readContacts(env, snapshot.entries.map((entry) => entry.recordId), run.definitions)\n const done: TransferUndoStep[] = []\n const conflicts: TransferUndoStep[] = []\n for (const entry of snapshot.entries) {\n const step = planTransferUndo(entry, current.get(entry.recordId) ?? null)\n if (step.action === 'conflict' && decisions?.[entry.recordId] !== 'revert') {\n if (decisions?.[entry.recordId] === 'keep') done.push({ action: 'nothing', recordId: entry.recordId, why: 'alreadyReverted' })\n else conflicts.push(step)\n continue\n }\n if (step.action === 'nothing') {\n done.push(step)\n continue\n }\n if (entry.action === 'created') {\n // The site lets the person go, as \"Remove from this site\" does: a\n // person only this import's site held is deleted, keeping every\n // refusal they made; one another site holds too stays theirs.\n const contactRef = contactsOf(env).doc(entry.recordId)\n let holders: string[] = []\n const removal = await removeContactKeepingRefusals({\n contactRef,\n decide: (contact) => {\n holders = contactFacetHolders(contact)\n return planContactDetach(contact, group)\n },\n nowMs: Date.now(),\n })\n if (removal.outcome === 'deleted') {\n await clearReportsToOf(env.firestore, contactsOf(env), holders, entry.recordId)\n await sweepDealContactRoles(\n env.firestore,\n env.orgRef.collection(CRM_COLLECTIONS.deals),\n entry.recordId,\n null,\n '[crm] transfer undo',\n )\n } else if (removal.outcome === 'detached') {\n await clearReportsToOf(env.firestore, contactsOf(env), [group.groupId], entry.recordId)\n await restampCrmListFieldsOf([contactRef], 'contacts').catch(() => undefined)\n }\n done.push({ action: 'delete', recordId: entry.recordId })\n continue\n }\n const values = step.action === 'restore' || step.action === 'conflict' ? step.values : {}\n await writeContact(run, entry.recordId, values)\n done.push(step)\n }\n return { done, conflicts }\n },\n }\n}\n"],"names":["composeContactName","contactDisplayName","contactFacetPath","CRM_COLLECTIONS","interactionsForGroup","isContactLifecycleStage","normalizeContactBirthdate","normalizePhone","planContactDetach","readContactFacet","readMarketingBasis","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_EXTRA_PHONE_FIELDS","PHONE_FIELDS","buildTransferPlan","planTransferUndo","rankTransferLookupSuggestions","TRANSFER_ID_FIELD","transferLookupNewName","prepareContactCaptureBatch","removeContactKeepingRefusals","restampCrmListFieldsOf","emitHostEvent","FieldValue","contactPrimaryGroup","captureHostContact","CONTACT_NOTES_MAX","cachedContactReader","clearReportsToOf","CONTACT_REPORTS_TO_LOOP_REFUSAL","CONTACT_REPORTS_TO_SELF_REFUSAL","contactFacetHolders","contactReportsToLoops","contactPatch","settleCompanyCounts","sweepDealContactRoles","CONTACT_ALIASES","addCrmPicklistValues","addressFromValues","addressValues","countCrmExport","crmCustomDefinitions","crmMemberEmails","crmMembersTarget","crmPicklistLists","crmRecordsRoom","crmTransferEnv","customTransferValues","customWriteValues","eachWithinBudget","failureMessage","holdBackChanges","IN_LIMIT","isoOf","lookupByField","lookupById","lookupResult","pickValues","plannedValues","readCrmExportPage","requireCrmSuite","requireSite","textOf","TransferEngineError","visibleIn","companyForName","CONTACT_TRANSFER_DERIVED","CONTACT_TRANSFER_FIELDS","CONTACT_TRANSFER_GROUPS","CRM_MEMBERS_TARGET","CRM_TIMESTAMP_FIELDS","crmCustomTransferFields","crmLifecycleStageOf","crmLifecycleStageRank","CONTACT_TRANSFER_INTERACTION_SUMMARY","SUITE_ACT","contactsOf","env","orgRef","collection","holderOf","contact","group","org","CAPTURE_REFUSALS","band","erased","error","contactTransferValues","id","definitions","names","facet","groupId","text","value","stage","lifecycleStage","basis","ownerUid","managerId","reportsToContactId","name","email","salutation","firstName","lastName","birthdate","phone","mobilePhone","homePhone","otherPhone","fax","doNotCall","assistantName","assistantPhone","jobTitle","department","company","companyName","companyId","reportsTo","manager","address","otherAddress","owner","leadSource","tags","Array","isArray","notes","marketingConsent","sources","Object","keys","lastInteractionAt","interactions","hostIds","atMs","lastEngagedAt","lastEmailEngagementAtMs","createdAt","updatedAt","custom","readContacts","ids","out","Map","at","length","page","slice","filter","includes","snapshot","firestore","getAll","map","doc","data","exists","set","companyLinkFor","run","undefined","create","actorUid","companies","admit","String","get","profileFields","values","stored","fields","skipped","key","max","typed","push","mailing","other","composed","skippedMessage","labels","find","field","label","join","writeContact","contactId","patch","ref","linkedCompanyName","link","readContact","refused","update","movedFrom","previous","delete","serverTimestamp","counts","hostId","previousStage","actor","kind","uid","catch","console","createContact","batch","verdict","source","interaction","summary","fromEntries","campaignIds","extra","created","contactsTransferResource","ctx","standard","derived","system","groups","matchKeys","fieldId","normalizer","aliases","lookupTargets","count","options","scopeTokens","readPage","cursor","fieldIds","emails","orgId","docs","managers","Set","lookup","requests","visible","found","request","trim","toLowerCase","suggest","answer","word","split","where","limit","candidates","flatMap","recordId","picklists","addPicklistValues","picklistId","lockedRules","reason","forced","mode","refuseValues","plan","input","row","change","before","existing","from","to","after","diff","entry","first","last","apply","chunk","writer","rows","creating","alreadyApplied","index","results","undo","timeLeftMs","earlier","changed","result","message","outcome","action","written","made","markApplied","revert","decisions","current","entries","done","conflicts","step","why","contactRef","holders","removal","decide","nowMs","Date","now","deals"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/transfer/contacts.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 * CONTACTS ON THE TRANSFER FRAMEWORK (AGL-3527) — the server half of\n * `crm.contacts`.\n *\n * A contact is one person shared by every site of the workspace, and each\n * site's consent group keeps its own profile of them — its facet. So a\n * transfer reads and writes the facet of the site it was opened under: an\n * export at the organization level reads each person's primary holder, as\n * the org-level list does; an import always names a site, because a\n * contact is a person some site met.\n *\n * ## The writes are the CRM's own\n *\n * A new person goes through `captureHostContact`, the door every capture\n * goes through — the records band, the erasure check, the consent group's\n * facet, `contactCreated` for the automations, the company by the email's\n * domain and the owner by the assignment rules when the row names neither.\n * A matched person is changed through the contact profile route's own\n * patch (`contactPatch`): the composed name, the company link and its\n * contact counts, the list's filter keys. A stage that moves raises\n * `contactStageChanged`, as the stage route does.\n *\n * ## What a file may not do\n *\n * The locked rules: an existing contact's email is who they are; consent is\n * never taken from a file; a stage moves forward only; Do not call is\n * turned on by a file and never off; the full name follows the first and\n * last names once either is set. The plan holds each one back by row and\n * names it, so the person acknowledges it before anything is written.\n *=========================================*/\n\nimport {\n composeContactName,\n type ConsentGroup,\n crmDefaultScopeOf,\n ORG_CONTACT_HOLDER_ID,\n contactDisplayName,\n contactFacetPath,\n type ContactFieldDefinition,\n CRM_COLLECTIONS,\n interactionsForGroup,\n isContactLifecycleStage,\n normalizeContactBirthdate,\n normalizePhone,\n planContactDetach,\n readContactFacet,\n readMarketingBasis,\n} from '@aglyn/aglyn/server'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS } from '@aglyn/aglyn/app-utils/crm'\nimport { CONTACT_EXTRA_PHONE_FIELDS as PHONE_FIELDS } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n buildTransferPlan,\n planTransferUndo,\n rankTransferLookupSuggestions,\n TRANSFER_ID_FIELD,\n transferLookupNewName,\n type PlannedTransferRow,\n type TransferRowResult,\n type TransferUndoEntry,\n type TransferUndoStep,\n} from '@aglyn/aglyn/data-transfer'\nimport type { TransferRecordsHooks } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport {\n prepareContactCaptureBatch,\n removeContactKeepingRefusals,\n restampCrmListFieldsOf,\n} from '@aglyn/tenant-data-admin'\nimport { emitHostEvent } from '@aglyn/tenant-runtime'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { contactPrimaryGroup, ORG_HOLDER_GROUP } from '../model/contact-holder'\nimport type { ContactUpdateFields } from '../model/contact-update'\nimport { captureHostContact } from '../server/capture-host-contact'\nimport { CONTACT_NOTES_MAX } from '../server/contact-profile'\nimport {\n cachedContactReader,\n clearReportsToOf,\n CONTACT_REPORTS_TO_LOOP_REFUSAL,\n CONTACT_REPORTS_TO_SELF_REFUSAL,\n contactFacetHolders,\n contactReportsToLoops,\n} from '../server/contact-reports-to'\nimport { contactPatch, settleCompanyCounts } from '../server/contact-update'\nimport { sweepDealContactRoles } from '../server/deal-contact-roles'\nimport { CONTACT_ALIASES } from './aliases'\nimport {\n addCrmPicklistValues,\n addressFromValues,\n addressValues,\n countCrmExport,\n crmCustomDefinitions,\n crmMemberEmails,\n crmMembersTarget,\n crmPicklistLists,\n crmRecordsRoom,\n crmTransferEnv,\n type CrmTransferEnv,\n customTransferValues,\n customWriteValues,\n eachWithinBudget,\n failureMessage,\n holdBackChanges,\n IN_LIMIT,\n isoOf,\n lookupByField,\n lookupById,\n lookupResult,\n pickValues,\n plannedValues,\n readCrmExportPage,\n requireCrmSuite,\n requireSite,\n textOf,\n TransferEngineError,\n visibleIn,\n} from './common'\nimport { companyForName } from './companies'\nimport { addContactByHand } from '../server/add-contact-by-hand'\nimport {\n CONTACT_TRANSFER_DERIVED,\n CONTACT_TRANSFER_FIELDS,\n CONTACT_TRANSFER_GROUPS,\n CRM_MEMBERS_TARGET,\n CRM_TIMESTAMP_FIELDS,\n crmCustomTransferFields,\n crmLifecycleStageOf,\n crmLifecycleStageRank,\n} from './fields'\n\n/** The sentence an imported person's timeline opens with. */\nexport const CONTACT_TRANSFER_INTERACTION_SUMMARY = 'Imported from a file'\n\nconst SUITE_ACT = 'Importing contacts'\n\nconst contactsOf = (env: CrmTransferEnv) => env.orgRef.collection('contacts')\n\n/** The holder a contact is read through: the site's group, else the person's primary holder. */\nfunction holderOf(env: CrmTransferEnv, contact: Record<string, unknown>): ConsentGroup {\n return env.group ?? contactPrimaryGroup(contact, env.org)\n}\n\n/** Why a capture refused a person, in the result's words. */\nconst CAPTURE_REFUSALS: Readonly<Record<string, string>> = {\n band: 'The contact limit is reached.',\n 'invalid-email': 'Not a valid email address.',\n erased: 'Erased from this workspace at their request.',\n error: 'The contact could not be saved.',\n}\n\n/**\n * One contact as values by field id, read through one holder's facet. A\n * lookup and an undo read links by id (`ids`), so the plan compares what is\n * stored; an export writes names a file can carry back (`names`).\n */\nexport function contactTransferValues(\n id: string,\n contact: Record<string, unknown>,\n group: ConsentGroup,\n definitions: readonly ContactFieldDefinition[],\n names?: { owner: (uid: string) => string | undefined; manager: (id: string) => string | undefined },\n): Record<string, unknown> {\n const facet = readContactFacet(contact, group.groupId)\n const text = (value: unknown) => textOf(value) || null\n const stage = isContactLifecycleStage(facet.lifecycleStage) ? CONTACT_LIFECYCLE_STAGE_LABELS[facet.lifecycleStage] : null\n const basis = group.groupId ? readMarketingBasis(contact, group).basis : 'unrecorded'\n const ownerUid = text(facet.ownerUid)\n const managerId = text(facet.reportsToContactId)\n return {\n // The name first: a record's label is the first text it holds.\n name: text(contactDisplayName(contact, group.groupId)),\n email: text(contact['email']),\n salutation: text(facet.salutation),\n firstName: text(facet.firstName),\n lastName: text(facet.lastName),\n birthdate: text(facet.birthdate),\n phone: text(facet.phone),\n mobilePhone: text(facet.mobilePhone),\n homePhone: text(facet.homePhone),\n otherPhone: text(facet.otherPhone),\n fax: text(facet.fax),\n doNotCall: facet.doNotCall === true,\n assistantName: text(facet.assistantName),\n assistantPhone: text(facet.assistantPhone),\n jobTitle: text(facet.jobTitle),\n department: text(facet.department),\n company: names ? text(facet.companyName) : text(facet.companyId),\n reportsTo: managerId ? (names ? (names.manager(managerId) ?? managerId) : managerId) : null,\n ...addressValues('mailing', facet.address),\n ...addressValues('other', facet.otherAddress),\n owner: ownerUid ? (names ? (names.owner(ownerUid) ?? ownerUid) : ownerUid) : null,\n lifecycleStage: stage,\n leadSource: text(facet.leadSource),\n tags: Array.isArray(facet.tags) ? facet.tags : [],\n notes: text(facet.notes),\n marketingConsent: basis === 'granted' ? true : basis === 'declined' ? false : null,\n sources: Object.keys(facet.sources ?? {}),\n lastInteractionAt: isoOf(interactionsForGroup(facet.interactions, group.hostIds)[0]?.atMs),\n lastEngagedAt: isoOf(facet.lastEmailEngagementAtMs),\n createdAt: isoOf(contact['createdAt']),\n updatedAt: isoOf(contact['updatedAt']),\n ...customTransferValues(definitions, facet.custom),\n [TRANSFER_ID_FIELD]: id,\n }\n}\n\n/** The contacts these ids name, as values through the site's facet. */\nasync function readContacts(\n env: CrmTransferEnv,\n ids: readonly string[],\n definitions: readonly ContactFieldDefinition[],\n): Promise<Map<string, Record<string, unknown>>> {\n const out = new Map<string, Record<string, unknown>>()\n for (let at = 0; at < ids.length; at += 500) {\n const page = ids.slice(at, at + 500).filter((id) => id && !id.includes('/'))\n if (!page.length) continue\n for (const snapshot of await env.firestore.getAll(...page.map((id) => contactsOf(env).doc(id)))) {\n const data = snapshot.data()\n if (snapshot.exists && data && visibleIn(env, data['visibleTo'])) {\n out.set(snapshot.id, contactTransferValues(snapshot.id, data, holderOf(env, data), definitions))\n }\n }\n }\n return out\n}\n\n/** A company link a row names: the company and its name, or `null` to unlink. */\ninterface CompanyLink {\n id: string | null\n name: string\n}\n\n/** Everything one apply call shares across its rows. */\ninterface ApplyRun {\n env: CrmTransferEnv\n /** The Source site, or `null` for a file with none (AGL-4122). */\n hostId: string | null\n group: ConsentGroup\n actorUid: string | null\n definitions: ContactFieldDefinition[]\n companies: Map<string, Promise<{ id: string; name: string } | null>>\n admit: () => Promise<boolean>\n readContact: ReturnType<typeof cachedContactReader>\n}\n\n/** The company a row's company column names, resolved: an id the engine found, or one created by name. */\nasync function companyLinkFor(run: ApplyRun, value: unknown): Promise<CompanyLink> {\n if (value === null || value === undefined || value === '') return { id: null, name: '' }\n const create = transferLookupNewName(value)\n if (create) {\n const company = await companyForName(run.env, create, run.actorUid, run.companies, run.admit)\n if (!company) throw new TransferEngineError('invalid', 422, 'The CRM records limit has no room for the company.')\n return company\n }\n const snapshot = await run.env.orgRef.collection(CRM_COLLECTIONS.companies).doc(String(value)).get()\n if (!snapshot.exists || !visibleIn(run.env, snapshot.get('visibleTo'))) {\n throw new TransferEngineError('notFound', 404, 'The company this row names no longer exists.')\n }\n return { id: snapshot.id, name: textOf(snapshot.get('name')).slice(0, 120) }\n}\n\n/**\n * A row's values as the profile route's fields: each one normalized the way\n * that route reads it, a blank clearing it. Fields a value could not be\n * stored as are left out and named in `skipped`.\n */\nfunction profileFields(\n values: Readonly<Record<string, unknown>>,\n stored: ReturnType<typeof readContactFacet>,\n): { fields: ContactUpdateFields; skipped: string[] } {\n const fields: ContactUpdateFields = {}\n const skipped: string[] = []\n const text = (key: string, max = 120) => textOf(values[key]).slice(0, max)\n for (const key of ['jobTitle', 'department', 'assistantName', 'salutation', 'leadSource'] as const) {\n if (key in values) fields[key] = text(key)\n }\n if ('firstName' in values) fields.firstName = composeContactName(text('firstName'), '')\n if ('lastName' in values) fields.lastName = composeContactName('', text('lastName'))\n for (const key of ['phone', ...PHONE_FIELDS] as const) {\n if (!(key in values)) continue\n const typed = text(key, 40)\n const phone = typed ? normalizePhone(typed) : ''\n if (phone === null) skipped.push(key)\n else fields[key] = phone\n }\n if ('birthdate' in values) {\n const birthdate = normalizeContactBirthdate(text('birthdate', 20))\n if (birthdate === null) skipped.push('birthdate')\n else fields.birthdate = birthdate\n }\n if ('doNotCall' in values) fields.doNotCall = values['doNotCall'] === true\n if ('notes' in values) fields.notes = textOf(values['notes']).slice(0, CONTACT_NOTES_MAX)\n if ('tags' in values) fields.tags = Array.isArray(values['tags']) ? (values['tags'] as string[]).slice(0, 20) : []\n if ('owner' in values) fields.ownerUid = text('owner', 128)\n if ('reportsTo' in values) fields.reportsToContactId = text('reportsTo', 200) || null\n const mailing = addressFromValues('mailing', values, stored.address)\n if (mailing !== undefined) fields.address = mailing\n const other = addressFromValues('other', values, stored.otherAddress)\n if (other !== undefined) fields.otherAddress = other\n // The name follows the first and last names once either is set; only a\n // holder with neither keeps a name of its own.\n const composed = composeContactName(fields.firstName ?? stored.firstName, fields.lastName ?? stored.lastName)\n if ('name' in values && !composed) fields.name = text('name')\n return { fields, skipped }\n}\n\n/** The sentence a row's result carries for fields it could not store. */\nfunction skippedMessage(skipped: readonly string[]): string | undefined {\n if (!skipped.length) return undefined\n const labels = skipped.map((id) => CONTACT_TRANSFER_FIELDS.find((field) => field.id === id)?.label ?? id)\n return `Not written, as the value could not be read: ${labels.join(', ')}.`\n}\n\n/**\n * Writes one existing contact's values through the profile route's patch,\n * and the stage beside it. Answers the stage it replaced when it moved one,\n * and the fields left out.\n */\nasync function writeContact(\n run: ApplyRun,\n contactId: string,\n values: Readonly<Record<string, unknown>>,\n): Promise<{ skipped: string[] }> {\n const ref = contactsOf(run.env).doc(contactId)\n const snapshot = await ref.get()\n const contact = snapshot.data()\n if (!snapshot.exists || !contact || !visibleIn(run.env, contact['visibleTo'])) {\n throw new TransferEngineError('notFound', 404, 'That contact no longer exists.')\n }\n // With no site the row is written to the holder the person already has.\n const groupId =\n (run.hostId ? run.group : contactPrimaryGroup(contact, run.env.org)).groupId ||\n ORG_CONTACT_HOLDER_ID\n const stored = readContactFacet(contact, groupId)\n const { fields, skipped } = profileFields(values, stored)\n let linkedCompanyName = ''\n if ('company' in values) {\n const link = await companyLinkFor(run, values['company'])\n fields.companyId = link.id\n linkedCompanyName = link.name\n }\n if (fields.reportsToContactId) {\n const managerId = fields.reportsToContactId\n if (managerId === contactId) throw new TransferEngineError('invalid', 422, CONTACT_REPORTS_TO_SELF_REFUSAL)\n if (await contactReportsToLoops(run.readContact, contactId, managerId, groupId)) {\n throw new TransferEngineError('invalid', 422, CONTACT_REPORTS_TO_LOOP_REFUSAL)\n }\n }\n const custom = customWriteValues(values, run.definitions, 'contact')\n if ('error' in custom) throw new TransferEngineError('invalid', 422, custom.error)\n const patch = contactPatch(contact, groupId, fields, custom.values, linkedCompanyName)\n if ('refused' in patch) throw new TransferEngineError('invalid', 422, patch.refused)\n const update: Record<string, unknown> = { ...(patch.update ?? {}) }\n\n let movedFrom: string | null = null\n if ('lifecycleStage' in values) {\n const stage = crmLifecycleStageOf(values['lifecycleStage'])\n const previous = isContactLifecycleStage(stored.lifecycleStage) ? stored.lifecycleStage : ''\n if ((stage ?? '') !== previous) {\n update[contactFacetPath(groupId, 'lifecycleStage')] = stage ?? FieldValue.delete()\n if (stage) movedFrom = previous\n }\n }\n if (!Object.keys(update).length) return { skipped }\n update['updatedAt'] = FieldValue.serverTimestamp()\n await ref.update(update)\n await restampCrmListFieldsOf([ref], 'contacts')\n await settleCompanyCounts(run.env.orgRef.collection(CRM_COLLECTIONS.companies), [\n { contactId, ref, update, counts: patch.counts },\n ])\n if (movedFrom !== null && run.hostId) {\n await emitHostEvent(\n run.hostId,\n 'contactStageChanged',\n {\n contactId,\n email: textOf(contact['email']),\n lifecycleStage: String(crmLifecycleStageOf(values['lifecycleStage'])),\n previousStage: movedFrom,\n },\n run.actorUid ? { actor: { kind: 'member', uid: run.actorUid, email: null } } : {},\n ).catch((error: unknown) => console.error('[crm] transfer stage event failed', contactId, error))\n }\n return { skipped }\n}\n\n/**\n * The site a contact import is filed under. None, when the org's records belong\n * to every site (AGL-4122): the file then has no Source site, and the people in\n * it are the organization's. An org that keeps its sites apart must name one.\n */\nfunction contactImportSite(env: CrmTransferEnv): { hostId: string | null; group: ConsentGroup } {\n if (!env.hostId && crmDefaultScopeOf(env.org) === 'org') {\n return { hostId: null, group: ORG_HOLDER_GROUP }\n }\n return requireSite(env, 'contacts')\n}\n\n/** Creates one person through the capture door, then what the door does not take. */\nasync function createContact(\n run: ApplyRun,\n values: Readonly<Record<string, unknown>>,\n batch: Awaited<ReturnType<typeof prepareContactCaptureBatch>> | undefined,\n): Promise<{ contactId: string; created: boolean; skipped: string[] }> {\n const email = textOf(values['email'])\n const { fields, skipped } = profileFields(values, { sources: {}, interactions: [] })\n const link = 'company' in values ? await companyLinkFor(run, values['company']) : null\n const custom = customWriteValues(values, run.definitions, 'contact')\n if ('error' in custom) throw new TransferEngineError('invalid', 422, custom.error)\n const stage = crmLifecycleStageOf(values['lifecycleStage'])\n const facet = {\n ...(fields.phone ? { phone: fields.phone } : {}),\n ...(fields.jobTitle ? { jobTitle: fields.jobTitle } : {}),\n ...(fields.salutation ? { salutation: fields.salutation } : {}),\n ...(fields.firstName ? { firstName: fields.firstName } : {}),\n ...(fields.lastName ? { lastName: fields.lastName } : {}),\n ...(fields.department ? { department: fields.department } : {}),\n ...Object.fromEntries(PHONE_FIELDS.filter((key) => fields[key]).map((key) => [key, fields[key]])),\n ...(fields.birthdate ? { birthdate: fields.birthdate } : {}),\n ...(fields.assistantName ? { assistantName: fields.assistantName } : {}),\n ...(fields.leadSource ? { leadSource: fields.leadSource } : {}),\n ...(fields.doNotCall ? { doNotCall: true } : {}),\n ...(fields.address ? { address: fields.address } : {}),\n ...(fields.otherAddress ? { otherAddress: fields.otherAddress } : {}),\n // The link AND the name: the merge fields read the facet's name.\n ...(link?.id ? { companyId: link.id, companyName: link.name } : {}),\n ...(fields.ownerUid ? { ownerUid: fields.ownerUid } : {}),\n ...(Object.keys(custom.values).length ? { custom: custom.values } : {}),\n }\n const staged = stage ? { lifecycleStage: stage } : {}\n /*\n * A SITE AND AN ADDRESS ARE A CAPTURE. Anything less is a person no capture\n * door could take, so a file can hold people with no email and be imported\n * with no site (AGL-4122). A row still has to say who it is about.\n */\n if (!email && !fields.name && !facet.phone && !facet.firstName && !facet.lastName && !fields['mobilePhone']) {\n throw new TransferEngineError('invalid', 422, 'A row needs a name, an email or a phone number.')\n }\n const verdict =\n run.hostId && email\n ? await captureHostContact({\n hostId: run.hostId,\n email,\n ...(fields.name ? { name: fields.name } : {}),\n source: 'import',\n ...(run.actorUid\n ? { actor: { kind: 'member' as const, uid: run.actorUid, email: null } }\n : {}),\n interaction: { summary: CONTACT_TRANSFER_INTERACTION_SUMMARY },\n tags: fields.tags ?? [],\n // The file's own stage, or none: `lifecycleStage` from the column.\n facet: { ...facet, ...staged },\n // An import files nobody under a campaign; the record's picker does.\n campaignIds: [],\n ...(batch ? { batch } : {}),\n })\n : await addContactByHand({\n orgId: run.env.orgId,\n org: run.env.org,\n sourceHostId: run.hostId,\n email: email || null,\n ...(fields.name ? { name: fields.name } : {}),\n facet: { ...facet, ...staged },\n tags: fields.tags ?? [],\n })\n if ('refused' in verdict) {\n throw new TransferEngineError('invalid', 422, CAPTURE_REFUSALS[verdict.refused] ?? CAPTURE_REFUSALS['error'] as string)\n }\n // What the capture door does not write: who they report to, and the notes.\n const extra: Record<string, unknown> = {}\n if (fields.reportsToContactId) extra['reportsTo'] = fields.reportsToContactId\n if (fields.notes) extra['notes'] = fields.notes\n if (Object.keys(extra).length) await writeContact(run, verdict.contactId, extra)\n return { contactId: verdict.contactId, created: verdict.created, skipped }\n}\n\n/** The `crm.contacts` hooks. */\nexport function contactsTransferResource(): TransferRecordsHooks {\n return {\n async fields(ctx) {\n const env = await crmTransferEnv(ctx)\n return {\n standard: CONTACT_TRANSFER_FIELDS,\n derived: CONTACT_TRANSFER_DERIVED,\n system: CRM_TIMESTAMP_FIELDS,\n custom: crmCustomTransferFields(await crmCustomDefinitions(env, 'contact'), 'contact'),\n groups: CONTACT_TRANSFER_GROUPS,\n }\n },\n matchKeys: [\n { fieldId: TRANSFER_ID_FIELD, normalizer: 'aglynId' },\n { fieldId: 'email', normalizer: 'email' },\n ],\n aliases: CONTACT_ALIASES,\n lookupTargets: { [CRM_MEMBERS_TARGET]: crmMembersTarget() },\n\n // The people a workspace holds are its own on every plan (AGL-2839):\n // exporting them asks nothing of the plan.\n async count(ctx, options) {\n const env = await crmTransferEnv(ctx)\n return countCrmExport(env, contactsOf(env), options, (data) => visibleIn(env, data['visibleTo'], options.scopeTokens))\n },\n\n async readPage(ctx, cursor, fieldIds, options) {\n const env = await crmTransferEnv(ctx)\n const definitions = await crmCustomDefinitions(env, 'contact')\n const emails = fieldIds.includes('owner') ? await crmMemberEmails(env.orgId) : new Map<string, string>()\n return readCrmExportPage(\n env,\n contactsOf(env),\n cursor,\n options,\n (data) => visibleIn(env, data['visibleTo'], options?.scopeTokens),\n async (docs) => {\n const managers = new Map<string, string>()\n if (fieldIds.includes('reportsTo')) {\n const ids = [\n ...new Set(\n docs\n .map((doc) => textOf(readContactFacet(doc.data, holderOf(env, doc.data).groupId).reportsToContactId))\n .filter((id) => id && !id.includes('/')),\n ),\n ]\n for (let at = 0; at < ids.length; at += 500) {\n const page = ids.slice(at, at + 500)\n for (const snapshot of await env.firestore.getAll(...page.map((id) => contactsOf(env).doc(id)))) {\n if (snapshot.exists) managers.set(snapshot.id, textOf(snapshot.get('email')))\n }\n }\n }\n return docs.map((doc) =>\n pickValues(\n contactTransferValues(doc.id, doc.data, holderOf(env, doc.data), definitions, {\n owner: (uid) => emails.get(uid),\n manager: (id) => managers.get(id),\n }),\n fieldIds,\n ),\n )\n },\n )\n },\n\n async lookup(ctx, requests) {\n const env = await crmTransferEnv(ctx)\n const definitions = await crmCustomDefinitions(env, 'contact')\n const visible = (data: Record<string, unknown>) => visibleIn(env, data['visibleTo'])\n const found = { lookup: new Map<string, string[]>(), docs: new Map<string, Record<string, unknown>>() }\n for (const request of requests) {\n if (request.fieldId === TRANSFER_ID_FIELD) await lookupById(env, contactsOf(env), request, visible, found)\n else if (request.fieldId === 'email') {\n await lookupByField(contactsOf(env), request, 'email', (value) => value.trim().toLowerCase(), visible, found)\n }\n }\n return lookupResult(found, (id, data) => contactTransferValues(id, data, holderOf(env, data), definitions))\n },\n\n async suggest(ctx, request) {\n const env = await crmTransferEnv(ctx)\n const answer: Record<string, ReturnType<typeof rankTransferLookupSuggestions>> = {}\n for (const value of request.values) {\n // People sharing the value's first word: the token the list's own\n // search reads, so a mistyped domain still finds the person.\n const word = value.trim().toLowerCase().split(/[\\s@.+_-]+/)[0]?.slice(0, 12) ?? ''\n if (word.length < 2) {\n answer[value] = []\n continue\n }\n const snapshot = await contactsOf(env).where('searchTokens', 'array-contains', word).limit(IN_LIMIT).get()\n const candidates = snapshot.docs\n .filter((doc) => visibleIn(env, doc.get('visibleTo')))\n .flatMap((doc) => {\n const email = textOf(doc.get('email'))\n return email ? [{ recordId: doc.id, label: email }] : []\n })\n answer[value] = rankTransferLookupSuggestions(value, candidates)\n }\n return answer\n },\n\n async picklists(ctx, ids) {\n const env = await crmTransferEnv(ctx)\n return crmPicklistLists(env, ids, await crmCustomDefinitions(env, 'contact'))\n },\n\n async addPicklistValues(ctx, picklistId, values) {\n await addCrmPicklistValues(await crmTransferEnv(ctx), picklistId, values)\n },\n\n lockedRules: () => [\n {\n fieldId: 'email',\n reason: 'An existing contact’s email is who they are; change it on the contact’s page.',\n forced: { mode: 'keepExisting' },\n },\n {\n fieldId: 'marketingConsent',\n reason:\n 'Consent is never taken from a file: a person gives it on a form, or a team member records how it was given on the contact’s page.',\n refuseValues: true,\n },\n {\n fieldId: 'lifecycleStage',\n reason: 'A stage moves forward only: a later stage in the file advances the contact, an earlier one is held back.',\n forced: { mode: 'overwrite' },\n },\n {\n fieldId: 'doNotCall',\n reason: 'A file can say a person asked not to be called, never that they took it back.',\n forced: { mode: 'overwrite' },\n },\n {\n fieldId: 'name',\n reason: 'The full name is built from the first and last names once either is set.',\n },\n ],\n\n async plan(ctx, input) {\n const env = await crmTransferEnv(ctx)\n requireCrmSuite(env, SUITE_ACT)\n contactImportSite(env)\n const plan = buildTransferPlan(input)\n return holdBackChanges(plan, (row, change) => {\n const before = row.recordId ? input.existing.get(row.recordId) : undefined\n if (change.fieldId === 'lifecycleStage' && before) {\n const from = crmLifecycleStageRank(before['lifecycleStage'])\n const to = crmLifecycleStageRank(change.after)\n if (from >= 0 && (to < from || to < 0)) return 'A stage moves forward only.'\n }\n if (change.fieldId === 'doNotCall' && before?.['doNotCall'] === true && change.after !== true) {\n return 'Do not call is turned off on the contact’s page, never by a file.'\n }\n if (change.fieldId === 'name') {\n const values = Object.fromEntries(row.diff.map((entry) => [entry.fieldId, entry.after]))\n const first = 'firstName' in values ? values['firstName'] : before?.['firstName']\n const last = 'lastName' in values ? values['lastName'] : before?.['lastName']\n if (textOf(first) || textOf(last)) return 'Built from the first and last names.'\n }\n return null\n })\n },\n\n async apply(ctx, chunk, writer) {\n const env = await crmTransferEnv(ctx)\n requireCrmSuite(env, SUITE_ACT)\n const { hostId, group } = contactImportSite(env)\n const run: ApplyRun = {\n env,\n hostId,\n group,\n actorUid: ctx.actorUid,\n definitions: await crmCustomDefinitions(env, 'contact'),\n companies: new Map(),\n admit: crmRecordsRoom(env),\n readContact: cachedContactReader(contactsOf(env)),\n }\n const rows = chunk.rows as PlannedTransferRow[]\n const creating: string[] = []\n for (const row of rows) {\n if (row.verdict === 'create' && !(await writer.alreadyApplied(row.index))) {\n const email = textOf(plannedValues(row)['email'])\n if (email) creating.push(email)\n }\n }\n // The capture door's lookups for every new person at once (AGL-3423).\n const batch =\n hostId && creating.length ? await prepareContactCaptureBatch(hostId, creating) : undefined\n const results: TransferRowResult[] = []\n const undo: TransferUndoEntry[] = []\n\n await eachWithinBudget(rows, () => writer.timeLeftMs(), async (row) => {\n const earlier = await writer.alreadyApplied(row.index)\n if (earlier) {\n results.push(earlier)\n return\n }\n const changed = row.diff.map((change) => change.fieldId)\n let result: TransferRowResult\n let entry: TransferUndoEntry | undefined\n try {\n const values = plannedValues(row)\n if (row.verdict === 'update' && row.recordId) {\n const before = (await readContacts(env, [row.recordId], run.definitions)).get(row.recordId)\n if (!before) throw new TransferEngineError('notFound', 404, 'That contact no longer exists.')\n const { skipped } = await writeContact(run, row.recordId, values)\n const after = (await readContacts(env, [row.recordId], run.definitions)).get(row.recordId) ?? {}\n const message = skippedMessage(skipped)\n result = { row: row.index, outcome: 'updated', recordId: row.recordId, ...(message ? { message } : {}) }\n entry = {\n row: row.index,\n recordId: row.recordId,\n action: 'updated',\n previous: pickValues(before, changed),\n written: pickValues(after, changed),\n }\n } else {\n const made = await createContact(run, values, batch)\n const after = (await readContacts(env, [made.contactId], run.definitions)).get(made.contactId) ?? {}\n const message = made.created\n ? skippedMessage(made.skipped)\n : 'Merged into the contact already holding this email.'\n result = {\n row: row.index,\n outcome: made.created ? 'created' : 'updated',\n recordId: made.contactId,\n ...(message ? { message } : {}),\n }\n // A merge into a person somebody else created meanwhile has no\n // \"before\" to put back, so undo leaves it alone.\n entry = made.created\n ? { row: row.index, recordId: made.contactId, action: 'created', written: pickValues(after, changed) }\n : undefined\n }\n } catch (error) {\n result = {\n row: row.index,\n outcome: 'failed',\n ...(row.recordId ? { recordId: row.recordId } : {}),\n message: failureMessage(error),\n }\n entry = undefined\n }\n await writer.markApplied(result, entry)\n results.push(result)\n if (entry) undo.push(entry)\n })\n return { results, undo }\n },\n\n async revert(ctx, snapshot, decisions) {\n const env = await crmTransferEnv(ctx)\n const { hostId, group } = contactImportSite(env)\n const run: ApplyRun = {\n env,\n hostId,\n group,\n actorUid: ctx.actorUid,\n definitions: await crmCustomDefinitions(env, 'contact'),\n companies: new Map(),\n admit: crmRecordsRoom(env),\n readContact: cachedContactReader(contactsOf(env)),\n }\n const current = await readContacts(env, snapshot.entries.map((entry) => entry.recordId), run.definitions)\n const done: TransferUndoStep[] = []\n const conflicts: TransferUndoStep[] = []\n for (const entry of snapshot.entries) {\n const step = planTransferUndo(entry, current.get(entry.recordId) ?? null)\n if (step.action === 'conflict' && decisions?.[entry.recordId] !== 'revert') {\n if (decisions?.[entry.recordId] === 'keep') done.push({ action: 'nothing', recordId: entry.recordId, why: 'alreadyReverted' })\n else conflicts.push(step)\n continue\n }\n if (step.action === 'nothing') {\n done.push(step)\n continue\n }\n if (entry.action === 'created') {\n // The site lets the person go, as \"Remove from this site\" does: a\n // person only this import's site held is deleted, keeping every\n // refusal they made; one another site holds too stays theirs.\n const contactRef = contactsOf(env).doc(entry.recordId)\n let holders: string[] = []\n const removal = await removeContactKeepingRefusals({\n contactRef,\n decide: (contact) => {\n holders = contactFacetHolders(contact)\n return planContactDetach(contact, group)\n },\n nowMs: Date.now(),\n })\n if (removal.outcome === 'deleted') {\n await clearReportsToOf(env.firestore, contactsOf(env), holders, entry.recordId)\n await sweepDealContactRoles(\n env.firestore,\n env.orgRef.collection(CRM_COLLECTIONS.deals),\n entry.recordId,\n null,\n '[crm] transfer undo',\n )\n } else if (removal.outcome === 'detached') {\n await clearReportsToOf(env.firestore, contactsOf(env), [group.groupId], entry.recordId)\n await restampCrmListFieldsOf([contactRef], 'contacts').catch(() => undefined)\n }\n done.push({ action: 'delete', recordId: entry.recordId })\n continue\n }\n const values = step.action === 'restore' || step.action === 'conflict' ? step.values : {}\n await writeContact(run, entry.recordId, values)\n done.push(step)\n }\n return { done, conflicts }\n },\n }\n}\n"],"names":["composeContactName","crmDefaultScopeOf","ORG_CONTACT_HOLDER_ID","contactDisplayName","contactFacetPath","CRM_COLLECTIONS","interactionsForGroup","isContactLifecycleStage","normalizeContactBirthdate","normalizePhone","planContactDetach","readContactFacet","readMarketingBasis","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_EXTRA_PHONE_FIELDS","PHONE_FIELDS","buildTransferPlan","planTransferUndo","rankTransferLookupSuggestions","TRANSFER_ID_FIELD","transferLookupNewName","prepareContactCaptureBatch","removeContactKeepingRefusals","restampCrmListFieldsOf","emitHostEvent","FieldValue","contactPrimaryGroup","ORG_HOLDER_GROUP","captureHostContact","CONTACT_NOTES_MAX","cachedContactReader","clearReportsToOf","CONTACT_REPORTS_TO_LOOP_REFUSAL","CONTACT_REPORTS_TO_SELF_REFUSAL","contactFacetHolders","contactReportsToLoops","contactPatch","settleCompanyCounts","sweepDealContactRoles","CONTACT_ALIASES","addCrmPicklistValues","addressFromValues","addressValues","countCrmExport","crmCustomDefinitions","crmMemberEmails","crmMembersTarget","crmPicklistLists","crmRecordsRoom","crmTransferEnv","customTransferValues","customWriteValues","eachWithinBudget","failureMessage","holdBackChanges","IN_LIMIT","isoOf","lookupByField","lookupById","lookupResult","pickValues","plannedValues","readCrmExportPage","requireCrmSuite","requireSite","textOf","TransferEngineError","visibleIn","companyForName","addContactByHand","CONTACT_TRANSFER_DERIVED","CONTACT_TRANSFER_FIELDS","CONTACT_TRANSFER_GROUPS","CRM_MEMBERS_TARGET","CRM_TIMESTAMP_FIELDS","crmCustomTransferFields","crmLifecycleStageOf","crmLifecycleStageRank","CONTACT_TRANSFER_INTERACTION_SUMMARY","SUITE_ACT","contactsOf","env","orgRef","collection","holderOf","contact","group","org","CAPTURE_REFUSALS","band","erased","error","contactTransferValues","id","definitions","names","facet","groupId","text","value","stage","lifecycleStage","basis","ownerUid","managerId","reportsToContactId","name","email","salutation","firstName","lastName","birthdate","phone","mobilePhone","homePhone","otherPhone","fax","doNotCall","assistantName","assistantPhone","jobTitle","department","company","companyName","companyId","reportsTo","manager","address","otherAddress","owner","leadSource","tags","Array","isArray","notes","marketingConsent","sources","Object","keys","lastInteractionAt","interactions","hostIds","atMs","lastEngagedAt","lastEmailEngagementAtMs","createdAt","updatedAt","custom","readContacts","ids","out","Map","at","length","page","slice","filter","includes","snapshot","firestore","getAll","map","doc","data","exists","set","companyLinkFor","run","undefined","create","actorUid","companies","admit","String","get","profileFields","values","stored","fields","skipped","key","max","typed","push","mailing","other","composed","skippedMessage","labels","find","field","label","join","writeContact","contactId","patch","ref","hostId","linkedCompanyName","link","readContact","refused","update","movedFrom","previous","delete","serverTimestamp","counts","previousStage","actor","kind","uid","catch","console","contactImportSite","createContact","batch","fromEntries","staged","verdict","source","interaction","summary","campaignIds","orgId","sourceHostId","extra","created","contactsTransferResource","ctx","standard","derived","system","groups","matchKeys","fieldId","normalizer","aliases","lookupTargets","count","options","scopeTokens","readPage","cursor","fieldIds","emails","docs","managers","Set","lookup","requests","visible","found","request","trim","toLowerCase","suggest","answer","word","split","where","limit","candidates","flatMap","recordId","picklists","addPicklistValues","picklistId","lockedRules","reason","forced","mode","refuseValues","plan","input","row","change","before","existing","from","to","after","diff","entry","first","last","apply","chunk","writer","rows","creating","alreadyApplied","index","results","undo","timeLeftMs","earlier","changed","result","message","outcome","action","written","made","markApplied","revert","decisions","current","entries","done","conflicts","step","why","contactRef","holders","removal","decide","nowMs","Date","now","deals"],"mappings":"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"}
@@ -149,7 +149,6 @@ export const CRM_COMPANIES_RESOURCE = 'crm.companies';
149
149
  label: 'Email',
150
150
  group: 'person',
151
151
  type: 'email',
152
- required: true,
153
152
  matchKey: true,
154
153
  aliases: [
155
154
  'email address',
@@ -159,7 +158,7 @@ export const CRM_COMPANIES_RESOURCE = 'crm.companies';
159
158
  'primary email',
160
159
  'work email'
161
160
  ],
162
- description: 'Who the person is: a contact is found and kept by its email.'
161
+ description: 'Optional. A row with one merges into the contact who has it.'
163
162
  },
164
163
  {
165
164
  id: 'name',