from fast_bitrix24 import Bitrix from woocommerce import API from tg import send_telegram from dotenv import load_dotenv import html import os load_dotenv() CONSUMER_KEY = os.getenv('CONSUMER_KEY') # токен бота CONSUMER_SECRET = os.getenv('CONSUMER_SECRET') # id чата URLS = os.getenv('URLS') # Настройки подключения wcapi = API( url=URLS, consumer_key = CONSUMER_KEY, # ключ пользователя с правами admin/shop_manager consumer_secret = CONSUMER_SECRET, version="wc/v3", timeout=30 ) # замените на ваш вебхук для доступа к Bitrix24 webhook = os.getenv('WEBHOOKS') bx = Bitrix(webhook, verbose=False) def clean(value): """Woo отдаёт поля с HTML-сущностями: " & и т.п.""" return html.unescape(str(value or '')).strip() def find_contact_by_email(email): """Ищет контакт по email штатным методом Битрикса. ⚠️ crm.contact.list НЕ умеет фильтровать по мультиполю EMAIL: фильтр молча игнорируется и возвращается вся база контактов. После такой выкачки fast_bitrix24 упирается в лимит времени отработки метода (480 сек / 10 мин) и засыпает на ~10 минут — именно из-за этого скрипт «висел» после создания сделки. """ response = bx.call('crm.duplicate.findbycomm', { 'entity_type': 'CONTACT', 'type': 'EMAIL', 'values': [email], }, raw=True) result = (response or {}).get('result') or {} ids = result.get('CONTACT') if isinstance(result, dict) else None return str(ids[0]) if ids else None def woo(order_id): # order_id = 112882 response = wcapi.get(f"orders/{order_id}") if response.status_code != 200: print(f"❌ WooCommerce вернул {response.status_code} по заказу {order_id}: {response.text[:200]}") return None order = response.json() prud = order['line_items'] rows = [] for p in prud: name = p['name'] quantity = p['quantity'] subtotal = p['price'] # 🔹 meta_data — это список, ищем нужный элемент display_value = None display_key = None for meta in p.get('meta_data', []): # Ищем метаданные с ключом 'pa_kh' (вес/объём) if meta.get('key') == 'pa_kh': display_value = meta.get('display_value') display_key = meta.get('display_key') break # формируем название с характеристикой if display_value: name = f"{name} {display_key}: {display_value}" rows.append({ "PRODUCT_NAME": clean(name), "PRICE": subtotal, "QUANTITY": quantity, "CURRENCY_ID": order.get('currency') or "UAH", }) print(rows) # print(order) print(f"Заказ #{order['id']}") print(f"Статус: {order['status']}") print(f"Клиент: {order['billing']['first_name']} {order['billing']['last_name']}") print(f"Сумма: {order['total']} {order['currency']}") print(f"email: {order['billing']['email']}") print(f"phone: {order['billing']['phone']}") print(f"City : {order['shipping']['city']}") print(f"state : {order['shipping']['state']}") print(f"address_1 : {order['shipping']['address_1']}") email = order['billing'].get('email') # 🔹 1. Нормализация email if not email or not isinstance(email, str): print(f"❌ Неверный тип email: {type(email)}") return None search_email = email.lower().strip() print(f"🔍 Ищем контакт с email: {search_email}") # 🔹 2. Поиск через crm.duplicate.findbycomm — единственный метод, # который реально умеет искать по мультиполю EMAIL contact_id = find_contact_by_email(search_email) if contact_id: print(f"✅ Контакт найден: #{contact_id}") # 🔹 3. Если не найден — создаем новый if not contact_id: print(f"⚡ Создаем новый контакт...") add_result = bx.call('crm.contact.add', { 'fields': { 'EMAIL': [{'VALUE': search_email, 'TYPE_ID': 'WORK'}], 'NAME': clean(order['billing'].get('first_name')), 'LAST_NAME': clean(order['billing'].get('last_name')), 'PHONE': [{'VALUE': clean(order['billing'].get('phone')), 'TYPE_ID': 'WORK'}] } }) if isinstance(add_result, dict): contact_id = add_result.get('result') else: contact_id = add_result if contact_id: print(f"✅ Контакт создан: #{contact_id}") else: print(f"❌ Ошибка создания: {add_result}") return None order_status = order['status'] if order_status == 'processing': stage_id = 'PREPARATION' else: stage_id = 'NEW' # Или любой другой статус по умолчанию # 🔹 4. Создаем сделку if contact_id: deal_result = bx.call('crm.deal.add', { "fields": { "TITLE": f"Order {order['id']}", 'STAGE_ID': stage_id, 'CONTACT_IDS': [contact_id], 'COMMENTS': clean(order.get('customer_note')), 'UF_CRM_1684256942409': clean(order['billing'].get('first_name')), 'UF_CRM_1684256770733': clean(order['billing'].get('last_name')), 'UF_CRM_1684256782976': clean(order['billing'].get('email')), 'UF_CRM_5ECB65AB5E752': clean(order['shipping'].get('city')), 'UF_CRM_5ECB65AB672CE': clean(order['shipping'].get('state')), 'UF_CRM_5ECB65AB708F4': clean(order['shipping'].get('address_1')), 'UF_CRM_5ECB65AB7AD15': clean(order['billing'].get('phone')), }, }) if isinstance(deal_result, dict): deal_id = deal_result.get('result') else: deal_id = deal_result if deal_id: print(f"✅ Сделка создана: #{deal_id}") if rows: bx.call("crm.deal.productrows.set", { "ID": deal_id, 'rows': rows }) print(f"📦 Товары добавлены") return deal_id else: print(f"❌ Ошибка сделки: {deal_result}") return None # woo(118961) #920 ЗАГРУЗИЛ def update_deal_stage(order_id): """Находит сделку и меняет её стадию""" result = bx.call('crm.deal.update', { 'id': order_id, 'fields': {'STAGE_ID': 'PREPARATION'} }) if result: print(f"✅ Стадия успешно обновлена") return True else: print(f"❌ Ошибка обновления: {result}") return False def lists_deal(orders): deals = bx.get_all( 'crm.deal.list', params={ 'select': ['ID', 'STAGE_ID'], 'filter': {"=TITLE": f"Order {orders}",} }) if deals and len(deals) > 0: deal = deals[0] # Берём первую найденную сделку return deal['ID'], deal['STAGE_ID'] else: return None, None # Если не найдено def toom(): # Получаем только заказы со статусом "processing" orders = wcapi.get("orders", params={ # "status": "processing", # Фильтр по статусу "per_page": 10, # Количество за раз "orderby": "date", # Сортировка по дате "order": "desc" # Сначала новые }).json() print(f"📋 Найдено заказов в обработке: {len(orders)}") for order in orders: order_id = order['id'] customer_email = order.get('billing', {}).get('email', 'нет') status = order.get('status') idsss, stage_id = lists_deal(order_id) if idsss is None: print(f"✅ #{order_id} | Добавить") deal_id = woo(order_id) if deal_id: send_telegram(f" ✅ Добавил в Б24 {order_id}") else: send_telegram(f" ❌ Не удалось добавить в Б24 {order_id}") elif status == 'processing' and stage_id == "NEW": print(f"✅ #{order_id} | Изменить статус") update_deal_stage(idsss) # изменить статус else: print(f"✅ #{order_id} | 👤 {customer_email} {status}") print(f"{idsss} {stage_id}") print(f"✓ Сделка #{idsss} в стадии {stage_id} — обновление не требуется") # 🔹 Здесь можно запускать вашу логику с Битриксом: # woo(order_id) # или ваш обработчик