from __future__ import annotations from .crm_mixin import CrmMixin from .crm_schema_helpers import ( _configured_or_search_fields, _date_field, _existing_field_names, _field_list, _field_names, _find_field, _find_named_field, _metadata_error, _relation_field, _template_sort, ) from .crm_shared import Any class CrmMetadataMixin(CrmMixin): def crm_health(self) -> dict[str, Any]: health = self.client.health(self.config.customer.resource, self.config.customer.id_field) configured = { "env": self.config.env_name, "data_source": self.config.data_source, "collections": { "customers": self.config.customer.resource, "contacts": self.config.contact.resource, "followups": self.config.followup.resource, "teams": self.config.team.resource, "projects": self.config.project.resource, "project_updates": self.config.project.update_resource, }, } return {"ok": bool(health.get("ok")), "nb": health, "configured": configured} def crm_schema(self, include_fields: bool = False) -> dict[str, Any]: schema: dict[str, Any] = { "env": self.config.env_name, "data_source": self.config.data_source, "collections": { "customers": self.config.customer.__dict__, "contacts": self.config.contact.__dict__, "followups": self.config.followup.__dict__, "users": self.config.user.__dict__, "teams": self.config.team.__dict__, "projects": self.config.project.__dict__, }, } if include_fields: schema["metadata"] = self._safe_metadata() return schema def crm_config_template(self) -> dict[str, Any]: metadata = self._safe_metadata() fields_by_kind = metadata.get("fields", {}) if isinstance(metadata, dict) else {} customer_fields = _field_list(fields_by_kind.get("customers")) contact_fields = _field_list(fields_by_kind.get("contacts")) followup_fields = _field_list(fields_by_kind.get("followups")) customer_resource = self.config.customer.resource contact_resource = self.config.contact.resource return { "env": self.config.env_name, "dataSource": self.config.data_source, "users": { "resource": self.config.user.resource, "fields": self.config.user.fields, "matchFields": self.config.user.match_fields, "labelFields": self.config.user.label_fields, }, "collections": { "customers": { "resource": customer_resource, "fields": _field_names(customer_fields), "searchFields": _configured_or_search_fields( customer_fields, ["f_q2q60q7bqbg", "f_kh_english_name", "f_territorial_name"], ), # Ordered: the first non-empty one becomes the customer's # displayed name everywhere it is referenced. "nameFields": _existing_field_names( customer_fields, ["f_q2q60q7bqbg", "f_kh_english_name", "f_territorial_name"], ) or self.config.customer.name_fields, "managerField": _find_named_field(customer_fields, ["kehu_manage_person"]) or self.config.customer.manager_field, "detailFields": { key: value for key, value in self.config.customer.detail_fields.items() if _find_named_field(customer_fields, [value]) }, "statusField": _find_named_field(customer_fields, ["kh_progress"]) or _find_field(customer_fields, ["进度", "状态", "status"]), "sourceField": _find_named_field(customer_fields, ["f_225h6n6v654"]) or _find_field(customer_fields, ["客户来源", "来源", "source"]), "ownerField": _find_named_field(customer_fields, ["kehu_followup_person"]) or self.config.customer.owner_field, "sort": self.config.customer.sort, }, "contacts": { "resource": contact_resource, "fields": _field_names(contact_fields), "searchFields": _configured_or_search_fields( contact_fields, ["f_2zpg91gtmwp", "khlxr_phone_number", "khlxr_email"], ), "nameFields": _existing_field_names(contact_fields, ["f_2zpg91gtmwp"]) or self.config.contact.name_fields, "titleField": _find_named_field(contact_fields, ["f_tn8zdm2f938"]) or self.config.contact.title_field, "phoneField": _find_named_field(contact_fields, ["khlxr_phone_number"]) or self.config.contact.phone_field, "emailField": _find_named_field(contact_fields, ["khlxr_email"]) or self.config.contact.email_field, "customerField": _find_named_field(contact_fields, ["kehulianxiren_client"]) or _relation_field(contact_fields, ["公司", "客户"]) or self.config.contact.customer_field, "sort": self.config.contact.sort, }, "followups": { "resource": self.config.followup.resource, "fields": _field_names(followup_fields), "searchFields": _configured_or_search_fields(followup_fields, ["f_19iplxz0nds"]), "customerField": _find_named_field(followup_fields, ["gjjl_client"]) or _relation_field(followup_fields, ["客户"]) or self.config.followup.customer_field, "contactField": _find_named_field(followup_fields, ["gjjl_client_contacts"]) or _relation_field(followup_fields, ["客户联系人", "联系人"]) or self.config.followup.contact_field, "ownerField": _find_named_field(followup_fields, ["gjjl_followup_person"]) or self.config.followup.owner_field, "ownerFields": _existing_field_names( followup_fields, ["gjjl_followup_person", "createdBy"], ) or self.config.followup.owner_fields, "dateField": _find_named_field(followup_fields, ["f_srysmjkwfq1"]) or _date_field(followup_fields) or self.config.followup.date_field, "contentField": _find_named_field(followup_fields, ["f_19iplxz0nds"]) or self.config.followup.content_field, "typeField": _find_named_field(followup_fields, ["gjjl_followup_type"]) or self.config.followup.type_field, "statusField": _find_named_field(followup_fields, ["f_hskl7gjvos9"]) or self.config.followup.status_field, "projectField": _find_named_field(followup_fields, ["f_gjjl_projects"]) or self.config.followup.project_field, "locationField": _find_named_field(followup_fields, ["f_b5c4hfi5og6"]) or self.config.followup.location_field, "sort": _template_sort( _find_named_field(followup_fields, ["f_srysmjkwfq1"]) or _date_field(followup_fields) or self.config.followup.date_field, self.config.followup.sort, ), }, }, "metadata": { "field_errors": metadata.get("field_errors", {}) if isinstance(metadata, dict) else {}, "note": "Review inferred owner/status/source/date fields before using this template in production.", }, } def _safe_metadata(self) -> dict[str, Any]: collections = { "customers": self.config.customer.resource, "contacts": self.config.contact.resource, "followups": self.config.followup.resource, "teams": self.config.team.resource, "projects": self.config.project.resource, "project_updates": self.config.project.update_resource, } fields: dict[str, list[dict[str, Any]]] = {} field_errors: dict[str, dict[str, str]] = {} for label, collection_name in collections.items(): try: response = self.client.list_collection_fields( collection_name, page_size=500, paginate=False, ) fields[label] = self._data(response) except Exception as error: field_errors[label] = _metadata_error(error) return {"collections": collections, "fields": fields, "field_errors": field_errors}