trytonpsk-sale_web_channel/web_channel.py

509 lines
20 KiB
Python

# -*- 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
from trytond.pyson import Eval, And
from datetime import datetime, timedelta
from trytond.pool import Pool
from trytond.wizard import (Wizard, StateTransition, StateView, Button)
from trytond.transaction import Transaction
from .exceptions import ErrorSynchronizingSale
from trytond.i18n import gettext
STATES = {
'readonly': (Eval('state') != 'draft')
}
CHANNELS = [
('', ''),
('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'),
]
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'),
]
ORDERS_CREATED = 0
class SaleWebChannel(Workflow, ModelSQL, ModelView):
'Web Channel'
__name__ = 'sale.web_channel'
_rec_name = 'name'
name = fields.Char('Name Channel')
channel_name = fields.Selection(CHANNELS,
'Channel', select=True, required=True, states=STATES)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
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')
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={
'invisible': And(
Eval('channel_name') != 'mercadolibre',
Eval('channel_name') != 'rappi'),
})
creation_time = fields.DateTime('Creation Time', states={
'invisible': (Eval('channel_name') != 'mercadolibre'),
})
status_token = fields.Function(fields.Selection([
('expired', 'Expired'),
('active', 'Active'),
], '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'),
})
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('channel_name') != 'mercadolibre'),
'readonly': (Eval('state') != 'draft')
})
tip_product = fields.Many2One('product.product', 'Tip Product', states={
'invisible': (Eval('channel_name') != 'shopify'),
'readonly': (Eval('state') != 'draft')
})
generic_product = fields.Many2One('product.product', 'Generic Product', states={
# 'invisible': (Eval('channel_name') != 'shopify'),
'readonly': (Eval('state') != 'draft')
})
report = fields.Many2One('ir.action.report', 'Report', states=STATES)
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice',
states=STATES)
seller_id = fields.Char('Seller ID', states={
'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={
'invisible': (Eval('channel_name') != 'shopify'),
})
password_api = fields.Char('Api Password', states={
'invisible': (Eval('channel_name') != 'shopify'),
})
host_name = fields.Char('Host Name', states={
'invisible': And(
Eval('channel_name') != 'shopify',
Eval('channel_name') != 'rappi'),
})
party = fields.Many2One('party.party', 'party', required=False)
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term')
@classmethod
def __setup__(cls):
super(SaleWebChannel, cls).__setup__()
cls._transitions |= set((
('draft', 'active'),
('active', 'draft'),
('active', 'finished'),
('finished', 'active'),
))
cls._buttons.update({
'draft': {
'invisible': Eval('state') != 'active',
},
'active': {
'invisible': Eval('state') == 'active',
},
'finished': {
'invisible': Eval('state') != 'active',
},
'refresh_token_b': {
'invisible': And(Eval('channel_name') != 'mercadolibre', Eval('channel_name') != 'rappi'),
},
'synchronize_menu': {
'invisible': And(Eval('channel_name') != 'rappi', Eval('state') == 'active'),
},
})
@staticmethod
def default_company():
return Transaction().context.get('company')
# @classmethod
# def import_data(cls, fields_names, data):
# return 0
@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
@classmethod
@ModelView.button
def synchronize_menu(cls, records):
for record in records:
if record.channel_name == 'rappi':
record.synchronize_menu_rappi()
@classmethod
@ModelView.button
def refresh_token_b(cls, records):
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'),
])
res = channels[0]._validate_token()
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!')
def send_mail_notification(self, message):
Template = Pool().get('email.template')
setattr(self, 'message', message)
response = Template.send(self.template_notification, self)
def get_status_token(self, name):
if self.creation_time:
now = datetime.now()
res = (now - self.creation_time).total_seconds()
if res >= 21600:
return 'expired'
else:
return 'active'
else:
return 'expired'
@classmethod
def request_api(cls, data):
return {}
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
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('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)
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'),
})
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.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)
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:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
print(result)
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()
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.channel_name == 'shopify':
Shopify = pool.get('sale.web_channel.shopify')
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 = channel._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 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')
Shopify = pool.get('sale.web_channel.shopify')
MercadoLibre = pool.get('sale.web_channel.mercado_libre')
if self.start.channel == 'mercadolibre':
channel = MercadoLibre(self.start.channel)
if self.start.channel == 'shopify':
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])
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])
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'
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'