276 lines
10 KiB
Python
276 lines
10 KiB
Python
from fast_bitrix24 import Bitrix
|
||
from woocommerce import API
|
||
from tg import send_telegram
|
||
from dotenv import load_dotenv
|
||
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)
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
def woo(order_id):
|
||
# order_id = 112882
|
||
order = wcapi.get(f"orders/{order_id}").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": name,
|
||
"PRICE": subtotal,
|
||
"QUANTITY": quantity,
|
||
"CURRENCY_ID": "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']['email']}
|
||
|
||
"""
|
||
Поиск контакта по email
|
||
⚠️ email должен быть строкой, а не set/list!
|
||
"""
|
||
# 🔹 Защита: если вдруг передали set/list — берём первый элемент
|
||
# 🔹 1. Защита и нормализация email
|
||
# 🔹 1. Нормализация email
|
||
if isinstance(email, (set, list)):
|
||
email = list(email)[0] if email else None
|
||
|
||
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}")
|
||
|
||
contact_id = None
|
||
|
||
# 🔹 2. Поиск через ПРАВИЛЬНЫЙ фильтр для мультиполя
|
||
# Оператор '=EMAIL' ищет точное совпадение значения в мультиполе
|
||
found_contacts = bx.call('crm.contact.list', {
|
||
'filter': {'=EMAIL': search_email},
|
||
'select': ['ID', 'NAME', 'LAST_NAME', 'EMAIL'],
|
||
'limit': 5 # Берем с запасом для отладки
|
||
})
|
||
|
||
# Получаем список результатов
|
||
contacts_list = []
|
||
if isinstance(found_contacts, dict):
|
||
contacts_list = found_contacts.get('result', [])
|
||
elif isinstance(found_contacts, list):
|
||
contacts_list = found_contacts
|
||
|
||
print(f"📋 Найдено контактов через фильтр: {len(contacts_list)}")
|
||
|
||
# 🔹 3. Если фильтр не сработал — ищем вручную (Fallback)
|
||
# Это нужно, если в Битриксе много мусора или нестандартные данные
|
||
if not contacts_list:
|
||
print("⚠️ Фильтр не дал результатов. Пробуем ручной перебор...")
|
||
|
||
all_contacts = bx.get_all('crm.contact.list', {
|
||
'select': ['ID', 'NAME', 'LAST_NAME', 'EMAIL'],
|
||
'filter': {'!@EMAIL': ''} # Только где есть email
|
||
})
|
||
|
||
for contact in all_contacts:
|
||
for email_item in contact.get('EMAIL', []):
|
||
stored_email = email_item.get('VALUE', '').lower().strip()
|
||
if stored_email == search_email:
|
||
contact_id = contact['ID']
|
||
print(f"✅ Контакт найден вручную: #{contact_id}")
|
||
break
|
||
if contact_id:
|
||
break
|
||
|
||
if not contact_id:
|
||
print(f"❌ Контакт действительно не найден в базе")
|
||
|
||
else:
|
||
# Берем первый из найденных фильтром
|
||
contact_id = contacts_list[0]['ID']
|
||
print(f"✅ Контакт найден через фильтр: #{contact_id}")
|
||
|
||
# 🔹 4. Если не найден — создаем новый
|
||
if not contact_id:
|
||
print(f"⚡ Создаем новый контакт...")
|
||
|
||
add_result = bx.call('crm.contact.add', {
|
||
'fields': {
|
||
'EMAIL': [{'VALUE': search_email, 'TYPE_ID': 'WORK'}],
|
||
'NAME': order['billing'].get('first_name', ''),
|
||
'LAST_NAME': order['billing'].get('last_name', ''),
|
||
'PHONE': [{'VALUE': 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' # Или любой другой статус по умолчанию
|
||
# 🔹 5. Создаем сделку
|
||
if contact_id:
|
||
deal_result = bx.call('crm.deal.add', {
|
||
"fields": {
|
||
"TITLE": f"Order {order['id']}",
|
||
'STAGE_ID': stage_id,
|
||
'CONTACT_IDS': [contact_id],
|
||
'UF_CRM_1684256942409': f"{order['billing'].get('first_name', '')}",
|
||
'UF_CRM_1684256770733': f"{order['billing'].get('last_name', '')}",
|
||
'UF_CRM_1684256782976': f"{order['billing'].get('email', '')}",
|
||
'UF_CRM_5ECB65AB5E752': f"{order['shipping'].get('city', '')}",
|
||
'UF_CRM_5ECB65AB672CE': f"{order['shipping'].get('state', '')}",
|
||
'UF_CRM_5ECB65AB708F4': f"{order['shipping'].get('address_1', '')}",
|
||
'UF_CRM_5ECB65AB7AD15': f"{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"📦 Товары добавлены")
|
||
else:
|
||
print(f"❌ Ошибка сделки: {deal_result}")
|
||
|
||
|
||
|
||
# woo(112928) #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} | Добавить")
|
||
woo(order_id)
|
||
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) # или ваш обработчик |