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.
This commit is contained in:
parent
23e4611c5e
commit
3769482f56
2 changed files with 77 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
@ -39,8 +40,6 @@ class AmoCRM:
|
||||||
self.client_id,
|
self.client_id,
|
||||||
self.client_secret,
|
self.client_secret,
|
||||||
self.redirect_uri,
|
self.redirect_uri,
|
||||||
self.pipeline_id,
|
|
||||||
self.stage_map,
|
|
||||||
self.fns_api_key,
|
self.fns_api_key,
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
|
|
@ -70,17 +69,82 @@ class AmoCRM:
|
||||||
return {"Authorization": f"Bearer {self._token()}", "Content-Type": "application/json"}
|
return {"Authorization": f"Bearer {self._token()}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
def _build_lead_payload(self, order: Order) -> dict:
|
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))
|
price = int(round(order.total_price))
|
||||||
stage_id = self.stage_map.get(order.status)
|
payload: dict = {"name": name, "price": price}
|
||||||
payload = {"name": name, "price": price, "pipeline_id": self.pipeline_id}
|
contact_id = self._create_contact(order)
|
||||||
if stage_id:
|
if contact_id:
|
||||||
payload["status_id"] = stage_id
|
payload["_embedded"] = {"contacts": [{"id": contact_id}]}
|
||||||
if self.responsible_user_id:
|
if self.responsible_user_id:
|
||||||
payload["responsible_user_id"] = self.responsible_user_id
|
payload["responsible_user_id"] = self.responsible_user_id
|
||||||
return payload
|
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():
|
with transaction.atomic():
|
||||||
try:
|
try:
|
||||||
link: Optional[OrderCrmLink] = OrderCrmLink.objects.get(order=order)
|
link: Optional[OrderCrmLink] = OrderCrmLink.objects.get(order=order)
|
||||||
|
|
@ -102,7 +166,7 @@ class AmoCRM:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
body = r.json()
|
body = r.json()
|
||||||
lead_id = str(body["_embedded"]["leads"][0]["id"])
|
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
|
return lead_id
|
||||||
|
|
||||||
def update_order_status(self, crm_lead_id: str, new_status_code: str) -> None:
|
def update_order_status(self, crm_lead_id: str, new_status_code: str) -> None:
|
||||||
|
|
|
||||||
|
|
@ -1789,20 +1789,20 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
|
||||||
crm_link = crm_links.first()
|
crm_link = crm_links.first()
|
||||||
crm_integration = create_object(crm_link.crm.integration_location, crm_link.crm.name)
|
crm_integration = create_object(crm_link.crm.integration_location, crm_link.crm.name)
|
||||||
try:
|
try:
|
||||||
crm_integration.upsert_order(self)
|
crm_integration.process_order_changes(self)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
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())
|
logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
crm = CustomerRelationshipManagementProvider.objects.get(default=True)
|
crm = CustomerRelationshipManagementProvider.objects.get(default=True)
|
||||||
crm_integration = create_object(crm.integration_location, crm.name)
|
crm_integration = create_object(crm.integration_location, crm.name)
|
||||||
try:
|
try:
|
||||||
crm_integration.upsert_order(self)
|
crm_integration.process_order_changes(self)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
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())
|
logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue