changes from channel to web_shop
This commit is contained in:
parent
9929a86c09
commit
71103f224d
27
__init__.py
27
__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')
|
||||
|
|
27
api_log.py
27
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)
|
||||
|
|
|
@ -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
|
||||
|
|
234
rappi.py
234
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)
|
||||
|
|
|
@ -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'
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="sale_shop_view_form">
|
||||
<field name="model">sale.shop</field>
|
||||
<field name="inherit" ref="sale_shop.sale_shop_view_form"/>
|
||||
<field name="name">shop_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -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
|
||||
|
|
|
@ -18,6 +18,10 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
|
|||
<field name="secret_key"/>
|
||||
<label name="seller_id"/>
|
||||
<field name="seller_id"/>
|
||||
<label name="admin_category"/>
|
||||
<field name="admin_category"/>
|
||||
<label name="price_list"/>
|
||||
<field name="price_list"/>
|
||||
<label name="redirect_uri"/>
|
||||
<field name="redirect_uri"/>
|
||||
<label name="authorization_code"/>
|
||||
|
@ -55,20 +59,9 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
|
|||
<label name="template_notification"/>
|
||||
<field name="template_notification"/>
|
||||
<group col="6" colspan="4" id="state_buttons">
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
<group col="5" colspan="2" id="buttons">
|
||||
<button name="draft" string="Draft"
|
||||
icon="tryton-clear"/>
|
||||
<button name="active" string="Active"
|
||||
icon="tryton-ok"/>
|
||||
<button name="finished" string="Finished"
|
||||
icon="tryton-forward"/>
|
||||
<button name="refresh_token_b" string="Refresh Token"
|
||||
icon="tryton-ok"/>
|
||||
<button name="generate_token_access" string="generate_token_access"
|
||||
icon="tryton-ok"/>
|
||||
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part sale_shop module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath
|
||||
expr="/form/notebook/page[@id='salesmans']"
|
||||
position="after">
|
||||
<page string="Web Channels" id="web_channels">
|
||||
<field name="sale_web_channels"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part sale_shop module for Tryton.
|
||||
The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath
|
||||
expr="/form/notebook/page[@id='products']"
|
||||
position="after">
|
||||
<page string="Connection Data" id="connection_data">
|
||||
<label name="secret_key"/>
|
||||
<field name="secret_key"/>
|
||||
<label name="app_id"/>
|
||||
<field name="app_id"/>
|
||||
<label name="redirect_uri"/>
|
||||
<field name="redirect_uri"/>
|
||||
<label name="authorization_code"/>
|
||||
<field name="authorization_code"/>
|
||||
<label name="access_token"/>
|
||||
<field name="access_token"/>
|
||||
<label name="status_token"/>
|
||||
<field name="status_token"/>
|
||||
<label name="refresh_token"/>
|
||||
<field name="refresh_token"/>
|
||||
<label name="seller_id"/>
|
||||
<field name="seller_id"/>
|
||||
<group col="6" colspan="4" id="state_buttons">
|
||||
<button name="generate_token_access" string="generate_token_access"
|
||||
icon="tryton-ok"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
|
@ -0,0 +1,131 @@
|
|||
from trytond.model import fields, ModelView
|
||||
from trytond.pool import PoolMeta, Pool
|
||||
from trytond.pyson import Eval, And, Or
|
||||
from datetime import datetime, timedelta
|
||||
from . import endpoints_rappi
|
||||
|
||||
TYPES = [
|
||||
('', ''),
|
||||
('mercadolibre', 'Mercado Libre'),
|
||||
('melhous', 'Melhous'),
|
||||
('shopify', 'Shopify'),
|
||||
('rappi', 'Rappi'),
|
||||
]
|
||||
TYPE_INVOICE = [
|
||||
('', ''),
|
||||
('C', 'Computador'),
|
||||
('P', 'POS'),
|
||||
('M', 'Manual'),
|
||||
('1', 'Venta Electronica'),
|
||||
('2', 'Exportacion'),
|
||||
('3', 'Factura por Contingencia Facturador'),
|
||||
('4', 'Factura por Contingencia DIAN'),
|
||||
('91', 'Nota Crédito Eléctronica'),
|
||||
('92', 'Nota Débito Eléctronica'),
|
||||
]
|
||||
|
||||
|
||||
class Shop(metaclass=PoolMeta):
|
||||
__name__ = 'web.shop'
|
||||
secret_key = fields.Char('Secret Key')
|
||||
app_id = fields.Char('Application ID')
|
||||
redirect_uri = fields.Char('Redirect URI', states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
})
|
||||
authorization_code = fields.Char('Authorization Code', states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
})
|
||||
access_token = fields.Char('Access Token', states={
|
||||
'invisible': And(
|
||||
Eval('type') != 'mercadolibre',
|
||||
Eval('type') != 'rappi'),
|
||||
})
|
||||
token_create_date = fields.DateTime('Token Create Date', states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
})
|
||||
status_token = fields.Function(fields.Selection([
|
||||
('expired', 'Expired'),
|
||||
('active', 'Active'),
|
||||
], 'Status Token', readonly=True, states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
}), 'get_status_token')
|
||||
refresh_token = fields.Char('Refresh Token', states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
})
|
||||
# state = fields.Selection([
|
||||
# ('draft', 'Draft'),
|
||||
# ('active', 'Active'),
|
||||
# ('finished', 'Finished'),
|
||||
# ], 'State', select=True, readonly=True)
|
||||
freight_product = fields.Many2One('product.product', 'Freight Product')
|
||||
# states=STATES)
|
||||
bonus_product = fields.Many2One('product.product', 'Bonus Product', states={
|
||||
'invisible': (Eval('type') != 'mercadolibre'),
|
||||
})
|
||||
tip_product = fields.Many2One('product.product', 'Tip Product', states={
|
||||
'invisible': (Eval('type') != 'shopify'),
|
||||
})
|
||||
|
||||
generic_product = fields.Many2One('product.product', 'Generic Product')
|
||||
# states={
|
||||
# 'invisible': (Eval('type') != 'shopify'),
|
||||
# })
|
||||
report = fields.Many2One('ir.action.report', 'Report')
|
||||
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice')
|
||||
seller_id = fields.Char('Seller ID', states={
|
||||
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Shop, cls).__setup__()
|
||||
cls.type.selection = TYPES
|
||||
|
||||
cls._buttons.update({
|
||||
'generate_token_access': {
|
||||
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
|
||||
},
|
||||
'synchronize_products': {
|
||||
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
|
||||
},
|
||||
})
|
||||
|
||||
def get_status_token(self, name):
|
||||
if self.token_create_date:
|
||||
now = datetime.now()
|
||||
res = (now - self.token_create_date).total_seconds()
|
||||
if res >= 21600:
|
||||
return 'expired'
|
||||
else:
|
||||
return 'active'
|
||||
else:
|
||||
return 'expired'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
def generate_token_access(cls, records):
|
||||
if records:
|
||||
for record in records:
|
||||
if record.type == 'mercadolibre':
|
||||
MercadoLibre = Pool().get('sale.web_channel.mercado_libre')
|
||||
channels = MercadoLibre.search([
|
||||
('state', '=', 'active'),
|
||||
('type', '=', 'mercadolibre'),
|
||||
])
|
||||
if record.type == 'rappi':
|
||||
record.generate_token_access_rappi()
|
||||
|
||||
def generate_token_access_rappi(self):
|
||||
api_rappi = endpoints_rappi.RappiAPI(self)
|
||||
result = api_rappi.get_token_access(self.seller_id, self.secret_key)
|
||||
self.access_token = result['access_token']
|
||||
self.save()
|
||||
|
||||
def synchronize_products(self):
|
||||
if self.type == 'rappi':
|
||||
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)
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="shop_view_form">
|
||||
<field name="model">web.shop</field>
|
||||
<field name="inherit" ref="web_shop.shop_view_form"/>
|
||||
<field name="name">web_shop_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -110,9 +110,14 @@ class SaleWebChannel(Workflow, ModelSQL, ModelView):
|
|||
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice',
|
||||
states=STATES)
|
||||
seller_id = fields.Char('Seller ID', states={
|
||||
'invisible': (Eval('channel_name') != 'mercadolibre'),
|
||||
'invisible': ~Eval('channel_name').in_(['mercadolibre', 'rappi']),
|
||||
'readonly': (Eval('state') != 'draft')
|
||||
})
|
||||
admin_category = fields.Many2One('product.category', 'Admin Category',
|
||||
domain=[
|
||||
('accounting', '=', False),
|
||||
])
|
||||
price_list = fields.Many2One('product.price_list', 'Pricelist', ondelete="RESTRICT")
|
||||
template_notification = fields.Many2One(
|
||||
'email.template', 'Template Notification')
|
||||
api_key = fields.Char('Api Key', states={
|
||||
|
@ -242,7 +247,7 @@ class SaleWebChannel(Workflow, ModelSQL, ModelView):
|
|||
report = Report.execute([record.id], {'id': record.id})
|
||||
ext, data, filename, file_name = report
|
||||
return data
|
||||
|
||||
|
||||
|
||||
class SynchronizeChannelOrdersStart(ModelView):
|
||||
'Synchronize Channel orders Start'
|
||||
|
|
Loading…
Reference in New Issue