diff --git a/connector_opencart/__manifest__.py b/connector_opencart/__manifest__.py
index af9c90e5..c31baaec 100644
--- a/connector_opencart/__manifest__.py
+++ b/connector_opencart/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'Opencart Connector',
- 'version': '12.0.1.2.0',
+ 'version': '12.0.1.3.0',
'category': 'Connector',
'depends': [
'account',
diff --git a/connector_opencart/components/api/opencart.py b/connector_opencart/components/api/opencart.py
index f0dfe367..db6487d8 100644
--- a/connector_opencart/components/api/opencart.py
+++ b/connector_opencart/components/api/opencart.py
@@ -4,6 +4,7 @@
import requests
from urllib.parse import urlencode
from json import loads, dumps
+from json.decoder import JSONDecodeError
import logging
_logger = logging.getLogger(__name__)
@@ -52,7 +53,10 @@ class Opencart:
elif method == 'PUT' or method == 'POST':
result_text = self.session.put(url, data=body, headers=headers).text
_logger.debug('raw_text: ' + str(result_text))
- return loads(result_text)
+ try:
+ return loads(result_text)
+ except JSONDecodeError:
+ return {}
class Resource:
@@ -75,10 +79,13 @@ class Orders(Resource):
path = 'orders'
- def all(self, id_larger_than=None):
+ def all(self, id_larger_than=None, modified_from=None):
url = self.url
if id_larger_than:
url += '/id_larger_than/%s' % id_larger_than
+ if modified_from:
+ # TODO remove details if it gets into main route
+ url += 'details/modified_from/%s' % modified_from
return self.connection.send_request(method='GET', url=url)
def get(self, id):
@@ -97,7 +104,6 @@ class Orders(Resource):
))
return res
-
def cancel(self, id):
url = self.connection.base_url + ('order_status/%s' % id)
return self.connection.send_request(method='POST', url=url, body=self.get_status_payload('Canceled'))
diff --git a/connector_opencart/models/opencart/backend.py b/connector_opencart/models/opencart/backend.py
index 0f0aff12..dd1767d3 100644
--- a/connector_opencart/models/opencart/backend.py
+++ b/connector_opencart/models/opencart/backend.py
@@ -4,6 +4,7 @@
from logging import getLogger
from contextlib import contextmanager
+from datetime import timedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
@@ -69,8 +70,17 @@ class OpencartBackend(models.Model):
product_categ_id = fields.Many2one(comodel_name='product.category', string='Product Category',
help='Default product category for newly created products.')
+ # renamed, and not used when searching orders anymore
import_orders_after_id = fields.Integer(
- string='Import sale orders after id',
+ string='Highest Order ID',
+ )
+ # Note that Opencart may not return timestamps in UTC
+ import_orders_after_date = fields.Datetime(
+ string='Import Orders Modified After',
+ )
+ server_offset_hours = fields.Float(
+ string='Opencart Server Timezone Offset',
+ help='E.g. US Pacific is -8.0, the important thing is to either not change this during DST or to adjust the import_orders_after_date field at the same time.',
)
so_require_product_setup = fields.Boolean(string='SO Require Product Setup',
@@ -134,14 +144,16 @@ class OpencartBackend(models.Model):
backends = self.search([
('base_url', '!=', False),
('restadmin_token', '!=', False),
- ('import_orders_after_id', '!=', False),
+ ('import_orders_after_date', '!=', False),
('scheduler_order_import', '=', True),
])
return backends.import_sale_orders()
@api.multi
def import_sale_orders(self):
- self._import_after_id('opencart.sale.order', 'import_orders_after_id')
+ # self._import_after_id('opencart.sale.order', 'import_orders_after_id')
+ # import_orders_after_date
+ self._import_sale_orders_after_date()
return True
@api.multi
@@ -152,3 +164,28 @@ class OpencartBackend(models.Model):
backend,
filters={'after_id': after_id}
)
+
+ @api.multi
+ def _import_sale_orders_after_date(self):
+ for backend in self:
+ date = backend.date_to_opencart(backend.import_orders_after_date)
+ date = str(date).replace(' ', 'T')
+ self.env['opencart.sale.order'].with_delay().import_batch(
+ backend,
+ filters={'modified_from': date}
+ )
+
+ def date_to_opencart(self, date):
+ # date provided should be UTC and will be converted to Opencart's dates
+ return self._date_plus_hours(date, self.server_offset_hours or 0)
+
+ def date_to_odoo(self, date):
+ # date provided should be in Opencart's TZ, converted to UTC
+ return self._date_plus_hours(date, -(self.server_offset_hours or 0))
+
+ def _date_plus_hours(self, date, hours):
+ if not hours:
+ return date
+ if isinstance(date, str):
+ date = fields.Datetime.from_string(date)
+ return date + timedelta(hours=self.server_offset_hours)
diff --git a/connector_opencart/models/sale_order/common.py b/connector_opencart/models/sale_order/common.py
index 2ad4f606..3e91b8f6 100644
--- a/connector_opencart/models/sale_order/common.py
+++ b/connector_opencart/models/sale_order/common.py
@@ -96,7 +96,12 @@ class SaleOrderAdapter(Component):
def search(self, filters=None):
api_instance = self.api_instance
- orders_response = api_instance.orders.all(id_larger_than=filters.get('after_id'))
+ api_filters = {}
+ if 'after_id' in filters:
+ api_filters['id_larger_than'] = filters['after_id']
+ if 'modified_from' in filters:
+ api_filters['modified_from'] = filters['modified_from']
+ orders_response = api_instance.orders.all(**api_filters)
if 'error' in orders_response and orders_response['error']:
raise ValidationError(str(orders_response))
@@ -105,7 +110,7 @@ class SaleOrderAdapter(Component):
orders = orders_response['data']
# Note that `store_id is None` is checked as it may not be in the output.
- return map(lambda o: (o['order_id'], o.get('store_id', None)), orders)
+ return map(lambda o: (o['order_id'], o.get('store_id', None), o.get('date_modified') or o.get('date_added')), orders)
def read(self, id):
api_instance = self.api_instance
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 7d7945a5..fa37610a 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -51,7 +51,11 @@ class SaleOrderBatchImporter(Component):
self._import_record(ids[0], ids[1])
if external_ids:
last_id = list(sorted(external_ids, key=lambda i: i[0]))[-1][0]
- self.backend_record.import_orders_after_id = last_id
+ last_date = list(sorted(external_ids, key=lambda i: i[2]))[-1][2]
+ self.backend_record.write({
+ 'import_orders_after_id': last_id,
+ 'import_orders_after_date': self.backend_record.date_to_odoo(last_date),
+ })
class SaleOrderImportMapper(Component):
@@ -133,7 +137,10 @@ class SaleOrderImportMapper(Component):
@mapping
def date_order(self, record):
- return {'date_order': record.get('date_added', fields.Datetime.now())}
+ date_added = record.get('date_added')
+ if date_added:
+ date_added = self.backend_record.date_to_odoo(date_added)
+ return {'date_order': date_added or fields.Datetime.now()}
@mapping
def fiscal_position_id(self, record):
diff --git a/connector_opencart/views/opencart_backend_views.xml b/connector_opencart/views/opencart_backend_views.xml
index 1b9138fe..be7adaad 100644
--- a/connector_opencart/views/opencart_backend_views.xml
+++ b/connector_opencart/views/opencart_backend_views.xml
@@ -23,6 +23,7 @@