@aglyn/plugins-crm 1.0.0 → 1.2.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.
- package/package.json +14 -14
- package/src/lib/components/activity-link-fields.d.ts +34 -0
- package/src/lib/components/activity-link-fields.js +141 -0
- package/src/lib/components/activity-link-fields.js.map +1 -0
- package/src/lib/components/activity-list.d.ts +8 -0
- package/src/lib/components/activity-list.js +4 -2
- package/src/lib/components/activity-list.js.map +1 -1
- package/src/lib/components/activity-queries.d.ts +7 -2
- package/src/lib/components/activity-queries.js +7 -3
- package/src/lib/components/activity-queries.js.map +1 -1
- package/src/lib/components/company-contact-roles-card.d.ts +42 -0
- package/src/lib/components/company-contact-roles-card.js +137 -0
- package/src/lib/components/company-contact-roles-card.js.map +1 -0
- package/src/lib/components/company-detail-page.js +12 -1
- package/src/lib/components/company-detail-page.js.map +1 -1
- package/src/lib/components/company-edit-drawer.js +9 -6
- package/src/lib/components/company-edit-drawer.js.map +1 -1
- package/src/lib/components/company-picker.js +4 -2
- package/src/lib/components/company-picker.js.map +1 -1
- package/src/lib/components/contact-associations-card.js +15 -1
- package/src/lib/components/contact-associations-card.js.map +1 -1
- package/src/lib/components/contact-detail-page.js +13 -14
- package/src/lib/components/contact-detail-page.js.map +1 -1
- package/src/lib/components/contact-known-by-card.d.ts +2 -1
- package/src/lib/components/contact-known-by-card.js +16 -3
- package/src/lib/components/contact-known-by-card.js.map +1 -1
- package/src/lib/components/contact-list-columns.js +5 -4
- package/src/lib/components/contact-list-columns.js.map +1 -1
- package/src/lib/components/contact-properties-card.js +41 -20
- package/src/lib/components/contact-properties-card.js.map +1 -1
- package/src/lib/components/contact-roles-editor.d.ts +54 -0
- package/src/lib/components/contact-roles-editor.js +270 -0
- package/src/lib/components/contact-roles-editor.js.map +1 -0
- package/src/lib/components/contacts-section.js +6 -4
- package/src/lib/components/contacts-section.js.map +1 -1
- package/src/lib/components/crm-console-sections.js +1 -1
- package/src/lib/components/crm-console-sections.js.map +1 -1
- package/src/lib/components/crm-record-picker.d.ts +19 -0
- package/src/lib/components/crm-record-picker.js +40 -11
- package/src/lib/components/crm-record-picker.js.map +1 -1
- package/src/lib/components/crm-related-to-picker.d.ts +33 -0
- package/src/lib/components/crm-related-to-picker.js +135 -0
- package/src/lib/components/crm-related-to-picker.js.map +1 -0
- package/src/lib/components/crm-site-picker.d.ts +13 -1
- package/src/lib/components/crm-site-picker.js +15 -6
- package/src/lib/components/crm-site-picker.js.map +1 -1
- package/src/lib/components/crm-transfer-buttons.d.ts +5 -0
- package/src/lib/components/crm-transfer-buttons.js +9 -6
- package/src/lib/components/crm-transfer-buttons.js.map +1 -1
- package/src/lib/components/deal-contact-roles-card.js +21 -239
- package/src/lib/components/deal-contact-roles-card.js.map +1 -1
- package/src/lib/components/deal-detail-page.js +2 -1
- package/src/lib/components/deal-detail-page.js.map +1 -1
- package/src/lib/components/deal-edit-drawer.js +11 -7
- package/src/lib/components/deal-edit-drawer.js.map +1 -1
- package/src/lib/components/deals-section.js +5 -4
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/log-activity-dialog.d.ts +27 -9
- package/src/lib/components/log-activity-dialog.js +96 -32
- package/src/lib/components/log-activity-dialog.js.map +1 -1
- package/src/lib/components/new-contact-drawer.d.ts +7 -3
- package/src/lib/components/new-contact-drawer.js +27 -15
- package/src/lib/components/new-contact-drawer.js.map +1 -1
- package/src/lib/components/pipeline-stages-dialog.js +5 -1
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
- package/src/lib/components/record-activity-card.d.ts +11 -2
- package/src/lib/components/record-activity-card.js +90 -5
- package/src/lib/components/record-activity-card.js.map +1 -1
- package/src/lib/components/settings-section.d.ts +4 -2
- package/src/lib/components/settings-section.js +7 -5
- package/src/lib/components/settings-section.js.map +1 -1
- package/src/lib/hooks/crm-activity-client-reads.d.ts +8 -0
- package/src/lib/hooks/crm-activity-client-reads.js +50 -0
- package/src/lib/hooks/crm-activity-client-reads.js.map +1 -0
- package/src/lib/hooks/use-campaign-filing-log.d.ts +2 -1
- package/src/lib/hooks/use-campaign-filing-log.js +10 -3
- package/src/lib/hooks/use-campaign-filing-log.js.map +1 -1
- package/src/lib/hooks/use-crm-org-mount.d.ts +8 -6
- package/src/lib/hooks/use-crm-org-mount.js +2 -1
- package/src/lib/hooks/use-crm-org-mount.js.map +1 -1
- package/src/lib/hooks/use-crm-record-names.d.ts +3 -2
- package/src/lib/hooks/use-crm-record-names.js +4 -4
- package/src/lib/hooks/use-crm-record-names.js.map +1 -1
- package/src/lib/hooks/use-crm-scope.d.ts +17 -5
- package/src/lib/hooks/use-crm-scope.js +12 -5
- package/src/lib/hooks/use-crm-scope.js.map +1 -1
- package/src/lib/model/contact-holder.d.ts +12 -0
- package/src/lib/model/contact-holder.js +20 -1
- package/src/lib/model/contact-holder.js.map +1 -1
- package/src/lib/model/contact-record.d.ts +5 -0
- package/src/lib/model/contact-record.js +7 -1
- package/src/lib/model/contact-record.js.map +1 -1
- package/src/lib/model/contact-update.d.ts +6 -0
- package/src/lib/model/contact-update.js +1 -0
- package/src/lib/model/contact-update.js.map +1 -1
- package/src/lib/model/crm-activity-link.d.ts +117 -0
- package/src/lib/model/crm-activity-link.js +113 -0
- package/src/lib/model/crm-activity-link.js.map +1 -0
- package/src/lib/model/crm-api.d.ts +4 -1
- package/src/lib/model/crm-api.js +9 -5
- package/src/lib/model/crm-api.js.map +1 -1
- package/src/lib/model/deal-board-model.js +5 -3
- package/src/lib/model/deal-board-model.js.map +1 -1
- package/src/lib/model/deal-contact-roles.d.ts +25 -1
- package/src/lib/model/deal-contact-roles.js +30 -4
- package/src/lib/model/deal-contact-roles.js.map +1 -1
- package/src/lib/model/native-vocabulary.d.ts +9 -0
- package/src/lib/model/native-vocabulary.js +13 -0
- package/src/lib/model/native-vocabulary.js.map +1 -1
- package/src/lib/model/settle-stage-names.d.ts +25 -0
- package/src/lib/model/settle-stage-names.js +29 -0
- package/src/lib/model/settle-stage-names.js.map +1 -0
- package/src/lib/native-nav.js +1 -1
- package/src/lib/native-nav.js.map +1 -1
- package/src/lib/server/add-contact-by-hand.d.ts +84 -0
- package/src/lib/server/add-contact-by-hand.js +212 -0
- package/src/lib/server/add-contact-by-hand.js.map +1 -0
- package/src/lib/server/api-v1/contacts.js +61 -23
- package/src/lib/server/api-v1/contacts.js.map +1 -1
- package/src/lib/server/api-v1/crm-activities.js +13 -3
- package/src/lib/server/api-v1/crm-activities.js.map +1 -1
- package/src/lib/server/api-v1/crm-pipelines.d.ts +1 -1
- package/src/lib/server/api-v1/crm-pipelines.js +8 -2
- package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
- package/src/lib/server/api-v1/crm-shared.d.ts +16 -9
- package/src/lib/server/api-v1/crm-shared.js +23 -11
- package/src/lib/server/api-v1/crm-shared.js.map +1 -1
- package/src/lib/server/api-v1/openapi.js +3 -3
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/automation-steps.js +11 -9
- package/src/lib/server/automation-steps.js.map +1 -1
- package/src/lib/server/campaign-filing-activity.js +7 -2
- package/src/lib/server/campaign-filing-activity.js.map +1 -1
- package/src/lib/server/contact-update.d.ts +4 -0
- package/src/lib/server/contact-update.js +71 -10
- package/src/lib/server/contact-update.js.map +1 -1
- package/src/lib/server/convert-host-lead.js +13 -2
- package/src/lib/server/convert-host-lead.js.map +1 -1
- package/src/lib/server/convert-open-lead.js +8 -1
- package/src/lib/server/convert-open-lead.js.map +1 -1
- package/src/lib/server/crm-activity-company.d.ts +25 -0
- package/src/lib/server/crm-activity-company.js +34 -0
- package/src/lib/server/crm-activity-company.js.map +1 -0
- package/src/lib/server/crm-inbound-email.js +21 -15
- package/src/lib/server/crm-inbound-email.js.map +1 -1
- package/src/lib/server/crm-sharing.js +9 -2
- package/src/lib/server/crm-sharing.js.map +1 -1
- package/src/lib/server/deal-contact-roles.d.ts +2 -0
- package/src/lib/server/deal-contact-roles.js +38 -1
- package/src/lib/server/deal-contact-roles.js.map +1 -1
- package/src/lib/server/email-send.js +10 -8
- package/src/lib/server/email-send.js.map +1 -1
- package/src/lib/server/merge-contacts.js +6 -2
- package/src/lib/server/merge-contacts.js.map +1 -1
- package/src/lib/server/org-caller.d.ts +8 -0
- package/src/lib/server/org-caller.js +10 -0
- package/src/lib/server/org-caller.js.map +1 -1
- package/src/lib/server/record-timeline.d.ts +3 -1
- package/src/lib/server/record-timeline.js +11 -3
- package/src/lib/server/record-timeline.js.map +1 -1
- package/src/lib/server.js +152 -43
- package/src/lib/server.js.map +1 -1
- package/src/lib/transfer/contacts.js +83 -48
- package/src/lib/transfer/contacts.js.map +1 -1
- package/src/lib/transfer/fields.js +1 -2
- package/src/lib/transfer/fields.js.map +1 -1
|
@@ -15,7 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { nameSearchFields } from "@aglyn/aglyn/app-utils/name-search";
|
|
18
|
-
import { CAPTURED_BY_HOST_FIELD, composeContactName, CONTACT_BIRTHDATE_REFUSAL, CONTACT_COMPANY_IDS_FIELD, CONTACT_EXTRA_PHONE_FIELDS, CONTACT_FACETS_FIELD, CONTACT_FIELDS_MAX_PER_ORG, CONTACT_LIFECYCLE_STAGES, CONTACT_NAME_PART_MAX, consentGroupForHost, contactFacetPath, createResourceUid, CRM_COLLECTIONS, CRM_LEAD_SOURCE_PICKLIST, CRM_MEDIA_IDS_MAX, CRM_SALUTATION_PICKLIST, isContactLifecycleStage, judgeCrmPicklistValue, MARKETING_CONSENT_BY_HOST_FIELD, marketingConsentFieldsForGroup, marketingConsentHostIds, normalizeAddress, normalizeContactBirthdate, normalizeContactEmail, normalizeCrmMediaIds, normalizePhone, ORG_SCOPE_TOKEN, planContactCompanyLink, readContactCompanyLink, readContactCustomInput, readContactFacet, soloConsentGroup } from "@aglyn/aglyn/server";
|
|
18
|
+
import { CAPTURED_BY_HOST_FIELD, composeContactName, CONTACT_BIRTHDATE_REFUSAL, CONTACT_COMPANY_IDS_FIELD, CONTACT_EXTRA_PHONE_FIELDS, CONTACT_FACETS_FIELD, ORG_CONTACT_HOLDER_ID, CONTACT_FIELDS_MAX_PER_ORG, CONTACT_LIFECYCLE_STAGES, CONTACT_NAME_PART_MAX, consentGroupForHost, crmDefaultScopeOf, crmScopeTokens, contactFacetPath, createResourceUid, CRM_COLLECTIONS, CRM_LEAD_SOURCE_PICKLIST, CRM_MEDIA_IDS_MAX, CRM_SALUTATION_PICKLIST, isContactLifecycleStage, judgeCrmPicklistValue, MARKETING_CONSENT_BY_HOST_FIELD, marketingConsentFieldsForGroup, marketingConsentHostIds, normalizeAddress, normalizeContactBirthdate, normalizeContactEmail, normalizeCrmMediaIds, normalizePhone, ORG_SCOPE_TOKEN, planContactCompanyLink, readContactCompanyLink, readContactCustomInput, readContactFacet, soloConsentGroup } from "@aglyn/aglyn/server";
|
|
19
19
|
import { ApiErrors, apiJson, contactCompanyMirrorValue, crmRecordsQuotaForOrg, listResponse, restampCrmListFieldsAt, settleCompanyContactsCounts } from "@aglyn/tenant-data-admin";
|
|
20
20
|
import { claimWrite, orgOwnsHost, paginate, readJsonBody, requireScope, serialize } from "@aglyn/tenant-data-admin/server/api-v1-kit";
|
|
21
21
|
// The leaf, so a spec that stands a partial barrel in still reaches it.
|
|
@@ -163,12 +163,13 @@ function contactCrmProfile(data, groupId) {
|
|
|
163
163
|
* @param groupId - the holder whose CRM profile to publish, or `null` for
|
|
164
164
|
* the union — see {@link contactCrmProfile}.
|
|
165
165
|
*/ function contactView(doc, groupId = null) {
|
|
166
|
-
var _doc_data,
|
|
166
|
+
var _doc_data, _data_name, _data_tags, _data_notes, _serialize, _serialize1;
|
|
167
167
|
const data = (_doc_data = doc.data()) != null ? _doc_data : {};
|
|
168
168
|
return _extends({
|
|
169
169
|
id: doc.id,
|
|
170
170
|
object: 'contact',
|
|
171
|
-
|
|
171
|
+
// Blank in the store so a sort lists them; no address to an integrator.
|
|
172
|
+
email: data.email || null,
|
|
172
173
|
name: (_data_name = data.name) != null ? _data_name : null,
|
|
173
174
|
tags: (_data_tags = data.tags) != null ? _data_tags : [],
|
|
174
175
|
notes: (_data_notes = data.notes) != null ? _data_notes : null
|
|
@@ -259,9 +260,13 @@ function readContactInput(body, { partial }) {
|
|
|
259
260
|
// The SAME normalizer every capture point uses. Re-implementing the
|
|
260
261
|
// check here would let the API accept an address `upsertHostContact`
|
|
261
262
|
// would reject, and the two would then disagree about who is a duplicate.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
// Optional (AGL-4122): an address that was sent still has to be one, and
|
|
264
|
+
// a person with none is named below instead.
|
|
265
|
+
if (body.email !== undefined && body.email !== null && body.email !== '') {
|
|
266
|
+
const email = normalizeContactEmail(body.email);
|
|
267
|
+
if (!email) errors.email = 'A valid email address is required';
|
|
268
|
+
else values.email = email;
|
|
269
|
+
}
|
|
265
270
|
}
|
|
266
271
|
if (body.name !== undefined) {
|
|
267
272
|
var _body_name;
|
|
@@ -449,9 +454,16 @@ function readContactInput(body, { partial }) {
|
|
|
449
454
|
* sites is this write made on behalf of — and an integrator should not
|
|
450
455
|
* learn two names for it. Beside a refusal the site is accepted, since the
|
|
451
456
|
* refusal still applies to every site and the site now names the facet.
|
|
452
|
-
*/ if (writesCrm && !values.consentSiteId) {
|
|
457
|
+
*/ if (writesCrm && !values.consentSiteId && partial) {
|
|
453
458
|
errors.consentSiteId = 'Required with a CRM profile field — name the site whose profile of this person to write';
|
|
454
459
|
}
|
|
460
|
+
/*
|
|
461
|
+
* WHO THIS IS (AGL-4122). A contact needs no address, so a create needs
|
|
462
|
+
* something else to call them by. With no site and no address the profile is
|
|
463
|
+
* the organization's own, and nothing here narrows that.
|
|
464
|
+
*/ if (!partial && !values.email && !values.name && !values.crm.phone && !values.crm.mobilePhone && !values.crm.firstName && !values.crm.lastName && !errors.email) {
|
|
465
|
+
errors.email = 'Send an email, a name or a phone number';
|
|
466
|
+
}
|
|
455
467
|
if (values.marketingConsent === false && values.consentSiteId && !writesCrm) {
|
|
456
468
|
errors.consentSiteId = 'Not accepted with marketingConsent: false — a refusal applies to every site';
|
|
457
469
|
}
|
|
@@ -580,10 +592,11 @@ function readContactInput(body, { partial }) {
|
|
|
580
592
|
// The holder's name is the first and last names' when either was given.
|
|
581
593
|
const composed = composeContactName(crm.firstName, crm.lastName);
|
|
582
594
|
if (composed) stored['name'] = composed;
|
|
583
|
-
|
|
595
|
+
// The person's holder, or the organization when no site was named (AGL-4122).
|
|
596
|
+
const groupId = siteId ? consentGroupForHost(ctx.org, siteId).groupId : ORG_CONTACT_HOLDER_ID;
|
|
584
597
|
return _extends({
|
|
585
598
|
[CONTACT_FACETS_FIELD]: {
|
|
586
|
-
[
|
|
599
|
+
[groupId]: _extends({
|
|
587
600
|
sources: {
|
|
588
601
|
api: true
|
|
589
602
|
},
|
|
@@ -717,10 +730,11 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
717
730
|
* taking-and-releasing on a genuine refusal. AGL-2278 applies it to the two
|
|
718
731
|
* older creates.
|
|
719
732
|
*
|
|
720
|
-
* `visibleTo` is stamped with `ORG_SCOPE_TOKEN`, as capture does
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
733
|
+
* `visibleTo` is stamped with `ORG_SCOPE_TOKEN`, as capture does — or, in an
|
|
734
|
+
* org that keeps each site's records apart, the named site's group, which
|
|
735
|
+
* that org must then name. A contact written without one matches no
|
|
736
|
+
* `array-contains-any` and is therefore visible on NO site (AGL-1044) — the
|
|
737
|
+
* API would create data nobody can see, which is worse than refusing.
|
|
724
738
|
*/ async function createContact(request, ctx) {
|
|
725
739
|
// Validation stays above the claim, as `createRecord` argues: a
|
|
726
740
|
// deterministic 400 must never take the key at all, so an integrator fixes
|
|
@@ -747,6 +761,25 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
747
761
|
headers: ctx.headers
|
|
748
762
|
});
|
|
749
763
|
}
|
|
764
|
+
/*
|
|
765
|
+
* Who may see the new contact: the whole org, unless the org keeps each
|
|
766
|
+
* site's records apart — then the named site's group, and a contact named
|
|
767
|
+
* by no site has nowhere to belong. A deterministic 400, so above the claim.
|
|
768
|
+
*/ const orgDocument = ctx.org;
|
|
769
|
+
const keepsSitesApart = crmDefaultScopeOf(orgDocument) === 'host';
|
|
770
|
+
if (keepsSitesApart && !consentSiteId) {
|
|
771
|
+
return ApiErrors.badRequest({
|
|
772
|
+
message: 'Contact failed validation',
|
|
773
|
+
code: 'validation_failed',
|
|
774
|
+
fields: {
|
|
775
|
+
consentSiteId: "Required — this organization keeps each site's records apart, so name the site this contact belongs to"
|
|
776
|
+
},
|
|
777
|
+
headers: ctx.headers
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
const visibleTo = keepsSitesApart && consentSiteId ? crmScopeTokens(orgDocument, consentGroupForHost(orgDocument, consentSiteId)) : [
|
|
781
|
+
ORG_SCOPE_TOKEN
|
|
782
|
+
];
|
|
750
783
|
// Judged against the org's definitions, above the claim like every other
|
|
751
784
|
// deterministic 400: an unknown key is named, never dropped (AGL-2601).
|
|
752
785
|
const custom = parsed.values.custom ? readContactCustomInput(parsed.values.custom, await readOrgContactFieldDefinitions(ctx)) : null;
|
|
@@ -780,16 +813,20 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
780
813
|
if ('replay' in claimed) return claimed.replay;
|
|
781
814
|
const { claim } = claimed;
|
|
782
815
|
try {
|
|
816
|
+
var _docs_;
|
|
783
817
|
// Through the org's address index (AGL-2633): an address a merge folded
|
|
784
818
|
// into another record is that record's, and a create on it would mint
|
|
785
819
|
// the duplicate the merge removed.
|
|
786
|
-
|
|
820
|
+
// With no address the number is who they are, and with neither nobody is:
|
|
821
|
+
// two blank emails are not the same person (AGL-4122).
|
|
822
|
+
const phone = crm.phone || crm.mobilePhone || '';
|
|
823
|
+
const existing = email ? await findContactByEmail(collection, email) : phone ? (_docs_ = (await collection.where('phone', '==', phone).limit(1).get()).docs[0]) != null ? _docs_ : null : null;
|
|
787
824
|
if (existing) {
|
|
788
825
|
// Released: the conflict clears if that contact is deleted or merged
|
|
789
826
|
// upstream, and the retry that should then succeed must not replay it.
|
|
790
827
|
await claim.release();
|
|
791
828
|
return ApiErrors.conflict({
|
|
792
|
-
message: `A contact with this email already exists (${existing.id}). Update it instead.`,
|
|
829
|
+
message: `A contact with this ${email ? 'email' : 'phone number'} already exists (${existing.id}). Update it instead.`,
|
|
793
830
|
code: 'contact_exists',
|
|
794
831
|
headers: ctx.headers
|
|
795
832
|
});
|
|
@@ -813,8 +850,11 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
813
850
|
}
|
|
814
851
|
const id = createResourceUid();
|
|
815
852
|
await collection.doc(id).create(_extends({
|
|
816
|
-
email
|
|
817
|
-
|
|
853
|
+
// Blank rather than absent, so a sort by email still lists them.
|
|
854
|
+
email: email != null ? email : ''
|
|
855
|
+
}, !email && phone ? {
|
|
856
|
+
phone
|
|
857
|
+
} : {}, canonicalName ? nameSearchFields(canonicalName) : {}, {
|
|
818
858
|
tags: tags != null ? tags : []
|
|
819
859
|
}, notes ? {
|
|
820
860
|
notes
|
|
@@ -826,7 +866,7 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
826
866
|
] : []
|
|
827
867
|
}, marketingConsent === false ? {
|
|
828
868
|
marketingConsent: false
|
|
829
|
-
} : {},
|
|
869
|
+
} : {}, contactCrmCreateFields(ctx, crm, consentSiteId), {
|
|
830
870
|
// `sources.api` — a first-class provenance value beside `form`,
|
|
831
871
|
// `member`, `order` and `booking`, so a merchant reading the console
|
|
832
872
|
// can see which people an integration put there.
|
|
@@ -838,9 +878,7 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
838
878
|
// "No next activity" filter asks as `null` (AGL-3321).
|
|
839
879
|
nextTaskAtMs: null,
|
|
840
880
|
// AGL-1044/AGL-1037: without this the contact matches no scoped read.
|
|
841
|
-
visibleTo
|
|
842
|
-
ORG_SCOPE_TOKEN
|
|
843
|
-
],
|
|
881
|
+
visibleTo,
|
|
844
882
|
createdAt: Timestamp.now(),
|
|
845
883
|
updatedAt: Timestamp.now()
|
|
846
884
|
}));
|
|
@@ -849,14 +887,14 @@ const contactsCollection = (ctx)=>ctx.firestore.collection('orgs').doc(ctx.orgId
|
|
|
849
887
|
await restampCrmListFieldsAt(collection.doc(id), 'contacts');
|
|
850
888
|
// The address's domain, checked after the response (AGL-3328): a domain
|
|
851
889
|
// that takes no mail makes the new contact read "Would bounce".
|
|
852
|
-
scheduleCapturedEmailCheck({
|
|
890
|
+
if (email) scheduleCapturedEmailCheck({
|
|
853
891
|
orgId: ctx.orgId,
|
|
854
892
|
email
|
|
855
893
|
});
|
|
856
894
|
// The company the body named has one more contact naming it (AGL-2613);
|
|
857
895
|
// a fresh row's plan is the trivial one, and `crmRefErrors` has already
|
|
858
896
|
// required the company to exist.
|
|
859
|
-
if (
|
|
897
|
+
if (crm.companyId) {
|
|
860
898
|
await settleCompanyContactsCounts(companiesCollection(ctx), planContactCompanyLink({
|
|
861
899
|
companyId: null,
|
|
862
900
|
companyIds: [],
|