fix in mercado_libre

This commit is contained in:
Elvis 2023-11-28 17:23:11 -05:00
parent 834530c797
commit b59092a369
10 changed files with 828 additions and 234 deletions

View File

@ -0,0 +1,4 @@
ALTER TABLE sale_web_channel RENAME TO web_shop;
ALTER TABLE web_shop RENAME COLUMN creation_time TO token_create_date;
ALTER TABLE web_shop RENAME COLUMN channel_name TO type;

View File

@ -9,24 +9,24 @@ from . import shopify
from . import rappi
from . import api_log
from . import ir
from . import routes
# from . import routes
from . import shop
from . import web
__all__ = ['register', 'routes']
# __all__ = ['register', 'routes']
__all__ = ['register']
def register():
Pool.register(
web_channel.SaleWebChannel,
mercado_libre.MercadoLibre,
# web_channel.SaleWebChannel,
web.Shop,
# shopify.Shopify,
sale.Sale,
# sale.SaleForChannelStart,
# web_channel.SynchronizeChannelOrdersStart,
# web_channel.SynchronizeChannelOrdersDone,
sale.SaleForChannelStart,
web.SynchronizeChannelOrdersStart,
web.SynchronizeChannelOrdersDone,
# web_channel.FinishInvoicesStart,
party.Party,
api_log.ApiLog,
@ -34,11 +34,11 @@ def register():
module='sale_web_channel', type_='model')
Pool.register(
sale.SaleUploadInvoice,
# sale.SaleForChannel,
# web_channel.SynchronizeChannelOrders,
sale.SaleForChannel,
web.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

@ -8,11 +8,13 @@ from trytond.transaction import Transaction
import requests
from urllib.parse import urlencode
from datetime import datetime, date
from .web_channel import SaleWebChannel
# from .web_channel import SaleWebChannel
# from .web import Shop
import json
from .exceptions import NotProductFoundError, NotProductFound
from trytond.i18n import gettext
from trytond.exceptions import UserError
from rich import print
HEADERS = {
'Accept': 'application/json',
@ -20,13 +22,575 @@ HEADERS = {
}
class MercadoLibre(SaleWebChannel):
'MercadoLibre'
__name__ = 'sale.web_channel.mercado_libre'
# class MercadoLibreTwo(Shop):
# 'MercadoLibre'
# __name__ = 'sale.web_channel.mercado_libre'
@classmethod
def __setup__(cls):
super(MercadoLibre, cls).__setup__()
# @classmethod
# def __setup__(cls):
# super(MercadoLibre, cls).__setup__()
# def _get_context(self):
# user_ = self._get_user()
# return {
# 'company': user_.company.id,
# 'user': user_.id,
# 'shops': [user_.shop.id],
# 'shop': user_.shop.id,
# 'language': 'es'
# }
# def _get_user(self):
# User = Pool().get('res.user')
# user, = User.search([
# ('login', '=', 'mercado.libre')
# ])
# return user
# def _create_product(self, codes, line={}):
# item = line['item']
# pool = Pool()
# Product = pool.get('product.product')
# Template = pool.get('product.template')
# description = ''
# if item.get('variation_attributes'):
# var = item['variation_attributes'][0]
# if var['id'] and var['name']:
# description = var['id'] + ' ' + var['name']
# create_template = {
# 'name': item['title'],
# 'list_price': line['unit_price'],
# 'sale_price_w_tax': line['full_unit_price'],
# 'type': 'goods',
# 'salable': True,
# 'purchasable': True,
# 'purchase_uom': 1,
# 'sale_uom': 1,
# 'default_uom': 1,
# 'account_category': 7,
# }
# template, = Template.create([create_template])
# create_product = []
# for code in codes:
# create_product.append({
# 'code': code,
# 'description': description,
# 'template': template.id
# })
# return Product.create(create_product)
# def _create_party(self, customer):
# _pool = Pool()
# City = _pool.get('party.city_code')
# Party = _pool.get('party.party')
# PartyObligationTax = _pool.get('party.obligation_tax')
# email = customer.get('email', '@')
# shipment_address, phone, city = '', '', None
# if customer.get('receiver_address'):
# receiver_address = customer['receiver_address']
# shipment_address = receiver_address['address_line']
# phone = receiver_address['receiver_phone'] or '000'
# city_name = receiver_address['city']['name']
# if city_name:
# cities = City.search([
# ('name', '=', city_name)
# ])
# if cities:
# city = cities[0]
# if customer['billing_info']['doc_type'] == 'NIT':
# type_document = '31'
# customer_name = customer['billing_info']['business_name']
# type_person = 'persona_juridica'
# else:
# customer_name = customer['billing_info']['first_name'] + ' ' + customer['billing_info']['last_name']
# type_document = '13'
# type_person = 'persona_natural'
# create_customer = {
# 'id_reference': str(customer['id']),
# 'name': customer_name.upper(),
# 'type_document': type_document,
# 'type_person': type_person,
# 'id_number': customer['billing_info']['doc_number'],
# 'addresses': [('create', [{
# 'street': shipment_address,
# }])],
# 'contact_mechanisms': [
# ('create', [
# {'type': 'phone', 'value': phone},
# {'type': 'email', 'value': email},
# ])
# ]
# }
# if not city:
# city = City(149)
# create_customer['addresses'][0][1][0]['department_code'] = city.department.id
# create_customer['addresses'][0][1][0]['city_code'] = city.id
# party, = Party.create([create_customer])
# PartyObligationTax.create([{
# 'party': party.id,
# 'obligation_fiscal': 6,
# }])
# return party
# def get_shipment_api(self, shipment_id):
# if not shipment_id:
# return None
# URI = 'https://api.mercadolibre.com/shipments/%s?access_token=%s' % (
# shipment_id, self.access_token)
# res = self.get_response(URI)
# return res.json()
# def get_billing_info_api(self, order_id):
# if not order_id:
# return None
# URI = 'https://api.mercadolibre.com/orders/%s/billing_info?access_token=%s' % (
# order_id, self.access_token)
# res = self.get_response(URI)
# return res.json()
# def _return_sale(self, sale):
# pool = Pool()
# Sale = pool.get('sale.sale')
# Date = pool.get('ir.date')
# ctx = self._get_context()
# dev_sales = []
# sales = [sale]
# if sale.pack_id:
# sales = Sale.search([
# ('pack_id', '=', sale.pack_id)
# ])
# with Transaction().set_context(ctx):
# return_sales = Sale.copy(sales)
# for return_sale, sale in zip(return_sales, sales):
# return_sale.origin = sale
# return_sale.reference = sale.reference
# return_sale.state = 'draft'
# return_sale.sale_date = Date.today()
# if sale.invoice_type == '1':
# return_sale.invoice_type = '91'
# for line in return_sale.lines:
# if line.type == 'line':
# line.quantity *= -1
# line.save()
# if return_sale.untaxed_amount_cache:
# return_sale.untaxed_amount_cache *= -1
# if return_sale.tax_amount_cache:
# return_sale.tax_amount_cache *= -1
# if return_sale.total_amount_cache:
# return_sale.total_amount_cache *= -1
# return_sale.save()
# Sale.quote([return_sale])
# Sale.write([return_sale], {'state': 'confirmed'})
# dev_sales.append(return_sale)
# return dev_sales
# def create_lines_sale(self, sale, sale_, freight=False):
# _pool = Pool()
# SaleLine = _pool.get('sale.line')
# Product = _pool.get('product.product')
# create_lines = []
# sale_items = sale_['order_items']
# ctx = self._get_context()
# sfm_id = sale_['shipping']['id']
# # reference = sale.reference.split(',')
# # reference_order = str(sale_['id'])
# # if reference_order not in reference:
# # sale.referece = sale.reference + ',' + reference_order
# # sale.save()
# with Transaction().set_context(ctx):
# for line in sale_items:
# item = line['item']
# sku_code = item['seller_sku']
# if sku_code:
# generic = False
# if sku_code.count('+') > 0:
# codes = sku_code.split('+')
# line['unit_price'] = Decimal(
# round((line['unit_price'] / 2), 2))
# else:
# codes = [sku_code]
# products = Product.search([
# ('code', 'in', codes),
# ('active', '=', True)
# ])
# description = ''
# if not products:
# products = self._create_product(codes, line)
# else:
# if not self.generic_product:
# raise NotProductFoundError(
# gettext('sale_web_channel.msg_product_generic_not_found'))
# products = [self.generic_product]
# generic = True
# description = ''
# raise NotProductFound(
# gettext('sale_web_channel.msg_product_not_found', s=self.generic_product.rec_name))
# for product in products:
# Tax = _pool.get('account.tax')
# un_price = Tax.reverse_compute(Decimal(line['unit_price']),
# product.customer_taxes_used)
# create_lines.append({
# 'sale': sale.id,
# 'type': 'line',
# 'unit': product.default_uom.id,
# 'quantity': line['quantity'],
# 'unit_price': round(Decimal(un_price), 3),
# 'unit_price_full': Decimal(line['unit_price']),
# 'product': product.id,
# 'taxes': [('add', product.customer_taxes_used)],
# 'description': description,
# })
# if freight and self.freight_product:
# product = self.freight_product
# URI = 'https://api.mercadolibre.com/shipments/%s/costs?access_token=%s' % (
# sfm_id, self.access_token)
# result = self.get_response(URI).json()
# shipping_amount = result['receiver']['cost']
# create_lines.append({
# 'sale': sale.id,
# 'type': 'line',
# 'unit': product.default_uom.id,
# 'quantity': 1,
# 'unit_price': Decimal(shipping_amount),
# 'unit_price_full': Decimal(shipping_amount),
# 'product': product.id,
# 'description': 'FLETE',
# })
# SaleLine.create(create_lines)
# return generic
# def validate_sale(self, sale_):
# if sale_.get('pack_id'):
# # if not sale_.get('shipping'):
# # return
# # shipment_id = sale_['shipping']['id']
# # URI = 'https://api.mercadolibre.com/shipments/%s/items?access_token=%s' % (
# # shipment_id, self.access_token)
# # res = self.get_response(URI)
# # shipment_items = res.json()
# # if len(shipment_items) > 1:
# URI = 'https://api.mercadolibre.com/packs/%s?access_token=%s' % (
# sale_.get('pack_id'), self.access_token)
# response = self.get_response(URI)
# response = response.json()
# if response.get('orders'):
# ids = ''
# for item in response['orders']:
# order_id = str(item['id'])
# if order_id != str(sale_['id']):
# URI2 = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
# order_id, self.access_token)
# order = self.get_response(URI2)
# sale_order = order.json()
# ids += ',' + order_id
# sale_['order_items'].extend(sale_order['order_items'])
# sale_['id'] = str(sale_['id']) + ids
# return self._create_sale(sale_)
# else:
# return self._create_sale(sale_)
# else:
# return self._create_sale(sale_)
# def _create_sale(self, sale_):
# _pool = Pool()
# Sale = _pool.get('sale.sale')
# print(sale_, 'que eres amigo')
# reference = '%' + str(sale_['id']).split(',')[0] + '%'
# dom = [
# ('reference', 'like', reference)
# ]
# sales = Sale.search(dom)
# if sales:
# if len(sales) == 1 and sale_['status'] == 'cancelled':
# sale = sales[0]
# if not sale.invoices:
# self.cancel_sales([sale], sale.pack_id)
# else:
# dev_sales = self._return_sale(sale)
# self._finish_sale(dev_sales[0], type='return')
# return True
# return False
# Party = _pool.get('party.party')
# User = _pool.get('res.user')
# sfm_id = sale_['shipping']['id']
# shipment_ = self.get_shipment_api(sfm_id)
# if sale_.get('buyer'):
# sale_id = str(sale_['id']).split(',')[0]
# billing_info = self.get_billing_info_api(sale_id)
# customer = sale_['buyer']
# dom_party = [('id_reference', '=', str(customer['id']))]
# if billing_info and billing_info['billing_info'].get('doc_number'):
# dom_party = [('id_number', '=', str(
# billing_info['billing_info']['doc_number']))]
# parties = Party.search(dom_party)
# if parties:
# party = parties[0]
# else:
# customer['receiver_address'] = shipment_['receiver_address']
# customer['billing_info'] = billing_info['billing_info']
# for dic in customer['billing_info']['additional_info']:
# customer['billing_info'][list(dic.values())[0].lower()
# ] = list(dic.values())[1]
# try:
# customer['billing_info']['additional_info'].pop()
# except Exception as e:
# msg = 'sin informacion de tercero' + e
# raise UserError(
# gettext('sale_web_channel.msg_billing_info', msg=msg))
# party = self._create_party(customer)
# ctx = self._get_context()
# user_ = User(ctx['user'])
# date_created = sale_['date_created'].split('T')
# year, month, day = date_created[0].split('-')
# sale_date = date(int(year), int(month), int(day))
# with Transaction().set_user(ctx['user']):
# sale, = Sale.create([{
# 'payment_term': 1,
# 'party': party.id,
# 'sale_date': sale_date,
# 'comment': shipment_['status'],
# 'state': 'draft',
# 'company': ctx['company'],
# 'currency': user_.company.currency.id,
# 'shop': user_.shop.id,
# 'reference': str(sale_['id']),
# 'invoice_address': Party.address_get(party, type='invoice'),
# 'shipment_address': Party.address_get(party, type='delivery'),
# 'description': 'VENTA WEB ',
# 'channel': self.id,
# 'invoice_type': self.invoice_type,
# 'pack_id': str(sale_['pack_id']) if sale_['pack_id'] else '',
# }])
# generic = self.create_lines_sale(
# sale, sale_, freight=True)
# sale.untaxed_amount_cache = sale.untaxed_amount
# if sale_['status'] == 'cancelled':
# sale.state = 'cancelled'
# else:
# Sale.quote([sale])
# if not generic:
# sale.state = 'confirmed'
# sale.save()
# # if shipment_['status'] in ['shipped', 'delivered']:
# # self._finish_sale(sale)
# return sale
# def cancel_sales(self, sales, pack_id=None):
# Sale = Pool().get('sale.sale')
# if pack_id:
# sales = Sale.search([('pack_id', '=', pack_id)])
# for sale in sales:
# sale.state = 'cancelled'
# sale.save()
# def _get_shipment_amount(self, order_id, shipment={}):
# URI = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
# order_id, self.access_token)
# res = self.get_response(URI)
# sale_ = res.json()
# shipping_amount = 0
# if sale_.get('payments'):
# shipping_amount = sale_['payments'][0]['shipping_cost']
# return shipping_amount
# def get_access_token(self, refresh_token, client_id, client_secret):
# params = {'grant_type': 'refresh_token',
# 'client_id': client_id,
# 'client_secret': client_secret,
# 'refresh_token': refresh_token
# }
# uri = 'https://api.mercadolibre.com/oauth/token'
# response = requests.post(uri, params=urlencode(params),
# headers=HEADERS, data=params)
# res = response.json()
# if res.get('error', None):
# raise UserError(res.get('message') + ' Error:' + res.get('error'))
# return res['access_token'], res['refresh_token']
# @classmethod
# def get_response(cls, URI, params={}):
# response = requests.get(URI, headers=HEADERS, params=urlencode(params))
# print(response)
# return response
# def _validate_token(self):
# access_token = self.access_token
# refresh_token = self.refresh_token
# client_id = self.app_id
# client_secret = self.secret_key
# if self.status_token != 'active':
# access_token, refresh_token = self.get_access_token(
# refresh_token, client_id, client_secret
# )
# self.write([self], {'access_token': access_token,
# 'refresh_token': refresh_token,
# 'creation_time': datetime.now()
# })
# return access_token, refresh_token, client_id, client_secret
# @classmethod
# def _get_channel(cls):
# channels = cls.search([
# ('state', '=', 'active'),
# ('channel_name', '=', 'mercadolibre'),
# ])
# if channels:
# return channels[0]
# else:
# return None
# @classmethod
# def request_api(cls, data):
# channel = cls._get_channel()
# response = {'status': 'error', 'msg_response': 'Fail in process !!!'}
# if not channel:
# return response
# access_token, refresh_token, client_id, client_secret = channel._validate_token()
# if data.get('resource') and data['resource'].count('orders'):
# order_id = str(data['resource'].replace('/orders/', ''))
# URI = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
# order_id, access_token)
# result = cls.get_response(URI).json()
# res = channel.validate_sale(result)
# if res:
# response = {
# 'status': 'ok',
# 'msg_response': 'Successfull process !!!',
# 'order': order_id
# }
# else:
# response = {
# 'status': 'ok',
# 'msg_response': 'Sale processed before !!!',
# 'order': order_id
# }
# elif data.get('resource') and data['resource'].count('shipments'):
# shipment_id = str(data['resource'].replace('/shipments/', ''))
# shipment_ = channel.get_shipment_api(shipment_id)
# order_id = shipment_['order_id']
# Sale = Pool().get('sale.sale')
# sales = Sale.search([
# ('reference', 'ilike', '%' + str(order_id) + '%')
# ])
# response.update({'order': order_id})
# if not sales:
# return response
# sale = sales[0]
# if sale.invoices:
# return response
# if len(sales) > 1:
# channel.upload_note(
# sale, 'Error, al generar factura orden duplicada')
# return response
# if shipment_.get('tracking_number'):
# Sale.write([sale], {
# 'description': 'GUIA DE ENVIO NO. ' + shipment_['tracking_number'],
# 'tracking_number': shipment_['tracking_number']
# })
# if shipment_['status'] in ['shipped', 'delivered']:
# try:
# channel._finish_sale(sale)
# except:
# channel.upload_note(sale, 'Error al finalizar factura')
# response = {'status': 'ok',
# 'msg_response': 'Successfull process !!!',
# 'order': order_id}
# else:
# return {'status': 'none', 'msg_response': 'Don`t process this topic'}
# return response
# def _finish_sale(self, sale, type='invoice'):
# ctx = self._get_context()
# pool = Pool()
# Sale = pool.get('sale.sale')
# Invoice = pool.get('account.invoice')
# Date = pool.get('ir.date')
# with Transaction().set_context(ctx):
# Sale.process([sale])
# if not sale.invoices:
# return
# invoice = sale.invoices[0]
# invoice.invoice_date = Date.today()
# if type == 'return':
# sale_origin = sale.origin
# if sale_origin.invoices:
# inv_origin = sale_origin.invoices[0]
# invoice.credit_note_concept = '2'
# invoice.original_invoice = inv_origin.id
# invoice.save()
# Invoice.validate_invoice([invoice])
# if invoice.invoice_type not in ('C', 'P', 'M'):
# try:
# invoice.submit([invoice])
# if not invoice.cufe:
# return
# except:
# self.upload_note(sale, 'Error de envio DIAN')
# try:
# Invoice.post([invoice])
# self.upload_note(sale, 'Factura generada')
# self.upload_invoice(sale)
# except:
# pass
# def upload_invoice(self, sale):
# if not sale.reference or not sale.invoices:
# return
# invoice = sale.invoices[0]
# pack_id = sale.reference
# if sale.pack_id:
# pack_id = sale.pack_id
# URI = 'https://api.mercadolibre.com/packs/%s/fiscal_documents?access_token=%s' % (
# pack_id, self.access_token)
# if sale.uploaded_invoice:
# response = requests.delete(URI)
# report = self.render_report(invoice)
# file = {"fiscal_document": report}
# response = requests.post(URI, files=file)
# print(response.status_code)
# message = 'Error al subir factura'
# if response.status_code in [200, 201, 202]:
# res = response.json()
# upload_ids = 'Upload ids: ' + ', '.join(list(res['ids']))
# sale.write([sale], {
# 'uploaded_invoice': True,
# 'document_invoice': upload_ids,
# })
# message = 'Factura Cargada exitosamente'
# self.upload_note(sale, message)
# def upload_note(self, sale, message):
# URI = 'https://api.mercadolibre.com/orders/%s/notes?access_token=%s' % (
# sale.reference, self.access_token)
# params = {"note": message}
# request = json.dumps(params)
# response = requests.post(URI, headers=HEADERS, data=request)
# if not response.status_code in [200, 201, 202]:
# self.send_mail_notification(
# 'error al crear nota en orden ' + sale.reference)
class MercadoLibre:
'MercadoLibre'
def __init__(self, web_shop):
self.access_token = web_shop.access_token if web_shop.access_token else ''
self.generic_product = web_shop.generic_product
self.refresh_token = web_shop.refresh_token
self.id = web_shop.id
self.app_id = web_shop.app_id
self.secret_key = web_shop.secret_key
def _get_context(self):
user_ = self._get_user()
@ -38,13 +602,6 @@ class MercadoLibre(SaleWebChannel):
'language': 'es'
}
def _get_user(self):
User = Pool().get('res.user')
user, = User.search([
('login', '=', 'mercado.libre')
])
return user
def _create_product(self, codes, line={}):
item = line['item']
pool = Pool()
@ -262,14 +819,6 @@ class MercadoLibre(SaleWebChannel):
def validate_sale(self, sale_):
if sale_.get('pack_id'):
# if not sale_.get('shipping'):
# return
# shipment_id = sale_['shipping']['id']
# URI = 'https://api.mercadolibre.com/shipments/%s/items?access_token=%s' % (
# shipment_id, self.access_token)
# res = self.get_response(URI)
# shipment_items = res.json()
# if len(shipment_items) > 1:
URI = 'https://api.mercadolibre.com/packs/%s?access_token=%s' % (
sale_.get('pack_id'), self.access_token)
response = self.get_response(URI)
@ -285,17 +834,22 @@ class MercadoLibre(SaleWebChannel):
sale_order = order.json()
ids += ',' + order_id
sale_['order_items'].extend(sale_order['order_items'])
# sale_['id'] = str(sale_['id']) + ids
sale_['id'] = str(sale_['id']) + ids
return self._create_sale(sale_)
else:
return self._create_sale(sale_)
else:
print(sale_, 'que carajos')
return self._create_sale(sale_)
def _create_sale(self, sale_):
_pool = Pool()
Sale = _pool.get('sale.sale')
reference = '%' + str(sale_['id']).split(',')[0] + '%'
# reference = '%' + str(sale_['id']).split(',')[0] + '%'
ids = [str(order['id']) for order in sale_['orders']]
reference = ', '.join(ids)
print(reference, 'esta es mi referencia')
dom = [
('reference', 'like', reference)
]
@ -357,7 +911,7 @@ class MercadoLibre(SaleWebChannel):
'invoice_address': Party.address_get(party, type='invoice'),
'shipment_address': Party.address_get(party, type='delivery'),
'description': 'VENTA WEB ',
'channel': self.id,
'web': self.id,
'invoice_type': self.invoice_type,
'pack_id': str(sale_['pack_id']) if sale_['pack_id'] else '',
}])
@ -375,23 +929,10 @@ class MercadoLibre(SaleWebChannel):
# self._finish_sale(sale)
return sale
def cancel_sales(self, sales, pack_id=None):
Sale = Pool().get('sale.sale')
if pack_id:
sales = Sale.search([('pack_id', '=', pack_id)])
for sale in sales:
sale.state = 'cancelled'
sale.save()
def _get_shipment_amount(self, order_id, shipment={}):
URI = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
order_id, self.access_token)
res = self.get_response(URI)
sale_ = res.json()
shipping_amount = 0
if sale_.get('payments'):
shipping_amount = sale_['payments'][0]['shipping_cost']
return shipping_amount
@classmethod
def get_response(cls, URI, params={}):
response = requests.get(URI, headers=HEADERS, params=urlencode(params))
return response
def get_access_token(self, refresh_token, client_id, client_secret):
params = {'grant_type': 'refresh_token',
@ -399,9 +940,7 @@ class MercadoLibre(SaleWebChannel):
'client_secret': client_secret,
'refresh_token': refresh_token
}
uri = 'https://api.mercadolibre.com/oauth/token'
response = requests.post(uri, params=urlencode(params),
headers=HEADERS, data=params)
res = response.json()
@ -409,170 +948,13 @@ class MercadoLibre(SaleWebChannel):
raise UserError(res.get('message') + ' Error:' + res.get('error'))
return res['access_token'], res['refresh_token']
@classmethod
def get_response(cls, URI, params={}):
response = requests.get(URI, headers=HEADERS, params=urlencode(params))
return response
def _validate_token(self):
access_token = self.access_token
refresh_token = self.refresh_token
client_id = self.app_id
client_secret = self.secret_key
if self.status_token != 'active':
access_token, refresh_token = self.get_access_token(
refresh_token, client_id, client_secret
)
self.write([self], {'access_token': access_token,
'refresh_token': refresh_token,
'creation_time': datetime.now()
})
return access_token, refresh_token, client_id, client_secret
@classmethod
def _get_channel(cls):
channels = cls.search([
('state', '=', 'active'),
('channel_name', '=', 'mercadolibre'),
])
if channels:
return channels[0]
else:
return None
@classmethod
def request_api(cls, data):
channel = cls._get_channel()
response = {'status': 'error', 'msg_response': 'Fail in process !!!'}
if not channel:
return response
access_token, refresh_token, client_id, client_secret = channel._validate_token()
if data.get('resource') and data['resource'].count('orders'):
order_id = str(data['resource'].replace('/orders/', ''))
URI = 'https://api.mercadolibre.com/orders/%s?access_token=%s' % (
order_id, access_token)
result = cls.get_response(URI).json()
res = channel.validate_sale(result)
if res:
response = {
'status': 'ok',
'msg_response': 'Successfull process !!!',
'order': order_id
}
else:
response = {
'status': 'ok',
'msg_response': 'Sale processed before !!!',
'order': order_id
}
elif data.get('resource') and data['resource'].count('shipments'):
shipment_id = str(data['resource'].replace('/shipments/', ''))
shipment_ = channel.get_shipment_api(shipment_id)
order_id = shipment_['order_id']
Sale = Pool().get('sale.sale')
sales = Sale.search([
('reference', 'ilike', '%' + str(order_id) + '%')
])
response.update({'order': order_id})
if not sales:
return response
sale = sales[0]
if sale.invoices:
return response
if len(sales) > 1:
channel.upload_note(
sale, 'Error, al generar factura orden duplicada')
return response
if shipment_.get('tracking_number'):
Sale.write([sale], {
'description': 'GUIA DE ENVIO NO. ' + shipment_['tracking_number'],
'tracking_number': shipment_['tracking_number']
})
if shipment_['status'] in ['shipped', 'delivered']:
try:
channel._finish_sale(sale)
except:
channel.upload_note(sale, 'Error al finalizar factura')
response = {'status': 'ok',
'msg_response': 'Successfull process !!!',
'order': order_id}
else:
return {'status': 'none', 'msg_response': 'Don`t process this topic'}
return response
def _finish_sale(self, sale, type='invoice'):
ctx = self._get_context()
pool = Pool()
Sale = pool.get('sale.sale')
Invoice = pool.get('account.invoice')
Date = pool.get('ir.date')
with Transaction().set_context(ctx):
Sale.process([sale])
if not sale.invoices:
return
invoice = sale.invoices[0]
invoice.invoice_date = Date.today()
if type == 'return':
sale_origin = sale.origin
if sale_origin.invoices:
inv_origin = sale_origin.invoices[0]
invoice.credit_note_concept = '2'
invoice.original_invoice = inv_origin.id
invoice.save()
Invoice.validate_invoice([invoice])
if invoice.invoice_type not in ('C', 'P', 'M'):
try:
invoice.submit([invoice])
if not invoice.cufe:
return
except:
self.upload_note(sale, 'Error de envio DIAN')
try:
Invoice.post([invoice])
self.upload_note(sale, 'Factura generada')
self.upload_invoice(sale)
except:
pass
def upload_invoice(self, sale):
if not sale.reference or not sale.invoices:
return
invoice = sale.invoices[0]
pack_id = sale.reference
if sale.pack_id:
pack_id = sale.pack_id
URI = 'https://api.mercadolibre.com/packs/%s/fiscal_documents?access_token=%s' % (
pack_id, self.access_token)
if sale.uploaded_invoice:
response = requests.delete(URI)
report = self.render_report(invoice)
file = {"fiscal_document": report}
response = requests.post(URI, files=file)
print(response.status_code)
message = 'Error al subir factura'
if response.status_code in [200, 201, 202]:
res = response.json()
upload_ids = 'Upload ids: ' + ', '.join(list(res['ids']))
sale.write([sale], {
'uploaded_invoice': True,
'document_invoice': upload_ids,
})
message = 'Factura Cargada exitosamente'
self.upload_note(sale, message)
def upload_note(self, sale, message):
URI = 'https://api.mercadolibre.com/orders/%s/notes?access_token=%s' % (
sale.reference, self.access_token)
params = {"note": message}
request = json.dumps(params)
response = requests.post(URI, headers=HEADERS, data=request)
if not response.status_code in [200, 201, 202]:
self.send_mail_notification(
'error al crear nota en orden ' + sale.reference)
access_token, refresh_token = self.get_access_token(
refresh_token, client_id, client_secret
)
# return access_token, refresh_token, client_id, client_secret
return access_token

