Add job to download sales
This commit is contained in:
parent
a0febb716e
commit
8d7fa886ec
|
@ -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
2
ir.py
|
@ -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"),
|
||||
])
|
||||
|
|
|
@ -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
152
web.py
|
@ -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)
|
||||
|
|
7
web.xml
7
web.xml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue