Add job to download sales

This commit is contained in:
Sergi Almacellas Abellana 2020-06-05 21:29:07 +02:00
parent a0febb716e
commit 8d7fa886ec
5 changed files with 223 additions and 12 deletions

View File

@ -3,6 +3,7 @@
from trytond.pool import Pool
from . import ir
from . import party
from . import product
from . import web
@ -14,6 +15,8 @@ def register():
ir.Cron,
web.ShopWooCommerceId,
web.Shop,
party.Party,
party.Address,
product.Category,
product.Product,
module='web_shop_woocommerce', type_='model')

2
ir.py
View File

@ -12,4 +12,6 @@ class Cron(metaclass=PoolMeta):
cls.method.selection.extend([
('web.shop|woocommerce_update_products',
"Update WooCommerce Products"),
('web.shop|woocommerce_download_orders',
"Download WooCommerce Orders"),
])

71
party.py Normal file
View File

@ -0,0 +1,71 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from trytond.pool import Pool, PoolMeta
from .web import ShopWooCommerceIdMixin
class Party(ShopWooCommerceIdMixin, metaclass=PoolMeta):
__name__ = 'party.party'
@classmethod
def create_from_woocommerce(cls, shop, values):
pool = Pool()
Address = pool.get('party.address')
Mechanism = pool.get('party.contact_mechanism')
party = cls()
party.name = values['billing'].get('company')
party.addresses = [
Address.create_from_woocommerce(shop, values['billing']),
# XXX: Shipment address
]
if not party.name:
party.name = party.addresses[0].party_name
party.woocommerce_id = values['customer_id']
mechanisms = []
if values['billing'].get('email'):
mechanism = Mechanism()
mechanism.party = party
mechanism.type = 'email'
mechanism.value = mechanism.format_value(
value=values['billing']['email'], type_='email')
mechanisms.append(mechanism)
if values['billing'].get('phone'):
mechanism = Mechanism()
mechanism.party = party
mechanism.type = 'phone'
mechanism.value = mechanism.format_value(
value=values['billing']['phone'], type_='phone')
mechanisms.append(mechanism)
party.contact_mechanisms = mechanisms
party.save()
return party
class Address(metaclass=PoolMeta):
__name__ = 'party.address'
@classmethod
def create_from_woocommerce(cls, shop, values):
pool = Pool()
Country = pool.get('country.country')
Subdivision = pool.get('country.subdivision')
address = cls()
address.party_name = ' '.join(filter(bool, [
values['first_name'], values['last_name']]))
address.street = '\n'.join(filter(bool, [
values['address_1'], values['address_2']]))
address.city = values['city']
address.zip = values['postcode']
country = Country.search([
('code', '=', values['country']),
], limit=1)
if country:
address.country, = country
subdivision = Subdivision.search([
('code', '=', '%s-%s' % (
address.country.code, values['state'])),
], limit=1)
if subdivision:
address.subdivision, = subdivision
return address

152
web.py
View File

