trytonpsk-sale_web_channel/web.py

327 lines
13 KiB
Python

from trytond.model import fields, ModelView
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, And
from datetime import datetime, timedelta
from trytond.wizard import (Wizard, StateTransition, StateView, Button)
from .exceptions import ErrorSynchronizingSale
from trytond.i18n import gettext
from trytond.transaction import Transaction
from . mercado_libre import MercadoLibre
from . shopify import Shopify
from . import 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'),
]
# TODO FOR EXECUTE in database
"""
alter database rename table sale_web_channel to web_shop
RENAME COLUMN channel_name by type
RENAME COLUMN creation_time by token_create_date
"""
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'),
})
shop = fields.Many2One('sale.shop', 'Shop', domain=[
('company', '=', Eval('company'))
])
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']),
})
password_api = fields.Char('Api Password', states={
'invisible': (Eval('type') != 'shopify'),
})
host_name = fields.Char('Host Name', states={
'invisible': And(
Eval('type') != 'shopify',
Eval('type') != 'rappi'),
})
api_key = fields.Char('Api Key', states={
'invisible': (Eval('type') != 'shopify'),
})
price_list = fields.Many2One('product.price_list', 'Price list', ondelete="RESTRICT")
@classmethod
def __setup__(cls):
super(Shop, cls).__setup__()
cls.type.selection = TYPES
cls._buttons.update({
'generate_token_access': {
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
},
'send_products': {
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
},
'get_orders': {
'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('web.shop')
mercadolibre_rec = MercadoLibre(record)
# record.access_token, refresh_token, client_id, client_secret = mercadolibre_rec._validate_token()
record.access_token = mercadolibre_rec._validate_token()
record.save()
if record.type == 'rappi':
rappi_rec = rappi.Rappi(record)
record.access_token = rappi_rec.get_token_access()['access_token']
record.save()
@classmethod
@ModelView.button
def send_products(cls, records):
for record in records:
if record.type == 'rappi':
rappi_rec = rappi.Rappi(record)
rappi_rec.synchronize_menu_rappi(record.products, record.categories)
@classmethod
@ModelView.button
def get_orders(cls, records):
for record in records:
if record.type == 'rappi':
rappi_rec = rappi.Rappi(record)
rappi_rec.create_sales()
class SynchronizeChannelOrdersStart(ModelView):
'Synchronize Channel orders Start'
__name__ = 'sale_web_channel.synchronize_orders.start'
company = fields.Many2One('company.company', 'Company', required=True)
shop = fields.Many2One('sale.shop', 'Shop', required=True, domain=[
('company', '=', Eval('company'))
])
channel = fields.Many2One('web.shop', 'Channel', required=True,
domain=[
('shop', '=', Eval('shop'))
])
operation = fields.Selection([
('', ''),
('orders_for_day', 'Orders for day'),
('especific_order', 'Especific Orders')
], 'Operation', required=True)
type = fields.Selection([
('', ''),
('shopify', 'Shopify'),
('mercadolibre', 'Mercado libre')
], 'Name Channel', required=True)
order_id = fields.Char('Order ID', states={
'invisible': (Eval('operation') != 'especific_order'),
'required': (Eval('operation') == 'especific_order'),
})
date = fields.Date('Date', states={
'invisible': (Eval('operation') != 'orders_for_day'),
'required': (Eval('operation') == 'orders_for_day'),
})
date_end = fields.Date('Finish Date', states={
'invisible': (Eval('operation') != 'orders_for_day') | (Eval('name_channel') != 'shopify'),
'required': (Eval('operation') == 'orders_for_day') & (Eval('name_channel') != 'mercadolibre'),
})
@staticmethod
def default_company():
return Transaction().context.get('company')
@fields.depends('channel')
def on_change_channel(self):
self.type = self.channel.type if self.channel else None
class SynchronizeChannelOrders(Wizard):
'Synchronize Channel Orders'
__name__ = 'sale_web_channel.synchronize_orders'
start = StateView('sale_web_channel.synchronize_orders.start',
'sale_web_channel.synchronize_orders_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Create', 'accept',
'tryton-ok', default=True),
])
accept = StateTransition()
done = StateView('sale_web_channel.synchronize_orders.done',
'sale_web_channel.synchronize_orders_done', [
Button('Done', 'end', 'tryton-ok', default=True),
])
@classmethod
def __setup__(cls):
super(SynchronizeChannelOrders, cls).__setup__()
def transition_accept(self):
pool = Pool()
Sale = pool.get('sale.sale')
channel = self.start.channel
operation = self.start.operation
if channel.type == 'mercadolibre':
mercado_libre = MercadoLibre(channel)
if operation == 'especific_order':
order_id = self.start.order_id
result = mercado_libre._validate_number_id(order_id, channel.access_token)
if not result:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
sale_created = result
if sale_created:
generic_code = self.start.channel.generic_product.code if self.start.channel.generic_product else None
product_generic = [
line.product.code for line in sale_created.lines if line.product.code == generic_code and generic_code]
if not sale_created.pack_id and \
sale_created.comment in ['shipped', 'delivered'] and \
len(product_generic) < 1:
Sale.process([sale_created])
else:
date_from = str(self.start.date) + 'T00:00:00.000-00:00'
date_to = str(self.start.date + timedelta(days=1)
) + 'T00:00:00.000-00:00'
URI = 'https://api.mercadolibre.com/orders/search?seller=%s&order.date_created.from=%s&order.date_created.to=%s&access_token=%s&order.status=paid' % (
channel.seller_id, date_from, date_to, channel.access_token)
result = MercadoLibre.get_response(URI).json()
for res in result['results']:
sales = Sale.search([('reference', 'like', '%' + str(res['id']) + '%' )])
shipment_status = ''
if not sales:
sale_created = channel.validate_sale(res)
shipment_status = sale_created.comment
sales = [sale_created]
else:
sfm_id = res['shipping']['id']
shipment_ = channel.get_shipment_api(sfm_id)
shipment_status = shipment_['status']
if res.get('pack_id') or not shipment_status:
continue
if shipment_status in ['shipped', 'delivered']:
for sale in sales:
if not sale.invoices:
Sale.process([sale])
if channel.type == 'shopify':
shopify = Shopify(channel)
# channel, = Shopify.search([('shop', '=', self.start.shop.id), ])
if operation == 'especific_order':
order_id = self.start.order_id
URI = 'https://%s:%s@%s/admin/api/2020-10/orders.json?status=any&ids=%s' % (
channel.api_key, channel.password_api, channel.host_name, order_id)
result = shopify.get_response(URI).json()
if not result:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
sale_created = shopify._create_sale(result['orders'][0])
if sale_created and \
sale_created.description.count('fulfilled'):
Sale.process([sale_created])
else:
date_from = str(self.start.date) + 'T00:00:00.000-00:00'
date_to = str(self.start.date_end) + 'T00:00:00.000-00:00'
URI = 'https://%s:%s@%s/admin/api/2020-10/orders.json?status=any&created_at_min=%s&created_at_max=%s' % (
channel.api_key, channel.password_api, channel.host_name, date_from, date_to)
result = Shopify.get_response(URI).json()
print('quantity orders----> ', len(result['orders']))
for res in result['orders']:
sales = Sale.search([('reference', '=', str(res['id']))])
fulfillment_status = ''
if not sales:
sale_created = channel._create_sale(res)
fulfillment_status = sale_created.description
sales = [sale_created]
else:
fulfillment_status = res['fulfillment_status']
if not fulfillment_status:
continue
if fulfillment_status.count('fulfilled'):
for sale in sales:
if not sale.invoices:
Sale.process([sale])
return 'done'
class SynchronizeChannelOrdersDone(ModelView):
'Synchronize Channel Orders Done'
__name__ = 'sale_web_channel.synchronize_orders.done'
result = fields.Char('Result', readonly=True)
@staticmethod
def default_result():
return 'successful synchronization'