changes from channel to web_shop

This commit is contained in:
Elvis 2023-09-01 08:29:48 -05:00
parent 9929a86c09
commit 71103f224d
13 changed files with 337 additions and 234 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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
View File

@ -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)

8
shop.py Normal file
View File

@ -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'

12
shop.xml Normal file
View File

@ -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>

View File

@ -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

View File

@ -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>

12
view/shop_form.xml Normal file
View File

@ -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>

31
view/web_shop_form.xml Normal file
View File

@ -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>

131
web.py Normal file
View File

@ -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)

12
web.xml Normal file
View File

@ -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>

View File

@ -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'