trytonpsk-sale_web_channel/web_channel.py

509 lines
20 KiB
Python
Raw Permalink Normal View History

2020-05-31 18:40:21 +02:00
# -*- coding: UTF-8 -*-
# This file is part electronic_mail_template module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.model import Workflow, ModelView, ModelSQL, fields
2023-08-17 21:52:47 +02:00
from trytond.pyson import Eval, And
from datetime import datetime, timedelta
2020-08-13 03:16:53 +02:00
from trytond.pool import Pool
from trytond.wizard import (Wizard, StateTransition, StateView, Button)
from trytond.transaction import Transaction
2021-07-21 00:22:58 +02:00
from .exceptions import ErrorSynchronizingSale
from trytond.i18n import gettext
2020-05-31 18:40:21 +02:00
STATES = {
2020-07-16 04:02:50 +02:00
'readonly': (Eval('state') != 'draft')
2020-05-31 18:40:21 +02:00
}
2020-06-23 06:24:08 +02:00
CHANNELS = [
2021-02-23 21:23:18 +01:00
('', ''),
('mercadolibre', 'Mercado Libre'),
('rappi', 'Rappi'),
('melhous', 'Melhous'),
('ifood', 'IFood'),
('shopify', 'Shopify'),
('socialnetwork', 'Social Network'),
('direct', 'Direct'),
('pageweb', 'Page Web'),
('internal', 'Internal'),
('platform', 'Platform'),
('telephone', 'Telephone'),
]
2020-06-23 06:24:08 +02:00
2020-06-29 06:17:49 +02:00
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'),
]
2020-05-31 18:40:21 +02:00
ORDERS_CREATED = 0
2021-01-26 00:35:56 +01:00
2020-05-31 18:40:21 +02:00
class SaleWebChannel(Workflow, ModelSQL, ModelView):
'Web Channel'
__name__ = 'sale.web_channel'
2021-01-26 00:35:56 +01:00
_rec_name = 'name'
2021-01-15 19:47:28 +01:00
name = fields.Char('Name Channel')
2021-01-26 00:35:56 +01:00
channel_name = fields.Selection(CHANNELS,
'Channel', select=True, required=True, states=STATES)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
2021-01-15 19:47:28 +01:00
shop = fields.Many2One('sale.shop', 'Shop', domain=[
('company', '=', Eval('company'))
], states=STATES)
user = fields.Many2One('res.user', 'User', domain=[
('company', '=', Eval('company'))
], states=STATES)
secret_key = fields.Char('Secret Key')
app_id = fields.Char('Application ID')
2020-10-16 16:46:59 +02:00
redirect_uri = fields.Char('Redirect URI', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
})
authorization_code = fields.Char('Authorization Code', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
})
access_token = fields.Char('Access Token', states={
2023-08-17 21:52:47 +02:00
'invisible': And(
Eval('channel_name') != 'mercadolibre',
Eval('channel_name') != 'rappi'),
2020-10-16 16:46:59 +02:00
})
creation_time = fields.DateTime('Creation Time', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
})
2020-05-31 18:40:21 +02:00
status_token = fields.Function(fields.Selection([
('expired', 'Expired'),
('active', 'Active'),
2020-10-16 16:46:59 +02:00
], 'Status Token', readonly=True, states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
}), 'get_status_token')
refresh_token = fields.Char('Refresh Token', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
})
2020-05-31 18:40:21 +02:00
state = fields.Selection([
('draft', 'Draft'),
('active', 'Active'),
('finished', 'Finished'),
], 'State', select=True, readonly=True)
2020-09-02 00:19:15 +02:00
freight_product = fields.Many2One('product.product', 'Freight Product',
states=STATES)
2021-02-23 19:58:11 +01:00
bonus_product = fields.Many2One('product.product', 'Bonus Product', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
'readonly': (Eval('state') != 'draft')
})
2021-02-19 19:12:03 +01:00
tip_product = fields.Many2One('product.product', 'Tip Product', states={
'invisible': (Eval('channel_name') != 'shopify'),
'readonly': (Eval('state') != 'draft')
})
2021-03-24 20:06:51 +01:00
generic_product = fields.Many2One('product.product', 'Generic Product', states={
# 'invisible': (Eval('channel_name') != 'shopify'),
'readonly': (Eval('state') != 'draft')
})
2020-06-26 04:50:30 +02:00
report = fields.Many2One('ir.action.report', 'Report', states=STATES)
2020-06-29 06:17:49 +02:00
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice',
2020-09-02 00:19:15 +02:00
states=STATES)
seller_id = fields.Char('Seller ID', states={
2023-09-01 15:29:48 +02:00
'invisible': ~Eval('channel_name').in_(['mercadolibre', 'rappi']),
'readonly': (Eval('state') != 'draft')
})
2023-11-28 23:23:11 +01:00
# admin_category = fields.Many2One('product.category', 'Admin Category',
# domain=[
# ('accounting', '=', False),
# ])
2023-09-01 15:29:48 +02:00
price_list = fields.Many2One('product.price_list', 'Pricelist', ondelete="RESTRICT")
template_notification = fields.Many2One(
'email.template', 'Template Notification')
2020-10-16 16:46:59 +02:00
api_key = fields.Char('Api Key', states={
'invisible': (Eval('channel_name') != 'shopify'),
})
password_api = fields.Char('Api Password', states={
'invisible': (Eval('channel_name') != 'shopify'),
})
host_name = fields.Char('Host Name', states={
2023-08-17 21:52:47 +02:00
'invisible': And(
Eval('channel_name') != 'shopify',
Eval('channel_name') != 'rappi'),
2020-10-16 16:46:59 +02:00
})
2020-12-29 02:05:39 +01:00
party = fields.Many2One('party.party', 'party', required=False)
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term')
2021-02-19 19:12:03 +01:00
2020-05-31 18:40:21 +02:00
@classmethod
def __setup__(cls):
super(SaleWebChannel, cls).__setup__()
cls._transitions |= set((
2021-02-23 21:23:18 +01:00
('draft', 'active'),
('active', 'draft'),
('active', 'finished'),
('finished', 'active'),
))
2020-05-31 18:40:21 +02:00
cls._buttons.update({
'draft': {
'invisible': Eval('state') != 'active',
2021-02-23 21:23:18 +01:00
},
2020-05-31 18:40:21 +02:00
'active': {
'invisible': Eval('state') == 'active',
2021-02-23 21:23:18 +01:00
},
2020-05-31 18:40:21 +02:00
'finished': {
'invisible': Eval('state') != 'active',
2021-02-23 21:23:18 +01:00
},
2021-08-23 23:43:26 +02:00
'refresh_token_b': {
2023-08-17 21:52:47 +02:00
'invisible': And(Eval('channel_name') != 'mercadolibre', Eval('channel_name') != 'rappi'),
},
'synchronize_menu': {
'invisible': And(Eval('channel_name') != 'rappi', Eval('state') == 'active'),
2021-08-23 22:47:20 +02:00
},
2020-05-31 18:40:21 +02:00
})
@staticmethod
def default_company():
return Transaction().context.get('company')
# @classmethod
# def import_data(cls, fields_names, data):
# return 0
2020-08-13 04:25:58 +02:00
2020-05-31 18:40:21 +02:00
@staticmethod
def default_state():
return 'draft'
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('active')
def active(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finished(cls, records):
pass
2023-08-17 21:52:47 +02:00
@classmethod
@ModelView.button
def synchronize_menu(cls, records):
for record in records:
if record.channel_name == 'rappi':
record.synchronize_menu_rappi()
2021-08-23 22:47:20 +02:00
@classmethod
@ModelView.button
2021-08-23 23:43:26 +02:00
def refresh_token_b(cls, records):
2021-08-23 22:47:20 +02:00
if records:
for record in records:
if record.channel_name == 'mercadolibre':
MercadoLibre = Pool().get('sale.web_channel.mercado_libre')
channels = MercadoLibre.search([
('state', '=', 'active'),
('channel_name', '=', 'mercadolibre'),
])
2021-08-23 23:50:54 +02:00
res = channels[0]._validate_token()
2023-08-17 21:52:47 +02:00
if record.channel_name == 'rappi':
Rappi = Pool().get('sale.web_channel.rappi')
channels = Rappi.search([
('state', '=', 'active'),
('channel_name', '=', 'rappi'),
])
res = channels[0].generate_token_access()
print(res, 'fuck yeah!')
2021-08-23 22:47:20 +02:00
2020-09-30 17:58:41 +02:00
def send_mail_notification(self, message):
Template = Pool().get('email.template')
setattr(self, 'message', message)
response = Template.send(self.template_notification, self)
2020-05-31 18:40:21 +02:00
def get_status_token(self, name):
if self.creation_time:
now = datetime.now()
2022-08-16 21:19:05 +02:00
res = (now - self.creation_time).total_seconds()
if res >= 21600:
2020-05-31 18:40:21 +02:00
return 'expired'
else:
return 'active'
else:
return 'expired'
2020-06-02 01:34:57 +02:00
@classmethod
2020-06-26 04:50:30 +02:00
def request_api(cls, data):
return {}
2020-08-18 22:11:20 +02:00
def render_report(self, record):
if not self.report:
return None
Report = Pool().get(self.report.report_name, type='report')
report = Report.execute([record.id], {'id': record.id})
ext, data, filename, file_name = report
return data
2023-09-01 15:29:48 +02: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'))
])
2021-02-23 21:23:18 +01:00
channel = fields.Many2One('sale.web_channel', 'Channel', required=True,
domain=[
('shop', '=', Eval('shop'))
])
operation = fields.Selection([
('', ''),
('orders_for_day', 'Orders for day'),
('especific_order', 'Especific Orders')
], 'Operation', required=True)
2021-02-19 19:12:03 +01:00
name_channel = 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'),
})
2021-02-19 19:12:03 +01:00
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')
2021-02-19 19:12:03 +01:00
@fields.depends('channel')
def on_change_channel(self):
self.name_channel = self.channel.channel_name 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.channel_name == 'mercadolibre':
MercadoLibre = pool.get('sale.web_channel.mercado_libre')
channel = MercadoLibre(channel)
2021-03-24 20:06:51 +01:00
if operation == 'especific_order':
order_id = self.start.order_id
URI = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
order_id, channel.access_token)
result = MercadoLibre.get_response(URI).json()
if not result:
2021-07-21 00:22:58 +02:00
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
2023-12-05 23:34:38 +01:00
print(result)
2021-11-08 16:29:02 +01:00
sale_created = channel.validate_sale(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()
2020-10-01 04:27:26 +02:00
for res in result['results']:
2021-12-29 20:21:22 +01:00
sales = Sale.search([('reference', 'like', '%' + str(res['id']) + '%' )])
2020-10-01 04:27:26 +02:00
shipment_status = ''
if not sales:
2021-11-08 16:29:02 +01:00
sale_created = channel.validate_sale(res)
2020-10-01 04:27:26 +02:00
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']
2020-10-01 16:32:19 +02:00
if res.get('pack_id') or not shipment_status:
2020-10-01 15:19:26 +02:00
continue
2020-10-01 16:32:19 +02:00
if shipment_status in ['shipped', 'delivered']:
2020-10-01 04:27:26 +02:00
for sale in sales:
if not sale.invoices:
Sale.process([sale])
2020-10-16 16:46:59 +02:00
if channel.channel_name == 'shopify':
Shopify = pool.get('sale.web_channel.shopify')
2021-06-30 22:01:11 +02:00
channel, = Shopify.search([('shop', '=', self.start.shop.id), ])
2020-10-16 16:46:59 +02:00
if operation == 'especific_order':
order_id = self.start.order_id
2021-02-19 19:12:03 +01:00
URI = 'https://%s:%s@%s/admin/api/2020-10/orders.json?status=any&ids=%s' % (
2020-10-16 16:46:59 +02:00
channel.api_key, channel.password_api, channel.host_name, order_id)
result = Shopify.get_response(URI).json()
if not result:
2021-07-21 00:22:58 +02:00
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
2020-10-16 16:46:59 +02:00
sale_created = channel._create_sale(result['orders'][0])
2021-02-19 19:12:03 +01:00
if sale_created and \
sale_created.description.count('fulfilled'):
2021-02-19 19:12:03 +01:00
Sale.process([sale_created])
2020-10-16 16:46:59 +02:00
else:
date_from = str(self.start.date) + 'T00:00:00.000-00:00'
2021-02-19 19:12:03 +01: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' % (
2020-10-16 16:46:59 +02:00
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']))])
2021-02-19 19:12:03 +01:00
fulfillment_status = ''
2020-10-16 16:46:59 +02:00
if not sales:
sale_created = channel._create_sale(res)
2021-02-19 19:12:03 +01:00
fulfillment_status = sale_created.description
2020-10-16 16:46:59 +02:00
sales = [sale_created]
2021-02-19 19:12:03 +01:00
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 FinishInvoicesStart(ModelView):
'Finish Invoices Start'
__name__ = 'sale_web_channel.finish_invoices.start'
company = fields.Many2One('company.company', 'Company', required=True)
shop = fields.Many2One('sale.shop', 'Shop', required=True, domain=[
('company', '=', Eval('company'))
])
channel = fields.Many2One('sale.web_channel', 'Channel', required=True, domain=[
('shop', '=', Eval('shop'))
])
date = fields.Date('Date', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
class FinishInvoices(Wizard):
'Finish Invoices'
__name__ = 'sale_web_channel.finish_invoices'
start = StateView('sale_web_channel.finish_invoices.start',
'sale_web_channel.finish_invoices_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(FinishInvoices, cls).__setup__()
def transition_accept(self):
pool = Pool()
Invoice = pool.get('account.invoice')
2021-02-23 20:30:34 +01:00
Shopify = pool.get('sale.web_channel.shopify')
MercadoLibre = pool.get('sale.web_channel.mercado_libre')
2021-02-23 20:30:34 +01:00
if self.start.channel == 'mercadolibre':
channel = MercadoLibre(self.start.channel)
2021-02-26 00:16:07 +01:00
if self.start.channel == 'shopify':
2021-02-23 20:30:34 +01:00
channel = Shopify(self.start.channel)
date = self.start.date
invoices = Invoice.search([
('state', '=', 'draft'),
('invoice_date', '=', date),
])
for inv in invoices:
if not inv.sales:
continue
sale = inv.sales[0]
channel_id = sale.channel.id
if channel_id != channel.id:
continue
Invoice.validate_invoice([inv])
2022-04-01 15:43:11 +02:00
if inv.invoice_type not in ('C', 'P', 'M') and inv.electronic_state != 'authorized':
Invoice.submit([inv])
if not inv.cufe:
return
Invoice.post([inv])
2021-02-23 20:30:34 +01:00
if self.start.channel == 'mercadolibre':
channel.upload_note(sale, 'Factura generada')
channel.upload_invoice(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'
2023-08-17 21:52:47 +02:00
class SynchronizeMenuWizard(Wizard):
'Synchronize Menu'
__name__ = 'sale_web_channel.synchronize_menu'
start_state = 'synchronize_menu'
synchronize_menu = StateTransition()
@classmethod
def transition_synchronize_menu(self):
pool = Pool()
active_id = Transaction().context.get('active_id')
Chanel = pool.get('sale.web_channel')
chanel = Chanel(active_id)
print(chanel)
if chanel.channel_name == 'rappi':
print('ya paso')
Rappi = Pool().get('sale.web_channel.rappi')
channels = Rappi.search([
('state', '=', 'active'),
('channel_name', '=', 'rappi'),
])
res = channels[0].synchronize_menu_rappi()
print(res, 'fuck yeah')
return 'end'