snapshot of https://github.com/frappe/crm.git (develop, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Frontend / Unit Tests & Coverage (push) Has been skipped
Server / Tests (frappe ${{ matrix.frappe-branch }}) (push) Has been skipped
Server / Resolve frappe matrix (push) Failing after 1s
Frontend / Lint (push) Failing after 1s
Frontend / Production Build (push) Failing after 1s
Server / Coverage Wrap Up (push) Has been skipped
Frontend / Coverage Wrap Up (push) Has been skipped
Regenerate POT file (translatable strings) / Regenerate POT file (develop) (push) Failing after 1s
Regenerate POT file (translatable strings) / Regenerate POT file (main-hotfix) (push) Failing after 1s
Some checks failed
Frontend / Unit Tests & Coverage (push) Has been skipped
Server / Tests (frappe ${{ matrix.frappe-branch }}) (push) Has been skipped
Server / Resolve frappe matrix (push) Failing after 1s
Frontend / Lint (push) Failing after 1s
Frontend / Production Build (push) Failing after 1s
Server / Coverage Wrap Up (push) Has been skipped
Frontend / Coverage Wrap Up (push) Has been skipped
Regenerate POT file (translatable strings) / Regenerate POT file (develop) (push) Failing after 1s
Regenerate POT file (translatable strings) / Regenerate POT file (main-hotfix) (push) Failing after 1s
This commit is contained in:
112
crm/domain_enrichment/cross_record.py
Normal file
112
crm/domain_enrichment/cross_record.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
"""Link-time Organization -> Lead/Deal enrichment copy.
|
||||
|
||||
The ONLY cross-record behaviour in Domain Enrichment: when a Lead or Deal links an
|
||||
already-enriched ``CRM Organization``, synchronously copy that Organization's stored
|
||||
enriched fields onto the *empty* fields of the Lead/Deal (fill-empty). The direction
|
||||
is strictly Organization -> Lead/Deal, in-request, on the origin save. There is no
|
||||
background job, no fan-out, and no write back to the Organization.
|
||||
|
||||
The actual field writing reuses :func:`crm.domain_enrichment.mapper.apply_to_document`
|
||||
-- the single result->field authority -- by wrapping the Organization's stored values
|
||||
in an :class:`~crm.domain_enrichment.result.EnrichmentResult`. No crawling happens
|
||||
here; this is a pure record-to-record copy.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import frappe
|
||||
|
||||
from crm.domain_enrichment import mapper
|
||||
from crm.domain_enrichment.config import get_config
|
||||
from crm.domain_enrichment.result import (
|
||||
EnrichmentResult,
|
||||
Field,
|
||||
SocialProfile,
|
||||
)
|
||||
|
||||
# Native enriched fields stored on a CRM Organization. If at least one of these is
|
||||
# populated (or the org has a completed enrichment run) we treat the org as enriched
|
||||
# and worth copying from. Empty source values never overwrite anything (mapper guards).
|
||||
_ORG_ENRICHED_FIELDS = (
|
||||
"company_description",
|
||||
"organization_logo",
|
||||
"industry",
|
||||
"linkedin",
|
||||
"twitter",
|
||||
"facebook",
|
||||
)
|
||||
|
||||
|
||||
def _is_org_enriched(org) -> bool:
|
||||
"""Has this Organization actually been enriched?
|
||||
|
||||
Cleanest signal: a completed ``CRM Enrichment Run`` for the org. We also accept
|
||||
any populated native enriched field as a fallback (an org may carry enriched data
|
||||
without a run row, e.g. seeded/imported data).
|
||||
"""
|
||||
if frappe.db.exists(
|
||||
"CRM Enrichment Run",
|
||||
{
|
||||
"reference_doctype": "CRM Organization",
|
||||
"reference_name": org.name,
|
||||
"status": "Completed",
|
||||
},
|
||||
):
|
||||
return True
|
||||
return any(org.get(fieldname) for fieldname in _ORG_ENRICHED_FIELDS)
|
||||
|
||||
|
||||
def _result_from_organization(org) -> EnrichmentResult:
|
||||
"""Wrap an Organization's stored enriched fields in an EnrichmentResult so the
|
||||
shared mapper can copy them onto the target via the same Field Mapping records."""
|
||||
result = EnrichmentResult(website=org.get("website") or "")
|
||||
result.company_name = Field(value=org.get("organization_name") or "")
|
||||
result.description = Field(value=org.get("company_description") or "")
|
||||
result.logo = Field(value=org.get("organization_logo") or "")
|
||||
result.industry = Field(value=org.get("industry") or "")
|
||||
|
||||
for network in ("linkedin", "twitter", "facebook"):
|
||||
value = org.get(network)
|
||||
if value:
|
||||
result.social_profiles[network] = SocialProfile(value=value)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def copy_enrichment_from_organization(target_doc) -> list[str]:
|
||||
"""Copy enriched fields from the linked CRM Organization onto ``target_doc``.
|
||||
|
||||
``target_doc`` is a CRM Lead or CRM Deal that has just linked an Organization
|
||||
(Lead: ``organization`` is free-text resolved to an org name; Deal: ``organization``
|
||||
is a Link to CRM Organization). If that Organization is actually enriched, its
|
||||
stored enriched fields are copied onto the *empty* fields of ``target_doc`` using
|
||||
the shared mapper with fill-empty semantics, so user-entered data is never touched.
|
||||
|
||||
Best-effort: never raises, never blocks the Lead/Deal save. Returns the list of
|
||||
filled field labels (empty when nothing was copied).
|
||||
"""
|
||||
try:
|
||||
org_name = target_doc.get("organization")
|
||||
if not org_name or not frappe.db.exists("CRM Organization", org_name):
|
||||
return []
|
||||
|
||||
org = frappe.get_cached_doc("CRM Organization", org_name)
|
||||
if not _is_org_enriched(org):
|
||||
return []
|
||||
|
||||
cfg = get_config()
|
||||
result = _result_from_organization(org)
|
||||
# Fill-empty only: force fill-empty semantics for every mapping (even ones
|
||||
# configured "Always refresh") so the link-time copy never
|
||||
# overwrites or clears user-entered data. apply_to_document mutates target_doc
|
||||
# in place and does NOT save (the caller's save persists it).
|
||||
return mapper.apply_to_document(target_doc, result, cfg, fill_empty_only=True)
|
||||
except Exception:
|
||||
frappe.log_error(
|
||||
title="Domain Enrichment: Org -> Lead/Deal copy failed",
|
||||
message=f"{target_doc.doctype} {target_doc.name}: {frappe.get_traceback()}",
|
||||
)
|
||||
return []
|
||||
Reference in New Issue
Block a user