commit 2eb19bc82cf7438749f8d6a36a495718dd184f1f Author: Vladyslav Shatrovskyi Date: Sat Feb 10 11:05:21 2024 +0200 created project diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..e1b701c --- /dev/null +++ b/.env.sample @@ -0,0 +1,13 @@ +LOGIN=LOGIN +EPIC_PASSWORD=EPIC_PASSWORD + +TGAPI_KEY=TGAPI_KEY +TGCHAT_ID=TGCHAT_ID + +DATABASE=DATABASE +DB_HOST=DB_HOST +USERS=USERS +PASSW=PASSW + +CRM_API_KEY=CRM_API_KEY +PAYMENT_METHOD_ID=PAYMENT_METHOD_ID diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dc53ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ diff --git a/cancel_order.py b/cancel_order.py new file mode 100644 index 0000000..c24fb8d --- /dev/null +++ b/cancel_order.py @@ -0,0 +1,148 @@ +import requests +from create_order_in_crm import BASE_URL, CRM_API_KEY +from get_order_number_from_crm_by_id import get_order_number +from get_orders import get_auth_token, LOGIN, PASSWORD +from models import EpicenterOrder, CancelReason, db + + +def get_cancel_reason_name(reason_ukr: str): + try: + # Connect to the database + db.connect() + + # Query the CancelReason table + cancel_reason = ( + CancelReason.select() + .where(CancelReason.ukr_description.contains(reason_ukr)) + .first() + ) + + # Close the database connection + db.close() + + if cancel_reason: + return cancel_reason.name + else: + return None + except Exception as e: + print(f"Error: {e}") + return None + + +def get_cancel_reason_from_crm(order_id: int): + + order_url = BASE_URL + f"order/{order_id}" + + headers = { + "accept": "application/json", + "Authorization": f"Bearer {CRM_API_KEY}", + } + + order_info_response = requests.get( + order_url, headers=headers, params={"include": "custom_fields"} + ) + + # Check if request was successful + if order_info_response.status_code == 200: + order_info = order_info_response.json() # Convert response to JSON + custom_fields = order_info.get("custom_fields", []) # Get custom_fields + + target_uuid = "OR_1002" + + # Используем list comprehension для поиска нужного словаря + matching_dict = next( + (item for item in custom_fields if item.get("uuid") == target_uuid), None + ) + + # Если словарь найден, выводим значение 'value' + if matching_dict: + methodpay = matching_dict.get("value") + return methodpay[0] + else: + print(f"Не найден словарь с uuid") + else: + print( + f"Failed to retrieve order information. Status code: {order_info_response.status_code}" + ) + + +def cancel_order(order_number: int, reason: str): + + order = EpicenterOrder.get(EpicenterOrder.order_number == order_number) + order_id = order.order_id + auth_token = get_auth_token(LOGIN, PASSWORD) + reason_to_cancel = get_cancel_reason_name(reason) + + first_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "access-control-request-headers": "authorization,content-type", + "access-control-request-method": "POST", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + first_response = requests.options( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/canceled", + headers=first_headers, + ) + + second_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + "content-type": "application/json", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + json_data = { + "reason_code": reason_to_cancel, + "comment": None, + "translationKey": f"order.{reason_to_cancel}", + "number": "", + "provider": "", + } + + second_response = requests.post( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/canceled", + headers=second_headers, + json=json_data, + ) + + +def change_status_in_db(order_number: int): + + with db.atomic(): + order = EpicenterOrder.get(EpicenterOrder.order_number == order_number) + order.order_status = "canceled" + order.save() + print(f"Status for order {order_number} changed to canceled.") + + +def cancel_order_and_change_status(order_id: int): + + order_number = get_order_number(order_id) + reason = get_cancel_reason_from_crm(order_id) + + print(order_number) + print(reason) + + cancel_order(order_number, reason=reason) + change_status_in_db(order_number) + + +# cancel_order_and_change_status(171) diff --git a/create_order_in_crm.py b/create_order_in_crm.py new file mode 100644 index 0000000..9c4ee54 --- /dev/null +++ b/create_order_in_crm.py @@ -0,0 +1,121 @@ +import requests +import os +from models import EpicenterOrder, EpicenterOrderProduct +from dotenv import load_dotenv + +load_dotenv() + +BASE_URL = "https://openapi.keycrm.app/v1/" +CRM_API_KEY = os.getenv("CRM_API_KEY") +PAYMENT_METHOD_ID = os.getenv("PAYMENT_METHOD_ID") + + +def create_order( + order_number: str, + order_creation_date: str, + full_name: str, + email: str, + phone_number: str, + city: str, + delivery_address: str, + delivery_post_number: str, + delivery_ref: str, + products: list, + amount: float, +): + create_order_url = BASE_URL + "order/" + + headers = { + "accept": "application/json", + "Authorization": f"Bearer {CRM_API_KEY}", + } + + json_data = { + "source_id": 1, + "source_uuid": order_number, + "ordered_at": order_creation_date, + "buyer": { + "full_name": full_name, + "email": email, + "phone": phone_number, + }, + "shipping": { + "delivery_service_id": 1, + "shipping_service": "Нова Пошта", + "shipping_address_city": city, + "shipping_address_country": "Ukraine", + "shipping_secondary_line": delivery_address, + "shipping_receive_point": delivery_post_number, + "recipient_full_name": full_name, + "recipient_phone": phone_number, + "warehouse_ref": delivery_ref, + }, + "products": products, + "payments": [ + { + "status": "not_paid", + "payment_method_id": PAYMENT_METHOD_ID, + "amount": amount, + }, + ], + } + + response = requests.post(create_order_url, headers=headers, json=json_data) + print(response.text) + + +def create_from_db(order_number: int): + + orders_query = EpicenterOrder.select().where( + EpicenterOrder.order_number == order_number + ) + + # print(orders_query) + + for order_info in orders_query: + # extract order information + order_number = order_info.order_number + order_creation_date = order_info.order_creation_date.strftime( + "%Y-%m-%d %H:%M:%S" + ) + full_name = order_info.full_name + email = order_info.email + phone_number = order_info.phone_number + delivery_address = order_info.delivery_address + delivery_post_number = order_info.delivery_post_number + delivery_ref = order_info.delivery_ref + settlement = order_info.settlement + amount = float(order_info.amount) + + # print(amount) + + # extract products associated with the order + products = [] + for order_product in EpicenterOrderProduct.select().where( + EpicenterOrderProduct.epicenter_order == order_info + ): + product_info = { + "name": order_product.product.name, + "sku": order_product.product.sku, + "price": float(order_product.product.price), + "quantity": order_product.quantity, + } + products.append(product_info) + print(product_info) + + create_order( + order_number=order_number, + order_creation_date=order_creation_date, + full_name=full_name, + email=email, + phone_number=phone_number, + delivery_post_number=delivery_post_number, + delivery_ref=delivery_ref, + delivery_address=delivery_address, + city=settlement, + products=products, + amount=amount, + ) + + +# create_from_db(32078457) diff --git a/get_order_number_from_crm_by_id.py b/get_order_number_from_crm_by_id.py new file mode 100644 index 0000000..080e691 --- /dev/null +++ b/get_order_number_from_crm_by_id.py @@ -0,0 +1,20 @@ +import requests + +from create_order_in_crm import BASE_URL, CRM_API_KEY + + +def get_order_number(order_id: int): + order_url = BASE_URL + f"order/{order_id}" + + headers = { + "accept": "application/json", + "Authorization": f"Bearer {CRM_API_KEY}", + } + + order_info_response = requests.get( + order_url, headers=headers, params={"include": "custom_fields"} + ) + + order_info = order_info_response.json() + order_number = order_info.get("source_uuid") + return order_number diff --git a/get_orders.py b/get_orders.py new file mode 100644 index 0000000..fe753f9 --- /dev/null +++ b/get_orders.py @@ -0,0 +1,370 @@ +import time +import requests +import json +import os + +from dotenv import load_dotenv +from datetime import datetime +from telegram_bot import order_epicenter_error +from models import db, EpicenterOrder, Product, EpicenterOrderProduct + + +load_dotenv() + +LOGIN = os.getenv("LOGIN") +PASSWORD = os.getenv("EPIC_PASSWORD") + + +def get_auth_token(login: str, password: str) -> str: + + headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "application/json", + "accept-language": "ru", + "content-type": "application/json", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Linux"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + } + + json_data = { + "login": login, + "password": password, + } + + response = requests.post( + "https://core-api.epicentrm.cloud/v1/users/login", + headers=headers, + json=json_data, + ) + + auth_token = response.json()["token"]["auth"] + + return auth_token + + +def is_new_order(order_id: str, auth_token: str) -> bool: + headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "application/json", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + response = requests.get( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}", + headers=headers, + ) + + data = json.loads(response.text) + + if ( + data["statusCode"] == "new" + and data["statusCode"] != "canceled" + and data["statusCode"] != "sent" + ) and data["statusCode"] != "confirmed": + return True + return False + + +def activate_order(order_id: str, auth_token: str): + + first_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "access-control-request-headers": "authorization,content-type", + "access-control-request-method": "POST", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + requests.options( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/confirmed_by_merchant", + headers=first_headers, + ) + + second_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + # 'content-length': '0', + "content-type": "application/json", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + requests.post( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}" + f"/change-status/to/confirmed_by_merchant", + headers=second_headers, + ) + + +def get_delivery_info( + shipment_settlementId: str, shipment_officeId: str, auth_token: str +): + + headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "application/json", + "accept-language": "ru", + "authorization": f"Bearer " f"{auth_token}", + "if-none-match": 'W/"196-wqlAXM70iFf1sVnvW/hGnongIL0"', + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + response = requests.get( + f"https://core-api.epicentrm.cloud/v2/deliveries/nova_poshta/settlements/{shipment_settlementId}" + f"/offices/{shipment_officeId}", + headers=headers, + ) + + shipment_data = json.loads(response.text) + + return shipment_data["title"] + + +def get_first_page_order_ids(auth_token: str): + headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "application/json", + "accept-language": "ru", + "authorization": f"Bearer {auth_token}", + "if-none-match": 'W/"6e25-Lp/88DlhwX6wEIzxPDq/6WiYQf8"', + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Linux"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + } + + params = { + "offset": "0", + "limit": "10", + } + + response = requests.get( + "https://core-api.epicentrm.cloud/v2/oms/orders", params=params, headers=headers + ) + + data = json.loads(response.text) + + order_ids = [order["id"] for order in data["items"]] + + return order_ids + + +def get_order_info(order_id: str, auth_token: str): + headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "application/json", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + "if-none-match": 'W/"4ced-XZFKegDeJ8gOKv2D+e5bORHnOoM"', + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + response = requests.get( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}", + headers=headers, + ) + + order = json.loads(response.text) + products = [] + + if is_new_order(order_id, auth_token): + activate_order(order_id, auth_token) + time.sleep(10) + return get_order_info(order_id, auth_token) + else: + # get product info + for item in range(len(order["items"])): + title = order["items"][item]["title"] + sku = order["items"][item]["sku"] + product_price = order["items"][item]["price"] + quantity = order["items"][item]["quantity"] + products.append( + { + "name": title, + "sku": sku, + "price": product_price, + "quantity": quantity, + } + ) + + # User info + first_name = order["address"]["firstName"] + last_name = order["address"]["lastName"] + patronymic = order["address"]["patronymic"] + email = order["address"]["email"] + phone_number = order["address"]["phone"] + + # Delivery info + delivery_info = get_delivery_info( + order["address"]["shipment"]["settlementId"], + order["address"]["shipment"]["officeId"], + auth_token, + ) + delivery_ref = order["address"]["shipment"]["officeId"] + settlement = order["settlement"]["title"] + + # Order info + order_number = order["number"] + order_status = order["statusCode"] + created_at = order["createdAt"] + subtotal = order["subtotal"] + + order_data = { + "first_name": first_name, + "last_name": last_name, + "patronymic": patronymic, + "email": email, + "phone_number": phone_number, + "delivery_info": delivery_info, + "delivery_ref": delivery_ref, + "settlement": settlement, + "order_number": order_number, + "order_id": order_id, + "products": products, + "order_status": order_status, + "created_at": created_at, + "subtotal": subtotal, + } + + return order_data + + +def insert_order_info(order_info): + with db.atomic(): + # Insert EpicenterOrder + epicenter_model = EpicenterOrder.create( + order_number=order_info["order_number"], + order_id=order_info["order_id"], + order_status=order_info["order_status"], + order_creation_date=datetime.strptime( + order_info["created_at"], "%Y-%m-%dT%H:%M:%S%z" + ).strftime("%Y-%m-%d %H:%M:%S"), + full_name=( + f"{order_info['first_name']} {order_info['last_name']} " + f"{order_info['patronymic'] if order_info['patronymic'] else ''}" + ), + email=order_info["email"], + phone_number=order_info["phone_number"], + delivery_address=", ".join(order_info["delivery_info"].split(",")[1:]), + delivery_post_number=f"Склад {order_info['delivery_info'].split(',')[0]}".replace( + "№", "#" + ).rstrip(), + delivery_ref=order_info["delivery_ref"], + settlement=order_info["settlement"], + amount=order_info["subtotal"], + ) + + # Insert or update products and associate them with the order + product_records = [] + for product_data in order_info["products"]: + sku = product_data["sku"] + existing_product = Product.select().where(Product.sku == sku).first() + if existing_product: + # Update existing product + existing_product.price = product_data["price"] + existing_product.save() + else: + # Create new product + existing_product = Product.create( + name=product_data["name"], sku=sku, price=product_data["price"] + ) + product_records.append(existing_product) + # Associate product with the order and specify quantity + EpicenterOrderProduct.create( + epicenter_order=epicenter_model, + product=existing_product, + quantity=product_data["quantity"], + ) + + +def write_orders(auth_token: str): + + # get order_ids from first 10 orders + order_ids = get_first_page_order_ids(auth_token) + + for order_id in order_ids: + print(order_id) + + existing_order = ( + EpicenterOrder.select().where(EpicenterOrder.order_id == order_id).first() + ) + + if existing_order: + continue + + order_info = get_order_info(order_id=order_id, auth_token=auth_token) + insert_order_info(order_info=order_info) + + db.close() + # request.get()url Alex send order_number + + print("DONE") + + +def run_all(): + try: + while True: + auth_token = get_auth_token(LOGIN, PASSWORD) + write_orders(auth_token=auth_token) + time.sleep(1800) + except Exception as e: + # print(e.with_traceback()) + exception_text = f"🆘An error in epicenter orders script. Please change something!🆘\n Exception: {e}" + order_epicenter_error(exception_text) + + +if __name__ == "__main__": + run_all() diff --git a/get_ttn_number.py b/get_ttn_number.py new file mode 100644 index 0000000..a56b23f --- /dev/null +++ b/get_ttn_number.py @@ -0,0 +1,169 @@ +import requests + +from get_order_number_from_crm_by_id import get_order_number +from create_order_in_crm import BASE_URL, CRM_API_KEY +from get_orders import get_auth_token, LOGIN, PASSWORD +from models import EpicenterOrder, db + + +def get_ttn_number_from_order(order_id: int): + order_number = get_order_number(order_id) + + order = ( + EpicenterOrder.select() + .where(EpicenterOrder.order_number == order_number) + .first() + ) + + if not order: + print(f"Order with order number {order_number} not found.") + + order_url = BASE_URL + f"order/{order_id}/" + + print(order_id, order_url) + + headers = { + "accept": "application/json", + "Authorization": f"Bearer {CRM_API_KEY}", + } + + params = { + "include": "shipping", + } + + try: + response = requests.get(order_url, headers=headers, params=params) + + data = response.json() + tracking_code = data["shipping"].get("tracking_code") + return tracking_code + + except Exception as e: + print(f"An unexpected error occurred: {e}") + + + +def change_status_to_confirmed(order_number: int, auth_token: str): + order = ( + EpicenterOrder.select() + .where(EpicenterOrder.order_number == order_number) + .first() + ) + order_id = order.order_id + order.status = "confirmed" + + first_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "access-control-request-headers": "authorization,content-type", + "access-control-request-method": "POST", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + first_response = requests.options( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/confirmed", + headers=first_headers, + ) + + second_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + # 'content-length': '0', + "content-type": "application/json", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + second_response = requests.post( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/confirmed", + headers=second_headers, + ) + + +def update_status_to_sent(order_number: int, order_id_crm: int): + + auth_token = get_auth_token(LOGIN, PASSWORD) + + with db.transaction(): + change_status_to_confirmed(order_number, auth_token) + order = ( + EpicenterOrder.select() + .where(EpicenterOrder.order_number == order_number) + .first() + ) + order_id = order.order_id + order.order_status = "sent" + + ttn_number = get_ttn_number_from_order(order_id_crm) + order.ttn = ttn_number + order.save() + + first_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "access-control-request-headers": "authorization,content-type", + "access-control-request-method": "POST", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + first_response = requests.options( + "https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/sent", + headers=first_headers, + ) + + second_headers = { + "authority": "core-api.epicentrm.cloud", + "accept": "*/*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8,ru-RU;q=0.7", + "authorization": f"Bearer {auth_token}", + "content-type": "application/json", + "origin": "https://admin.epicentrm.com.ua", + "referer": "https://admin.epicentrm.com.ua/", + "sec-ch-ua": '"Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", + } + + json_data = { + "reason_code": "", + "comment": None, + "translationKey": "", + "number": ttn_number, + "provider": "nova_poshta", + } + + second_response = requests.post( + f"https://core-api.epicentrm.cloud/v2/oms/orders/{order_id}/change-status/to/sent", + headers=second_headers, + json=json_data, + ) + + +def get_and_write_ttn_number(order_id_crm: int): + order_number = get_order_number(order_id=order_id_crm) + update_status_to_sent(order_number, order_id_crm) diff --git a/models.py b/models.py new file mode 100644 index 0000000..ecb9619 --- /dev/null +++ b/models.py @@ -0,0 +1,117 @@ +import os +from dotenv import load_dotenv +from peewee import * + + +load_dotenv() + +DATABASE = os.getenv("DATABASE") +DB_HOST = os.getenv("DB_HOST") +USERS = os.getenv("USERS") +PASSW = os.getenv("PASSW") + + +db = MySQLDatabase( + database=DATABASE, user=USERS, password=PASSW, host=DB_HOST, port=3306 +) + + +class Product(Model): + product_id = AutoField(primary_key=True) + name = CharField() + sku = CharField(unique=True) + price = DecimalField() + + class Meta: + database = db + + +class EpicenterOrder(Model): + order_number = CharField(unique=True) + order_id = CharField(unique=True) + products = ManyToManyField(Product, backref="epicenter_orders") + order_status = CharField() + order_creation_date = DateTimeField() + full_name = CharField() + email = CharField() + phone_number = CharField() + delivery_address = CharField() + delivery_post_number = CharField() + delivery_ref = CharField() + settlement = CharField() + amount = DecimalField() + ttn = CharField(null=True) + + class Meta: + database = db + + +class EpicenterOrderProduct(Model): + epicenter_order = ForeignKeyField(EpicenterOrder) + product = ForeignKeyField(Product) + quantity = IntegerField() + + class Meta: + database = db + + +class CancelReason(Model): + name = CharField() + ukr_description = CharField() + + class Meta: + database = db + + +# db.create_tables([EpicenterOrder, Product, EpicenterOrderProduct, CancelReason]) +# db.close() + + +# run when you lost cancelreason table +def insert_cancel_reasons(reasons_dict): + try: + # Connect to the database + db.connect() + + # Create the table if it doesn't exist + db.create_tables([CancelReason]) + + # Insert data into the table + with db.atomic(): + for key, value in reasons_dict.items(): + CancelReason.create(name=key, ukr_description=value) + + # Close the database connection + db.close() + print("Data insertion successful.") + + except Exception as e: + print(f"Error: {e}") + + +# customer_cancel_reasons = { +# "customer_account_payment_with_vat_is_preferred": "Хотів сплатити на Р/Р (з ПДВ)", +# "customer_account_payment_without_vat_is_preferred": "Хотів сплатити на Р/Р (без ПДВ)", +# "customer_bought_elsewhere_as_a_gift": "Купив в іншому місці/подарували", +# "customer_canceled_at_delivery_office": "Відмова у відділенні", +# "customer_changed_mind": "Потрібна була консультація", +# "customer_credit_card_payment_is_preferred": "Хотів сплатити карткою", +# "customer_delivery_speed_too_slow": "Не влаштували терміни доставки", +# "customer_dissatisfied_with_the_shipping_cost": "Не влаштувала ціна доставки", +# "customer_epic_warranty_is_preferred": "Вже купив в Епіцентрі", +# "customer_found_cheaper": "Не влаштувала ціна товару", +# "customer_get_loan_is_preferred_payment_type": "Хотів оформити розтермінування/кредит", +# "customer_long_merchant_confirmation": "Тривале підтвердження", +# "customer_no_payment": "Не вдалося оплатити на сайті", +# "customer_not_timely_confirmation_of_the_availability_of_goods": "Немає в наявності", +# "customer_order_duplicate": "Помилково оформив кілька замовлень", +# "customer_prepayment_required": "Потрібна передоплата", +# "customer_price_obsoleted": "Ціна неактуальна", +# "customer_product_characteristics_are_not_suitable": "Товар не підходить за характеристиками", +# "customer_requires_advance_payment_which_is_not_indicated_on_the_website": "Вимагають передоплату, про яку не інформує сайт", +# "customer_several_packages_delivery_payment_is_not_suitable": "Хотів доставку одним замовленням", +# "customer_shop_pickup_preferred_delivery_price_too_high": "Хотів забрати з пунктів самовивозу", +# "customer_test": "Тест", +# } + +# insert_cancel_reasons(customer_cancel_reasons) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7bf0daa --- /dev/null +++ b/requirements.txt @@ -0,0 +1,18 @@ +black==24.1.1 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 +colorama==0.4.6 +idna==3.6 +mypy-extensions==1.0.0 +mysql-connector-python==8.3.0 +packaging==23.2 +pathspec==0.12.1 +peewee==3.17.0 +platformdirs==4.2.0 +PyMySQL==1.1.0 +python-dotenv==1.0.1 +requests==2.31.0 +tomli==2.0.1 +typing_extensions==4.9.0 +urllib3==2.2.0 diff --git a/telegram_bot.py b/telegram_bot.py new file mode 100644 index 0000000..c917c5c --- /dev/null +++ b/telegram_bot.py @@ -0,0 +1,16 @@ +import os + +import requests +from dotenv import load_dotenv + +load_dotenv() + +api_key = os.getenv("TGAPI_KEY") +chat = os.getenv("TGCHAT_ID") + + +def order_epicenter_error(text): + answer = requests.get( + "https://api.telegram.org/bot{}/sendMessage".format(api_key), + params=dict(chat_id=chat, text=str(text), parse_mode="HTML"), + )