From 71103f224df9603d46792dc6b9757f8082cf5546 Mon Sep 17 00:00:00 2001 From: Elvis Date: Fri, 1 Sep 2023 08:29:48 -0500 Subject: [PATCH] changes from channel to web_shop --- __init__.py | 27 ++-- api_log.py | 27 ++-- endpoints_rappi.py | 48 ++++--- rappi.py | 234 ++++++++------------------------- shop.py | 8 ++ shop.xml | 12 ++ tryton.cfg | 5 +- view/sale_web_channel_form.xml | 15 +-- view/shop_form.xml | 12 ++ view/web_shop_form.xml | 31 +++++ web.py | 131 ++++++++++++++++++ web.xml | 12 ++ web_channel.py | 9 +- 13 files changed, 337 insertions(+), 234 deletions(-) create mode 100644 shop.py create mode 100644 shop.xml create mode 100644 view/shop_form.xml create mode 100644 view/web_shop_form.xml create mode 100644 web.py create mode 100644 web.xml diff --git a/__init__.py b/__init__.py index 8e247c7..ee340b0 100644 --- a/__init__.py +++ b/__init__.py @@ -10,6 +10,8 @@ from . import rappi from . import api_log from . import ir from . import routes +from . import shop +from . import web __all__ = ['register', 'routes'] @@ -17,26 +19,27 @@ __all__ = ['register', 'routes'] def register(): Pool.register( + web.Shop, web_channel.SaleWebChannel, - mercado_libre.MercadoLibre, - shopify.Shopify, - rappi.Rappi, + # mercado_libre.MercadoLibre, + # shopify.Shopify, + # rappi.Rappi, sale.Sale, - sale.SaleForChannelStart, - web_channel.SynchronizeChannelOrdersStart, - web_channel.SynchronizeChannelOrdersDone, - web_channel.FinishInvoicesStart, + # sale.SaleForChannelStart, + # web_channel.SynchronizeChannelOrdersStart, + # web_channel.SynchronizeChannelOrdersDone, + # web_channel.FinishInvoicesStart, party.Party, api_log.ApiLog, ir.Cron, module='sale_web_channel', type_='model') Pool.register( sale.SaleUploadInvoice, - sale.SaleForChannel, - web_channel.SynchronizeChannelOrders, - web_channel.FinishInvoices, - web_channel.SynchronizeMenuWizard, + # sale.SaleForChannel, + # web_channel.SynchronizeChannelOrders, + # web_channel.FinishInvoices, + # web_channel.SynchronizeMenuWizard, module='sale_web_channel', type_='wizard') Pool.register( - sale.SaleForChannelReport, + # sale.SaleForChannelReport, module='sale_web_channel', type_='report') diff --git a/api_log.py b/api_log.py index b637f94..89fae30 100644 --- a/api_log.py +++ b/api_log.py @@ -10,7 +10,8 @@ STATES = {'readonly': True} class ApiLog(metaclass=PoolMeta): "API Log" __name__ = "api.log" - channel = fields.Many2One('sale.web_channel', 'channel', states=STATES) + web_shop = fields.Many2One('web.shop', 'Web Shop', states=STATES) + # channel = fields.Many2One('sale.web_channel', 'channel', states=STATES) # number = fields.Char('Number Doc', states=STATES) # order = fields.Char('Order', states=STATES) # record_date = fields.Date('Record Date', states=STATES) @@ -25,15 +26,15 @@ class ApiLog(metaclass=PoolMeta): super(ApiLog, cls).__setup__() cls._order.insert(0, ('record_date', 'DESC')) - @classmethod - def process_log_cron(cls): - logs = cls.search([ - ('status', '=', 'pending') - ], limit=10) - for log in logs: - data = log.file_json.decode("utf-8") - res = {} - if log.channel.channel_name == 'mercadolibre': - MercadoLibre = Pool().get('sale.web_channel.mercado_libre') - res = MercadoLibre.request_api(json.loads(data)) - cls.write([log], res) + # @classmethod + # def process_log_cron(cls): + # logs = cls.search([ + # ('status', '=', 'pending') + # ], limit=10) + # for log in logs: + # data = log.file_json.decode("utf-8") + # res = {} + # if log.channel.channel_name == 'mercadolibre': + # MercadoLibre = Pool().get('sale.web_channel.mercado_libre') + # res = MercadoLibre.request_api(json.loads(data)) + # cls.write([log], res) diff --git a/endpoints_rappi.py b/endpoints_rappi.py index 32d9e5a..c5d2d4d 100644 --- a/endpoints_rappi.py +++ b/endpoints_rappi.py @@ -2,10 +2,13 @@ import requests import json from datetime import datetime from collections import namedtuple +from rich import print +from . import endpoints_rappi URL_AUTH_DEV = 'https://rests-integrations-dev.auth0.com/oauth/token' URL_AUTH_PRODUCTION = 'https://rests-integrations.auth0.com/oauth/token' URL_ENV = 'https://microservices.dev.rappi.com/api/v2/restaurants-integrations-public-api' +AUDIENCE = 'https://int-public-api-v2/api' HEADERS = { 'Accept': 'application/json', @@ -29,11 +32,23 @@ channel = ChannelRappi( class RappiAPI: def __init__(self, channel): - self.api_key = channel.api_key + # self.api_key = channel.api_key + # self.url_base = 'https://microservices.dev.rappi.com/api/v2/restaurants-integrations-public-api' + # self.url_auth = URL_AUTH_DEV + # self.session = requests.Session() + # self.session.headers.update({'x-authorization': self.api_key}) + # self.session.headers.update({'Content-Type': 'application/json'}) + # self.store_name = channel.shop.name + self.seller_id = channel.seller_id + self.app_id = channel.app_id + self.store_integration_id = channel.app_id self.url_base = 'https://microservices.dev.rappi.com/api/v2/restaurants-integrations-public-api' self.url_auth = URL_AUTH_DEV + self.token = channel.access_token if channel.access_token else '' self.session = requests.Session() - self.session.headers.update({'x-authorization': self.api_key}) + self.session.headers.update({ + 'x-authorization': 'bearer ' + self.token + }) self.session.headers.update({'Content-Type': 'application/json'}) def _send_request(self, endpoint=None, method='GET', data: dict = None, auth: bool = False): @@ -45,6 +60,7 @@ class RappiAPI: if method == 'GET': response = self.session.get(url, timeout=10) elif method == 'POST': + print(data) response = self.session.post(url, data=json.dumps(data), timeout=10) elif method == 'PUT': response = self.session.put(url, data=json.dumps(data), timeout=10) @@ -56,8 +72,10 @@ class RappiAPI: content_type = response.headers.get('Content-Type', '') # if response.status_code == 200: if content_type.startswith('application/json'): + print(response.json()) return response.json() else: + print(response.text) return response.text # else: # if content_type.startswith('application/json'): @@ -67,11 +85,11 @@ class RappiAPI: except requests.exceptions.RequestException as e: raise RuntimeError(f'Request error: {str(e)}') - def get_token_access(self, client_id: str, client_secret: str, audience: str): + def get_token_access(self, client_id: str, client_secret: str): data = { "client_id": client_id, "client_secret": client_secret, - "audience": audience, + "audience": AUDIENCE, "grant_type": "client_credentials" } return self._send_request(method='POST', data=data, auth=True) @@ -137,13 +155,13 @@ class RappiAPI: endpoint = f'/menu/approved/{store_id}' return self._send_request(endpoint=endpoint) - def create_menu_store(self, store_id, items): + def create_menu_store(self, items): """ NOTE: If menu don't created return response message dict {message: 'reason'} """ endpoint = '/menu' data = { - 'storeId': store_id, + 'storeId': self.store_integration_id, 'items': items, } return self._send_request(endpoint=endpoint, method='POST', data=data) @@ -220,12 +238,12 @@ class RappiAPI: # Ejemplo de uso -print(channel.seller_id, 'this is objetc') -token = { - 'access_token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Jlc3RzLWludGVncmF0aW9ucy1kZXYuYXV0aDAuY29tLyIsInN1YiI6ImlKQjlVdnhDaE5nam12WEdnUENiSXpVWWFZekNmN1BKQGNsaWVudHMiLCJhdWQiOiJodHRwczovL2ludC1wdWJsaWMtYXBpLXYyL2FwaSIsImlhdCI6MTY5MzAxNzExNCwiZXhwIjoxNjkzNjIxOTEyLCJhenAiOiJpSkI5VXZ4Q2hOZ2ptdlhHZ1BDYkl6VVlhWXpDZjdQSiIsInNjb3BlIjoidXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXMgdXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXNfaXRlbXMgdXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXNfaXRlbXNfcmFwcGkgcmVhZDptZW51X3RoaXJkcGFydHkgdXBkYXRlOm1lbnUgcmVhZDptZW51X3JhcHBpIHVwZGF0ZTpvcmRlcnNfcmVhZHlfZm9yX3BpY2t1cCByZWFkOm1lbnVzX3JhcHBpIHJlYWQ6Y29uZmlnIGNyZWF0ZTpjb25maWcgdXBkYXRlOmNvbmZpZyBkZWxldGU6Y29uZmlnIHJlYWQ6b3JkZXJzIHBpY2t1cDpvcmRlciByZWplY3Q6b3JkZXIgdGFrZTpvcmRlciByZWFkOnN0b3JlcyByZWFkOmFwcF9jbGllbnRzIGNyZWF0ZTphcHBfY2xpZW50cyBkZWxldGU6YXBwX2NsaWVudHMgY3JlYXRlOmFwcF9jbGllbnRzX3N0b3JlcyByZWFkOm9yZGVyX2V2ZW50cyBkZWxldGU6YXBwX2NsaWVudHNfc3RvcmVzIGNyZWF0ZTpjbGllbnRzIHJlYWQ6bWVudV9zdGF0dXNfcmFwcGkgcmVhZDphdmFpbGFiaWxpdHlfc3RvcmVzX2l0ZW1zIHJlYWQ6d2ViaG9vayBjcmVhdGU6d2ViaG9vayB1cGRhdGU6d2ViaG9vayBkZWxldGU6d2ViaG9vayB1cGRhdGU6d2ViaG9va19zZWNyZXQiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.Loh_g9qB3jNhOHxXPVrWgDJQAr7n9DzB6jWbPD_vUDQ', - 'scope': 'update:availability_stores update:availability_stores_items update:availability_stores_items_rappi read:menu_thirdparty update:menu read:menu_rappi update:orders_ready_for_pickup read:menus_rappi read:config create:config update:config delete:config read:orders pickup:order reject:order take:order read:stores read:app_clients create:app_clients delete:app_clients create:app_clients_stores read:order_events delete:app_clients_stores create:clients read:menu_status_rappi read:availability_stores_items read:webhook create:webhook update:webhook delete:webhook update:webhook_secret', 'expires_in': 604798, 'token_type': 'Bearer'} -channel = channel._replace(api_key=token['access_token'], creation_time=datetime.now()) -rappi = RappiAPI(f'Bearer {channel.api_key}', URL_ENV, URL_AUTH_DEV) +# print(channel.seller_id, 'this is objetc') +# token = { +# 'access_token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Jlc3RzLWludGVncmF0aW9ucy1kZXYuYXV0aDAuY29tLyIsInN1YiI6ImlKQjlVdnhDaE5nam12WEdnUENiSXpVWWFZekNmN1BKQGNsaWVudHMiLCJhdWQiOiJodHRwczovL2ludC1wdWJsaWMtYXBpLXYyL2FwaSIsImlhdCI6MTY5MzAxNzExNCwiZXhwIjoxNjkzNjIxOTEyLCJhenAiOiJpSkI5VXZ4Q2hOZ2ptdlhHZ1BDYkl6VVlhWXpDZjdQSiIsInNjb3BlIjoidXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXMgdXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXNfaXRlbXMgdXBkYXRlOmF2YWlsYWJpbGl0eV9zdG9yZXNfaXRlbXNfcmFwcGkgcmVhZDptZW51X3RoaXJkcGFydHkgdXBkYXRlOm1lbnUgcmVhZDptZW51X3JhcHBpIHVwZGF0ZTpvcmRlcnNfcmVhZHlfZm9yX3BpY2t1cCByZWFkOm1lbnVzX3JhcHBpIHJlYWQ6Y29uZmlnIGNyZWF0ZTpjb25maWcgdXBkYXRlOmNvbmZpZyBkZWxldGU6Y29uZmlnIHJlYWQ6b3JkZXJzIHBpY2t1cDpvcmRlciByZWplY3Q6b3JkZXIgdGFrZTpvcmRlciByZWFkOnN0b3JlcyByZWFkOmFwcF9jbGllbnRzIGNyZWF0ZTphcHBfY2xpZW50cyBkZWxldGU6YXBwX2NsaWVudHMgY3JlYXRlOmFwcF9jbGllbnRzX3N0b3JlcyByZWFkOm9yZGVyX2V2ZW50cyBkZWxldGU6YXBwX2NsaWVudHNfc3RvcmVzIGNyZWF0ZTpjbGllbnRzIHJlYWQ6bWVudV9zdGF0dXNfcmFwcGkgcmVhZDphdmFpbGFiaWxpdHlfc3RvcmVzX2l0ZW1zIHJlYWQ6d2ViaG9vayBjcmVhdGU6d2ViaG9vayB1cGRhdGU6d2ViaG9vayBkZWxldGU6d2ViaG9vayB1cGRhdGU6d2ViaG9va19zZWNyZXQiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.Loh_g9qB3jNhOHxXPVrWgDJQAr7n9DzB6jWbPD_vUDQ', +# 'scope': 'update:availability_stores update:availability_stores_items update:availability_stores_items_rappi read:menu_thirdparty update:menu read:menu_rappi update:orders_ready_for_pickup read:menus_rappi read:config create:config update:config delete:config read:orders pickup:order reject:order take:order read:stores read:app_clients create:app_clients delete:app_clients create:app_clients_stores read:order_events delete:app_clients_stores create:clients read:menu_status_rappi read:availability_stores_items read:webhook create:webhook update:webhook delete:webhook update:webhook_secret', 'expires_in': 604798, 'token_type': 'Bearer'} +# channel = channel._replace(api_key=token['access_token'], creation_time=datetime.now()) +# rappi = RappiAPI(f'Bearer {channel.api_key}', URL_ENV, URL_AUTH_DEV) # get token # token = rappi.get_token_access(channel.seller_id, channel.secret_key, 'https://int-public-api-v2/api') @@ -245,9 +263,9 @@ rappi = RappiAPI(f'Bearer {channel.api_key}', URL_ENV, URL_AUTH_DEV) # create menu # NOTE: If menu don't created return response message json {message: 'reason'} -items = [] -menu_created = rappi.create_menu_store(store_id=channel.app_id, items=items) -print(menu_created, 'menu created') +# items = [] +# menu_created = rappi.create_menu_store(store_id=channel.app_id, items=items) +# print(menu_created, 'menu created') # Order options to call enpoints to manage order client # get_orders, take_order or reject_order, ready_order diff --git a/rappi.py b/rappi.py index 98463cc..f84e0a0 100644 --- a/rappi.py +++ b/rappi.py @@ -9,14 +9,10 @@ from trytond.transaction import Transaction from urllib.parse import urlencode from datetime import datetime, date from trytond.pyson import Eval -from .web_channel import SaleWebChannel -from urllib.parse import urlencode +from .web import Shop import json import requests -import base64 -import hmac -import hashlib -from pprint import pprint +from . import endpoints_rappi URL_DEV = 'https://rests-integrations-dev.auth0.com/oauth/token' @@ -28,18 +24,18 @@ HEADERS = { } -class Rappi(SaleWebChannel): +class Rappi(Shop): 'Rappi' - __name__ = 'sale.web_channel.rappi' + __name__ = 'web.shop.rappi' @classmethod def __setup__(cls): super(Rappi, cls).__setup__() - cls._buttons.update({ - 'generate_token_access': { - 'invisible': Eval('state') != 'active', - }, - }) + # cls._buttons.update({ + # 'generate_token_access': { + # 'invisible': Eval('state') != 'active', + # }, + # }) def _get_context(self): print(self) @@ -52,7 +48,6 @@ class Rappi(SaleWebChannel): } def generate_token_access(self): - print('holis') token_request = { "client_id": self.app_id, "client_secret": self.secret_key, @@ -68,172 +63,53 @@ class Rappi(SaleWebChannel): self.save() def synchronize_menu_rappi(self): - menu = { - # 'storeId': self.store_id, - 'storeId': '900152684', - 'items': [] - } - menu_append = menu['items'].append - pool = Pool() - Category = pool.get('product.template-product.category') - # price_list = self.shop.price_list.lines - # for index, line in enumerate(price_list): - # if line.category and not line.product: - # self.create_menu_by_global_category(line, Category) - # elif not line.category and line.product: - # # elif line.category and line.product: - # # pass - # # else: - # # raise UserWarning('BAD CONFIGURATION IN LINE ID ', line.id) + items = [] + for category in self.admin_category.childs: + items.extend(self.get_products(category)) + print(items) + api_rappi = endpoints_rappi.RappiAPI(self) + api_rappi.create_menu_store(items) - # # menu_append({ - # # 'category': { - # # 'id': line.product.categories[0].id, - # # 'maxQty': 0, - # # 'minQty': 0, - # # 'name': line.product.categories[0].name, - # # }, - # # 'children': [], - # # 'name': line.product.categories[0].name, - # # 'description': line.product.description if line.product.description else 'no esta disponible', - # # 'imageUrl': line.product.images[0].image_url if line.product.images else '', - # # 'price': float(line.price_w_tax_computed), - # # 'sku': line.product.code, - # # 'sortingPosition': 0, - # # "type": "PRODUCT", - # # # 'maxLimit': '', - # # }) - # # if hasattr(line.product, 'products_mix') and line.product.products_mix: - # # for mix in line.product.products_mix: - # # menu['items'][0]['children'].append({ - # # "category": { - # # "id": mix.categories[0].id, - # # "maxQty": 1, - # # "minQty": 0, - # # "name": mix.categories[0].name, - # # "sortingPosition": 0 - # # }, - # # "name": mix.name, - # # "description": mix.description, - # # "price": float(mix.list_price), - # # "sku": mix.id, - # # "maxLimit": 1, - # # "sortingPosition": 1, - # # "type": "PRODUCT" - # # }) - - menu = { - "storeId": "900152684", - "items": [ - { - "name": "Grilled Chicken Burger", - "description": "Grilled chicken burger description", - "price": 14000, - "sku": "10", - "sortingPosition": 0, - "type": "PRODUCT", + def get_products(self, category): + children_list = [] + for child in category.childs: + children_list.append({ "category": { - "id": "2090019638", - "maxQty": 0, - "minQty": 0, - "name": "Burgers", - "sortingPosition": 0 + "id": category.id, + "maxQty": 0, + "minQty": 0, + "name": category.name, + "sortingPosition": 0, + "children": [] }, - "children": [ - { - "category": { - "id": "211", - "maxQty": 1, - "minQty": 0, - "name": "Do you want to add?", - "sortingPosition": 0 - }, - "name": "French Fries", - "description": "crunchy french fries", - "price": 5000, - "sku": "1", - "maxLimit": 1, - "sortingPosition": 1, - "type": "TOPPING" - }, - { - "category": { - "id": "211", - "maxQty": 1, - "minQty": 0, - "name": "Do you want to add?", - "sortingPosition": 0 - }, - "name": "Potato Wedges", - "price": 7000, - "sku": "2", - "maxLimit": 1, - "sortingPosition": 1, - "type": "TOPPING" - } - ] - }, - { - "name": "Hawaiian Pizza", - "description": "hawaiian pizza description", - "price": 18000, - "sku": "11", - "sortingPosition": 1, - "type": "PRODUCT", - "category": { - "id": "2090019639", - "maxQty": 0, - "minQty": 0, - "name": "Pizzas", - "sortingPosition": 1 - }, - "children": [] - } - ] - } - menu_json = json.dumps(menu) - URL = 'https://microservices.dev.rappi.com/api/v2/restaurants-integrations-public-api/menu' - token = self.access_token - headers = { - 'Content-Type': 'application/json', - 'x-authorization': 'bearer ' + token, - } - pprint(menu_json) - response = requests.request("POST", URL, headers=headers, data=menu_json) - pprint(response.text.encode('utf8')) - - def create_menu_by_global_category(self, line, Category): - pass - # category = Category.search_read([('categories', 'in', '')]) - # dict_menu = { - # 'category': { - # 'id': line.product.category.id, - # 'maxQty': 0, - # 'minQty': 0, - # 'name': line.product.category.name, - # }, - - # dict_menu['children'].append(dict_child) - # 'children': [], - # 'name': line.product.categories[0].name, - # 'description': line.product.description if line.product.description else 'no esta disponible', - # 'imageUrl': line.product.images[0].image_url if line.product.images else '', - # 'price': float(line.price_w_tax_computed), - # 'sku': line.product.code, - # 'sortingPosition': 0, - # "type": "PRODUCT", - # # 'maxLimit': '', - # }) - - def create_menu_by_local_category(self, line): - if line.product.categories[0]: - category = { - 'id': line.product.categories[0].id, - 'maxQty': 0, - 'minQty': 0, - 'name': line.product.categories[0].name, - 'children': [] - } - category['children'].append({ - }) + children_list[0]['children'] = self.get_products(child) + if not category.childs: + for product in category.products: + children_list.append({ + "name": product.name, + "description": product.description if product.description else 'not available', + "price": float(product.list_price), + "sku": product.code, + "sortingPosition": 0, + "type": "PRODUCT", + "category": { + "id": category.id, + "maxQty": 0, + "minQty": 0, + "name": category.name, + "sortingPosition": 0 + } + }) + return children_list + + # rappi_api = endpoints_rappi.RappiAPI(self) + # rappi_api.create_menu_store(self.shop.store_id, items) + # menu_json = json.dumps(menu) + # URL = 'https://microservices.dev.rappi.com/api/v2/restaurants-integrations-public-api/menu' + # token = self.access_token + # headers = { + # 'Content-Type': 'application/json', + # 'x-authorization': 'bearer ' + token, + # } + # response = requests.request("POST", URL, headers=headers, data=menu_json) diff --git a/shop.py b/shop.py new file mode 100644 index 0000000..5908913 --- /dev/null +++ b/shop.py @@ -0,0 +1,8 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from trytond.pool import Pool, PoolMeta +from trytond.model import fields + + +class SaleShop(metaclass=PoolMeta): + __name__ = 'sale.shop' diff --git a/shop.xml b/shop.xml new file mode 100644 index 0000000..c443cc7 --- /dev/null +++ b/shop.xml @@ -0,0 +1,12 @@ + + + + + + sale.shop + + shop_form + + + diff --git a/tryton.cfg b/tryton.cfg index 87a37a5..f21f57a 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,12 +1,13 @@ [tryton] version=6.0.3 depends: + ir + web_shop sale_pos product_reference log - ir xml: - web_channel.xml + web.xml sale.xml party.xml api_log.xml diff --git a/view/sale_web_channel_form.xml b/view/sale_web_channel_form.xml index effa2d2..f1a0aa5 100644 --- a/view/sale_web_channel_form.xml +++ b/view/sale_web_channel_form.xml @@ -18,6 +18,10 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig