Tomati/files2.py

266 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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) # или ваш обработчик