From 3769482f56ddf0f89eb2d5b9d92cea8838ce2efc Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Sun, 7 Sep 2025 02:33:54 +0300 Subject: [PATCH] Features: 1) Add `_create_contact` and `_get_customer_name` to handle customer-related details; 2) Introduce `process_order_changes` to streamline order processing. Fixes: 1) Correct logging format for CRM integration error messages. Extra: Remove unused fields `pipeline_id` and `stage_map`; refine `_build_lead_payload`; enhance error logging with stack traces. --- core/crm/amo/gateway.py | 82 ++++++++++++++++++++++++++++++++++++----- core/models.py | 8 ++-- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/core/crm/amo/gateway.py b/core/crm/amo/gateway.py index b8b0d272..42209bd1 100644 --- a/core/crm/amo/gateway.py +++ b/core/crm/amo/gateway.py @@ -1,5 +1,6 @@ import logging import time +import traceback from typing import Optional import requests @@ -39,8 +40,6 @@ class AmoCRM: self.client_id, self.client_secret, self.redirect_uri, - self.pipeline_id, - self.stage_map, self.fns_api_key, ] ): @@ -70,17 +69,82 @@ class AmoCRM: return {"Authorization": f"Bearer {self._token()}", "Content-Type": "application/json"} def _build_lead_payload(self, order: Order) -> dict: - name = f"Order #{order.human_readable_id}" + name = f"Заказ #{order.human_readable_id}" price = int(round(order.total_price)) - stage_id = self.stage_map.get(order.status) - payload = {"name": name, "price": price, "pipeline_id": self.pipeline_id} - if stage_id: - payload["status_id"] = stage_id + payload: dict = {"name": name, "price": price} + contact_id = self._create_contact(order) + if contact_id: + payload["_embedded"] = {"contacts": [{"id": contact_id}]} if self.responsible_user_id: payload["responsible_user_id"] = self.responsible_user_id return payload - def upsert_order(self, order: Order) -> str: + def _get_customer_name(self, order: Order) -> str: + if not order.attributes.get("business_identificator"): + return order.user.get_full_name() or ( + f"{order.attributes.get('customer_name')} | " + f"{order.attributes.get('customer_phone_number') or order.attributes.get('customer_email')}" + ) + try: + business_identificator = order.attributes.get("business_identificator") + r = requests.get( + f"https://api-fns.ru/api/egr?req={business_identificator}&key={self.fns_api_key}", timeout=15 + ) + body = r.json() + except requests.exceptions.RequestException as rex: + logger.error(f"Unable to get company info with FNS: {rex}") + logger.error(traceback.format_exc()) + return "" + + ul = body.get("ЮЛ") + ip = body.get("ИП") + + if ul and not ip: + return f"{ul.get('НаимСокрЮЛ')} | {business_identificator}" + if ip and not ul: + return f"ИП {ip.get('ФИОПолн')} | {business_identificator}" + + return "" + + def _create_contact(self, order: Order) -> int | None: + try: + customer_name = self._get_customer_name(order) + + if customer_name: + r = requests.get( + f"{self.base}/api/v4/contacts", + headers=self._headers().update({"filter[name]": customer_name, "limit": 1}), + timeout=15, + ) + if r.status_code == 200: + body = r.json() + return body.get("_embedded", {}).get("contacts", [{}])[0].get("id", None) + create_contact_payload = {"name": customer_name} + + if self.responsible_user_id: + create_contact_payload["responsible_user_id"] = self.responsible_user_id + + if order.user: + create_contact_payload["first_name"] = order.user.first_name or "" + create_contact_payload["last_name"] = order.user.last_name or "" + + r = requests.post( + f"{self.base}/api/v4/contacts", json={"name": customer_name}, headers=self._headers(), timeout=15 + ) + + if r.status_code == 200: + body = r.json() + return body.get("_embedded", {}).get("contacts", [{}])[0].get("id", None) + + else: + return None + + except requests.exceptions.RequestException as rex: + logger.error(f"Unable to create a company in AmoCRM: {rex}") + logger.error(traceback.format_exc()) + raise CRMException("Unable to create a company in AmoCRM") from rex + + def process_order_changes(self, order: Order) -> str: with transaction.atomic(): try: link: Optional[OrderCrmLink] = OrderCrmLink.objects.get(order=order) @@ -102,7 +166,7 @@ class AmoCRM: r.raise_for_status() body = r.json() lead_id = str(body["_embedded"]["leads"][0]["id"]) - OrderCrmLink.objects.create(order_uuid=order.uuid, crm_lead_id=lead_id) + OrderCrmLink.objects.create(order=order, crm_lead_id=lead_id, crm=self.instance) return lead_id def update_order_status(self, crm_lead_id: str, new_status_code: str) -> None: diff --git a/core/models.py b/core/models.py index c86fd60c..135515fd 100644 --- a/core/models.py +++ b/core/models.py @@ -1789,20 +1789,20 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi crm_link = crm_links.first() crm_integration = create_object(crm_link.crm.integration_location, crm_link.crm.name) try: - crm_integration.upsert_order(self) + crm_integration.process_order_changes(self) return True except Exception as e: - logger.error(f"failed to trigger crm integration {crm_link.crm.name} for order {self.uuid}: {e}") + logger.error(f"failed to trigger CRM integration {crm_link.crm.name} for order {self.uuid}: {e}") logger.error(traceback.format_exc()) return False else: crm = CustomerRelationshipManagementProvider.objects.get(default=True) crm_integration = create_object(crm.integration_location, crm.name) try: - crm_integration.upsert_order(self) + crm_integration.process_order_changes(self) return True except Exception as e: - logger.error(f"failed to trigger crm integration {crm.name} for order {self.uuid}: {e}") + logger.error(f"failed to trigger CRM integration {crm.name} for order {self.uuid}: {e}") logger.error(traceback.format_exc()) return False