11
sale.py
View File

@ -15,7 +15,7 @@ class Sale(metaclass=PoolMeta):
is_paymented = fields.Function(fields.Boolean('Is Paymented'), 'get_is_paymented')
products_string = fields.Char('Products String')
channel = fields.Many2One('sale.web_channel', 'channel')
# channel = fields.Many2One('sale.web_channel', 'channel')
uploaded_invoice = fields.Boolean('uploaded Invoice', readonly=True)
document_invoice = fields.Char('Document Invoice')
tracking_number = fields.Char('Tracking Number')
@ -57,10 +57,11 @@ class Sale(metaclass=PoolMeta):
def quote(cls, sales):
super(Sale, cls).quote(sales)
for sale in sales:
if sale.web_shop.type == 'rappi':
print('si entra aqui en quote')
rappi_rec = rappi.Rappi(sale.web_shop)
rappi_rec.take_order(sale.web_id, 20)
# if sale.web_shop.type == 'rappi':
# print('si entra aqui en quote')
# rappi_rec = rappi.Rappi(sale.web_shop)
# rappi_rec.take_order(sale.web_id, 20)
pass
@classmethod
def confirm(cls, sales):

View File

@ -4,8 +4,8 @@ this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/notebook/page[@id='other']/field[@name='company']"
position="after">
<label name="channel"/>
<field name="channel"/>
<!-- <label name="channel"/>
<field name="channel"/> -->
<label name="uploaded_invoice"/>
<field name="uploaded_invoice"/>
<label name="tracking_number"/>