@ -1,13 +1,17 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from decimal import Decimal
from woocommerce import API
from trytond.exceptions import UserError
from trytond.i18n import lazy_gettext
from trytond.model import ModelSQL, Unique, fields
from trytond.model import Model, ModelSQL, Unique, fields
from trytond.pyson import Eval
from trytond.pool import PoolMeta, Pool
from trytond.tools import grouped_slice
from trytond.transaction import Transaction
from trytond.modules.product import round_price
class ShopWooCommerceId(ModelSQL):
@ -45,8 +49,7 @@ class ShopWooCommerceIdMixin:
fields.Integer(
lazy_gettext('web_shop_woocommerce.msg_woocommerce_id')),
'get_woocommerce_id',
setter='set_woocommerce_id',
searcher='search_woocommerce_id')
setter='set_woocommerce_id')
@classmethod
def get_woocommerce_id(cls, records, name):
@ -84,9 +87,16 @@ class ShopWooCommerceIdMixin:
WooCommerceId.write(woo_ids, {'woocommerce_id': value})
@classmethod
def search_woocommerce_id(cls, name, clause):
# TODO: Implement it
return ['id', 'in', -1]
def delete(cls, records):
pool = Pool()
WooCommerceId = pool.get('web.shop.woocommerce_id')
for sub_records in grouped_slice(records):
woo_ids = WooCommerceId.search([
('record', 'in', [str(r) for r in sub_records]),
])
if woo_ids:
WooCommerceId.delete(woo_ids)
super().delete(records)
class Shop(metaclass=PoolMeta):
@ -153,6 +163,24 @@ class Shop(metaclass=PoolMeta):
raise Exception(response['message'])
return response
def woocommerce_tryton_record(self, model, woocommerce_id):
"Return the tryton record of a giveen woocommerce id"
pool = Pool()
WooCommerceID = pool.get('web.shop.woocommerce_id')
if issubclass(model, Model):
model = model.__name__
# TODO: Cache?
table = WooCommerceID.__table__()
query = table.select(table.id,
where=(table.record.like(model + '%%')
& (table.shop == self.id)
& (table.woocommerce_id == woocommerce_id)))
records = WooCommerceID.search([('id', 'in', query)], limit=1)
if records:
return records[0].record
return None
def woocommerce_sync_records(self, Model, records, endpoint):
wcapi = self.get_woocommerce_api()
to_update = {}
@ -238,12 +266,8 @@ class Shop(metaclass=PoolMeta):
def woocommerce_update_products(cls, shops=None):
pool = Pool()
Product = pool.get('product.product')
ProductTemplate = pool.get('product.template')
Category = pool.get('product.category')
try:
ProductAttribute = pool.get('product.attribute')
except KeyError:
ProductAttribute = None
if shops is None:
shops = cls.search([
('type', '=', 'woocommerce'),
@ -266,3 +290,109 @@ class Shop(metaclass=PoolMeta):
shop.categories_removed = []
cls.save(shops)
def woocommerce_orders_params(self, page):
# XXX: Save last sync date and use that for filtering?
return {
'status': 'on-hold',
'page': page,
}
def woocommerce_customer(self, order):
pool = Pool()
Party = pool.get('party.party')
customer_id = order.get('customer_id', 0)
email = order.get('billing', {}).get('email', '')
if customer_id != 0:
party = self.woocommerce_tryton_record(Party, customer_id)
if party:
return party
elif email:
parties = Party.search([
('contact_mechanisms', 'where', [
('type', '=', 'email'),
('value', '=', email),
]),
], limit=1)
if parties:
return parties[0]
return Party.create_from_woocommerce(self, order)
def woocommerce_sale(self, order):
pool = Pool()
Sale = pool.get('sale.sale')
Currency = pool.get('currency.currency')
sale = Sale()
sale.company = self.company
sale.web_shop = self
sale.web_id = order['id']
sale.reference = order['number']
currencies = Currency.search([
('code', '=', order['currency'])
], limit=1)
if not currencies:
currencies = Currency.search([
('symbol', '=', order['currency_symbol'])
], limit=1)
if not currencies:
raise UserError('missing currency')
sale.currency, = currencies
sale.party = self.woocommerce_customer(order)
# XXX: Addresses
sale.on_change_party()
lines = []
for item in order['line_items']:
line = self.woocommerce_sale_line(order, item, sale)
if line:
lines.append(line)
sale.lines = lines
return sale
def woocommerce_sale_line(self, order, item, sale):
pool = Pool()
Product = pool.get('product.product')
Line = pool.get('sale.line')
line = Line()
line.type = 'line'
line.sale = sale
line.product = self.woocommerce_tryton_record(
Product, item['product_id'])
line.quantity = item['quantity']
line.on_change_product()
line.unit_price = round_price(Decimal(str(item['price'])))
return line
@classmethod
def woocommerce_download_orders(cls, shops=None):
pool = Pool()
Sale = pool.get('sale.sale')
if shops is None:
shops = cls.search([
('type', '=', 'woocommerce'),
])
cls.lock(shops)
sales = []
for shop in shops:
with Transaction().set_context(woocommerce_shop=shop.id):
wcapi = shop.get_woocommerce_api()
page = 1
orders = shop.woocommerce_response(
wcapi.get(
'orders',
params=shop.woocommerce_orders_params(page)))
while orders:
for order in orders:
sale = shop.woocommerce_sale(order)
if sale:
sales.append(sale)
page += 1
orders = shop.woocommerce_response(
wcapi.get(
'orders',
params=shop.woocommerce_orders_params(page)))
# Store last updated date
Sale.save(sales)
cls.save(shops)

View File

@ -9,10 +9,15 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">shop_form</field>
</record>
<record model="ir.cron" id="cron_update">
<record model="ir.cron" id="cron_update_products">
<field name="method">web.shop|woocommerce_update_products</field>
<field name="interval_number" eval="1"/>
<field name="interval_type">days</field>
</record>
<record model="ir.cron" id="cron_download_orders">
<field name="method">web.shop|woocommerce_download_orders</field>
<field name="interval_number" eval="15"/>
<field name="interval_type">minutes</field>
</record>
</data>
</tryton>