trytonpsk-sale_web_channel/web.py

327 lines
13 KiB
Python
Raw Permalink Normal View History

2023-09-01 15:29:48 +02:00
from trytond.model import fields, ModelView
from trytond.pool import PoolMeta, Pool
2023-11-28 23:23:11 +01:00
from trytond.pyson import Eval, And
2023-09-01 15:29:48 +02:00
from datetime import datetime, timedelta
2023-11-28 23:23:11 +01:00
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
2023-12-11 17:22:25 +01:00
from . shopify import Shopify
2023-09-27 23:57:00 +02:00
from . import rappi
2023-09-01 15:29:48 +02:00
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'),
]
2023-11-28 23:23:11 +01:00
# 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
"""
2023-09-01 15:29:48 +02:00
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'),
})
2023-11-28 23:23:11 +01:00
shop = fields.Many2One('sale.shop', 'Shop', domain=[
('company', '=', Eval('company'))
])
2023-09-01 15:29:48 +02:00
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'),
})
2023-12-05 14:20:07 +01:00
generic_product = fields.Many2One('product.product', 'Generic Product', states={
'invisible': (Eval('type') != 'shopify'),
})
2023-09-01 15:29:48 +02:00
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']),
})
2023-11-28 23:23:11 +01:00
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'),
})
2023-09-28 21:39:30 +02:00
price_list = fields.Many2One('product.price_list', 'Price list', ondelete="RESTRICT")
2023-09-01 15:29:48 +02:00
@classmethod
def __setup__(cls):
super(Shop, cls).__setup__()
cls.type.selection = TYPES
cls._buttons.update({
'generate_token_access': {
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
},
2023-09-27 23:57:00 +02:00
'send_products': {
'invisible': ~Eval('type').in_(['mercadolibre', 'rappi']),
},
'get_orders': {
2023-09-01 15:29:48 +02:00
'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':
2023-11-28 23:23:11 +01:00
# 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()
2023-09-01 15:29:48 +02:00
if record.type == 'rappi':
2023-09-27 23:57:00 +02:00
rappi_rec = rappi.Rappi(record)
record.access_token = rappi_rec.get_token_access()['access_token']
record.save()
2023-09-01 15:29:48 +02:00
2023-09-27 23:57:00 +02:00
@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)
2023-09-01 15:29:48 +02:00
2023-09-27 23:57:00 +02:00
@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()
2023-09-28 21:39:30 +02:00
2023-11-28 23:23:11 +01:00
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
2023-12-05 23:34:38 +01:00
result = mercado_libre._validate_number_id(order_id, channel.access_token)
2023-11-28 23:23:11 +01:00
if not result:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
2023-12-05 23:34:38 +01:00
sale_created = result
2023-11-28 23:23:11 +01:00
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':
2023-12-11 17:22:25 +01:00
shopify = Shopify(channel)
2023-11-28 23:23:11 +01:00
2023-12-11 17:22:25 +01:00
# channel, = Shopify.search([('shop', '=', self.start.shop.id), ])
2023-11-28 23:23:11 +01:00
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)
2023-12-11 17:22:25 +01:00
result = shopify.get_response(URI).json()
2023-11-28 23:23:11 +01:00
if not result:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
2023-12-11 17:22:25 +01:00
sale_created = shopify._create_sale(result['orders'][0])
2023-11-28 23:23:11 +01:00
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'