View File

@ -4,7 +4,7 @@ this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='state']"
position="before">
<field name="channel"/>
<!-- <field name="channel"/> -->
<field name="pack_id"/>
<field name="tracking_number"/>
</xpath>

View File

@ -9,7 +9,7 @@ The COPYRIGHT file at the top level of this repository contains the full copyrig
<label name="channel"/>
<field name="channel" widget="selection"/>
<!-- <label name="name_channel"/> -->
<field name="name_channel" invisible="1"/>
<field name="type" invisible="1"/>
<newline/>
<label name="operation"/>
<field name="operation"/>

202
web.py
View File

@ -1,7 +1,12 @@
from trytond.model import fields, ModelView
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, And, Or
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 . import rappi
@ -25,6 +30,14 @@ TYPE_INVOICE = [
('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'
@ -36,6 +49,9 @@ class Shop(metaclass=PoolMeta):
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',
@ -76,6 +92,17 @@ class Shop(metaclass=PoolMeta):
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
@ -112,11 +139,11 @@ class Shop(metaclass=PoolMeta):
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'),
])
# 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']
@ -138,3 +165,166 @@ class Shop(metaclass=PoolMeta):
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
URI = 'https://api.mercadolibre.com/packs/%s?access_token=%s' % (
order_id, channel.access_token)
result = mercado_libre.get_response(URI).json()
print(result, 'este es el result')
if not result:
raise ErrorSynchronizingSale(
gettext('sale_web_channel.msg_error_synchronizing_sale', s=result))
print(result, channel.access_token)
sale_created = mercado_libre.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.type == '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 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'

17
web.xml
View File

@ -8,5 +8,22 @@ this repository contains the full copyright notices and license terms. -->
<field name="inherit" ref="web_shop.shop_view_form"/>
<field name="name">web_shop_form</field>
</record>
<record model="ir.action.wizard" id="synchronize_orders_wizard">
<field name="name">Synchronize Channel Orders</field>
<field name="wiz_name">sale_web_channel.synchronize_orders</field>
</record>
<record model="ir.ui.view" id="synchronize_orders_view_form">
<field name="model">sale_web_channel.synchronize_orders.start</field>
<field name="type">form</field>
<field name="name">synchronize_orders_view_form</field>
</record>
<record model="ir.ui.view" id="synchronize_orders_done">
<field name="model">sale_web_channel.synchronize_orders.done</field>
<field name="type">form</field>
<field name="name">synchronize_orders_done</field>
</record>
<menuitem action="synchronize_orders_wizard" parent="sale.menu_sale"
id="menu_synchronize_orders" sequence="100"/>
</data>
</tryton>

View File

@ -113,10 +113,10 @@ class SaleWebChannel(Workflow, ModelSQL, ModelView):
'invisible': ~Eval('channel_name').in_(['mercadolibre', 'rappi']),
'readonly': (Eval('state') != 'draft')
})
admin_category = fields.Many2One('product.category', 'Admin Category',
domain=[
('accounting', '=', False),
])
# 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')