diff --git a/connector_opencart/__manifest__.py b/connector_opencart/__manifest__.py
index ce265249..ddd2c9bc 100644
--- a/connector_opencart/__manifest__.py
+++ b/connector_opencart/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'Opencart Connector',
- 'version': '12.0.1.0.0',
+ 'version': '12.0.1.1.0',
'category': 'Connector',
'depends': [
'account',
@@ -17,9 +17,12 @@
'license': 'AGPL-3',
'website': 'https://hibou.io',
'data': [
+ 'data/connector_opencart_data.xml',
'security/ir.model.access.csv',
'views/delivery_views.xml',
'views/opencart_backend_views.xml',
+ 'views/opencart_product_views.xml',
+ 'views/product_views.xml',
],
'installable': True,
'application': False,
diff --git a/connector_opencart/components/api/opencart.py b/connector_opencart/components/api/opencart.py
index cd52828f..f0dfe367 100644
--- a/connector_opencart/components/api/opencart.py
+++ b/connector_opencart/components/api/opencart.py
@@ -5,6 +5,9 @@ import requests
from urllib.parse import urlencode
from json import loads, dumps
+import logging
+_logger = logging.getLogger(__name__)
+
class Opencart:
@@ -22,6 +25,10 @@ class Opencart:
def stores(self):
return Stores(connection=self)
+ @property
+ def products(self):
+ return Products(connection=self)
+
def get_headers(self, url, method):
headers = {}
if method in ('POST', 'PUT', ):
@@ -33,11 +40,19 @@ class Opencart:
if params:
encoded_url += '?%s' % urlencode(params)
headers = self.get_headers(encoded_url, method)
-
+ _logger.debug('send_request method: %s url: %s headers: %s params: %s body: %s' % (
+ method,
+ url,
+ headers,
+ params,
+ body
+ ))
if method == 'GET':
- return loads(self.session.get(url, params=params, headers=headers).text)
+ result_text = self.session.get(url, params=params, headers=headers).text
elif method == 'PUT' or method == 'POST':
- return loads(self.session.put(url, data=body, headers=headers).text)
+ result_text = self.session.put(url, data=body, headers=headers).text
+ _logger.debug('raw_text: ' + str(result_text))
+ return loads(result_text)
class Resource:
@@ -138,3 +153,14 @@ class Stores(Resource):
def get(self, id):
url = self.url + ('/%s' % id)
return self.connection.send_request(method='GET', url=url)
+
+
+class Products(Resource):
+ """
+ Retrieves Product details
+ """
+ path = 'products'
+
+ def get(self, id):
+ url = self.url + ('/%s' % id)
+ return self.connection.send_request(method='GET', url=url)
diff --git a/connector_opencart/components/binder.py b/connector_opencart/components/binder.py
index d8bbaebd..b2525fea 100644
--- a/connector_opencart/components/binder.py
+++ b/connector_opencart/components/binder.py
@@ -20,4 +20,6 @@ class OpencartModelBinder(Component):
'opencart.sale.order',
'opencart.sale.order.line',
'opencart.stock.picking',
+ 'opencart.product.template',
+ 'opencart.product.template.attribute.value',
]
diff --git a/connector_opencart/components/importer.py b/connector_opencart/components/importer.py
index a2c7f973..6a1ef5db 100644
--- a/connector_opencart/components/importer.py
+++ b/connector_opencart/components/importer.py
@@ -88,7 +88,8 @@ class OpencartImporter(AbstractComponent):
if not external_id:
return
binder = self.binder_for(binding_model)
- if always or not binder.to_internal(external_id):
+ record = binder.to_internal(external_id)
+ if always or not record:
if importer is None:
importer = self.component(usage='record.importer',
model_name=binding_model)
@@ -99,6 +100,13 @@ class OpencartImporter(AbstractComponent):
'Dependency import of %s(%s) has been ignored.',
binding_model._name, external_id
)
+ return True
+ if binding_model == 'opencart.product.template' and record.backend_id.so_require_product_setup:
+ # Though this is not the "right" place to do this,
+ # we need to return True if there is a checkpoint for a product.
+ if record.backend_id.find_checkpoint(record):
+ return True
+ return False
def _import_dependencies(self):
""" Import the dependencies for the record
diff --git a/connector_opencart/data/connector_opencart_data.xml b/connector_opencart/data/connector_opencart_data.xml
index fef62b5b..b575ec5d 100644
--- a/connector_opencart/data/connector_opencart_data.xml
+++ b/connector_opencart/data/connector_opencart_data.xml
@@ -4,13 +4,13 @@
Opencart - Import Sales Orders
-
+
code
1
- days
+ hours
-1
-
+
model._scheduler_import_sale_orders()
@@ -27,26 +27,7 @@ Check your taxes and fiscal positions configuration and correct them if necessar
30
sale.order
sale
- if sale.opencart_bind_ids and abs(sale.amount_total - sale.opencart_bind_ids[0].total_amount) >= 0.01:
- failed = True
-
-
-
-
- Total Tax Amount differs from Opencart
- The tax amount computed in Odoo doesn't match with the tax amount in Opencart.
-
-Cause:
-The taxes are probably different between Odoo and Opencart. A fiscal position could have changed the final price.
-
-Resolution:
-Check your taxes and fiscal positions configuration and correct them if necessary.
- 30
- sale.order
- sale
- # By default, a cent of difference for the tax amount is allowed, feel free to customise it in your own module
-if sale.opencart_bind_ids and abs(sale.amount_tax - sale.opencart_bind_ids[0].total_amount_tax) > 0.01:
- failed = True
+ failed = sale.opencart_bind_ids and abs(sale.amount_total - sale.opencart_bind_ids[0].total_amount) >= 0.01
diff --git a/connector_opencart/models/__init__.py b/connector_opencart/models/__init__.py
index 4ed0f156..a7f55577 100644
--- a/connector_opencart/models/__init__.py
+++ b/connector_opencart/models/__init__.py
@@ -1,4 +1,5 @@
from . import delivery
from . import opencart
+from . import product
from . import sale_order
from . import stock_picking
diff --git a/connector_opencart/models/opencart/backend.py b/connector_opencart/models/opencart/backend.py
index f3b344ce..42cd62ad 100644
--- a/connector_opencart/models/opencart/backend.py
+++ b/connector_opencart/models/opencart/backend.py
@@ -2,12 +2,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from datetime import datetime, timedelta
from logging import getLogger
from contextlib import contextmanager
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.addons.connector.models.checkpoint import add_checkpoint
from ...components.api.opencart import Opencart
_logger = getLogger(__name__)
@@ -62,6 +62,8 @@ class OpencartBackend(models.Model):
"in Odoo.",
)
# payment_mode_id = fields.Many2one(comodel_name='account.payment.mode', string="Payment Mode")
+ coupon_product_id = fields.Many2one(comodel_name='product.product', string='Coupon Product',
+ help='Product to represent coupon discounts.')
# New Product fields.
product_categ_id = fields.Many2one(comodel_name='product.category', string='Product Category',
@@ -71,6 +73,9 @@ class OpencartBackend(models.Model):
string='Import sale orders after id',
)
+ so_require_product_setup = fields.Boolean(string='SO Require Product Setup',
+ help='Prevents SO from being confirmed (failed queue job), if one or more products has an open checkpoint.')
+
@contextmanager
@api.multi
def work_on(self, model_name, **kwargs):
@@ -80,6 +85,27 @@ class OpencartBackend(models.Model):
with _super.work_on(model_name, opencart_api=opencart_api, **kwargs) as work:
yield work
+ @api.multi
+ def add_checkpoint(self, record):
+ self.ensure_one()
+ record.ensure_one()
+ return add_checkpoint(self.env, record._name, record.id,
+ self._name, self.id)
+
+ @api.multi
+ def find_checkpoint(self, record):
+ self.ensure_one()
+ record.ensure_one()
+ checkpoint_model = self.env['connector.checkpoint']
+ model_model = self.env['ir.model']
+ model = model_model.search([('model', '=', record._name)], limit=1)
+ return checkpoint_model.search([
+ ('backend_id', '=', '%s,%s' % (self._name, self.id)),
+ ('model_id', '=', model.id),
+ ('record_id', '=', record.id),
+ ('state', '=', 'need_review'),
+ ], limit=1)
+
@api.multi
def synchronize_metadata(self):
try:
diff --git a/connector_opencart/models/opencart/store.py b/connector_opencart/models/opencart/store.py
index 31e71e39..4444b16f 100644
--- a/connector_opencart/models/opencart/store.py
+++ b/connector_opencart/models/opencart/store.py
@@ -50,6 +50,8 @@ class OpencartStore(models.Model):
"order 36071 in Opencart, will be named 'OC-36071' "
"in Odoo. (overridden from backend)",
)
+ coupon_product_id = fields.Many2one(comodel_name='product.product', string='Coupon Product',
+ help='Product to represent coupon discounts.')
class OpencartStoreAdapter(Component):
diff --git a/connector_opencart/models/product/__init__.py b/connector_opencart/models/product/__init__.py
new file mode 100644
index 00000000..79ab5dc6
--- /dev/null
+++ b/connector_opencart/models/product/__init__.py
@@ -0,0 +1,2 @@
+from . import common
+from . import importer
diff --git a/connector_opencart/models/product/common.py b/connector_opencart/models/product/common.py
new file mode 100644
index 00000000..4d169803
--- /dev/null
+++ b/connector_opencart/models/product/common.py
@@ -0,0 +1,81 @@
+from odoo import api, fields, models
+from odoo.addons.queue_job.exception import NothingToDoJob, RetryableJobError
+from odoo.addons.component.core import Component
+
+
+class OpencartProductTemplate(models.Model):
+ _name = 'opencart.product.template'
+ _inherit = 'opencart.binding'
+ _inherits = {'product.template': 'odoo_id'}
+ _description = 'Opencart Product'
+
+ odoo_id = fields.Many2one('product.template',
+ string='Product',
+ required=True,
+ ondelete='cascade') # cascade so that you can delete an Odoo product that was created by connector
+ opencart_attribute_value_ids = fields.One2many('opencart.product.template.attribute.value',
+ 'opencart_product_tmpl_id',
+ string='Opencart Product Attribute Values')
+
+ def opencart_sale_get_combination(self, options, reentry=False):
+ selected_attribute_values = self.env['product.template.attribute.value']
+ for option in options:
+ product_option_value_id = str(option['product_option_value_id'])
+ opencart_attribute_value = self.opencart_attribute_value_ids.filtered(lambda v: v.external_id == product_option_value_id)
+ if not opencart_attribute_value:
+ if reentry:
+ # we have already triggered an import.
+ raise Exception('Order Product has option (%s) "%s" that does not exist on the product.' % (product_option_value_id, option.get('name', '')))
+ # need to re-import product.
+ try:
+ self.import_record(self.backend_id, self.external_id, force=True)
+ return self.opencart_sale_get_combination(options, reentry=True)
+ except NothingToDoJob:
+ if reentry:
+ raise RetryableJobError('Product imported, but selected option is not available.')
+ if not opencart_attribute_value.odoo_id:
+ raise RetryableJobError('Order Product (%s) has option (%s) "%s" that is not mapped to an Odoo Attribute Value.' % (self, opencart_attribute_value.external_id, opencart_attribute_value.opencart_name))
+ selected_attribute_values += opencart_attribute_value.odoo_id
+ # Now that we know what options are selected, we can load a variant with those options
+ product = self.odoo_id._create_product_variant(selected_attribute_values, log_warning=True)
+ if not product:
+ raise Exception('No product can be created for selected attribute values, check logs. ' + str(selected_attribute_values))
+ return product
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ opencart_sku = fields.Char('Opencart SKU')
+ opencart_bind_ids = fields.One2many('opencart.product.template', 'odoo_id', string='Opencart Bindings')
+
+
+class OpencartProductTemplateAdapter(Component):
+ _name = 'opencart.product.template.adapter'
+ _inherit = 'opencart.adapter'
+ _apply_on = 'opencart.product.template'
+
+ def read(self, id):
+ api_instance = self.api_instance
+ record = api_instance.products.get(id)
+ if 'data' in record and record['data']:
+ return record['data']
+ raise RetryableJobError('Product "' + str(id) + '" did not return an product response. ' + str(record))
+
+
+# Product Attribute Value, cannot "inherits" the odoo_id as then it cannot be empty
+class OpencartProductTemplateAttributeValue(models.Model):
+ _name = 'opencart.product.template.attribute.value'
+ _inherit = 'opencart.binding'
+ _description = 'Opencart Product Attribute Value'
+
+ odoo_id = fields.Many2one('product.template.attribute.value',
+ string='Product Attribute Value',
+ required=False,
+ ondelete='cascade')
+ opencart_name = fields.Char(string='Opencart Name', help='For matching purposes.')
+ opencart_product_tmpl_id = fields.Many2one('opencart.product.template',
+ string='Opencart Product',
+ required=True,
+ ondelete='cascade')
+ product_tmpl_id = fields.Many2one(related='opencart_product_tmpl_id.odoo_id')
diff --git a/connector_opencart/models/product/importer.py b/connector_opencart/models/product/importer.py
new file mode 100644
index 00000000..c7988323
--- /dev/null
+++ b/connector_opencart/models/product/importer.py
@@ -0,0 +1,91 @@
+from html import unescape
+from odoo.addons.component.core import Component
+from odoo.addons.connector.components.mapper import mapping, only_create
+
+
+class ProductImportMapper(Component):
+ _name = 'opencart.product.template.import.mapper'
+ _inherit = 'opencart.import.mapper'
+ _apply_on = ['opencart.product.template']
+
+ @mapping
+ def backend_id(self, record):
+ return {'backend_id': self.backend_record.id}
+
+ @mapping
+ def name(self, record):
+ name = record.get('product_description', [{}])[0].get('name', record.get('id'))
+ return {'name': unescape(name)}
+
+ # TODO more fields like pricing....
+
+ @mapping
+ def product_type(self, record):
+ return {'type': 'product' if record.get('shipping') else 'service'}
+
+ @mapping
+ def opencart_sku(self, record):
+ sku = str(record.get('model') or record.get('sku') or '').strip()
+ return {'opencart_sku': sku}
+
+ @only_create
+ @mapping
+ def existing_product(self, record):
+ product_template = self.env['product.template']
+ template = product_template.browse()
+
+ if record.get('model'):
+ model = str(record.get('model') or '').strip()
+ # Try to match our own field
+ template = product_template.search([('opencart_sku', '=', model)], limit=1)
+ if not template:
+ # Try to match the default_code
+ template = product_template.search([('default_code', '=', model)], limit=1)
+ if not template and record.get('sku'):
+ sku = str(record.get('sku') or '').strip()
+ template = product_template.search([('opencart_sku', '=', sku)], limit=1)
+ if not template:
+ template = product_template.search([('default_code', '=', sku)], limit=1)
+ if not template and record.get('name'):
+ name = record.get('product_description', [{}])[0].get('name')
+ if name:
+ template = product_template.search([('name', '=', unescape(name))], limit=1)
+ return {'odoo_id': template.id}
+
+
+class ProductImporter(Component):
+ _name = 'opencart.product.template.importer'
+ _inherit = 'opencart.importer'
+ _apply_on = ['opencart.product.template']
+
+ def _create(self, data):
+ binding = super(ProductImporter, self)._create(data)
+ self.backend_record.add_checkpoint(binding)
+ return binding
+
+ def _after_import(self, binding):
+ self._sync_options(binding)
+
+ def _sync_options(self, binding):
+ existing_option_values = binding.opencart_attribute_value_ids
+ mapped_option_values = binding.opencart_attribute_value_ids.browse()
+ record = self.opencart_record
+ backend = self.backend_record
+ for option in record.get('options', []):
+ for record_option_value in option.get('option_value', []):
+ option_value = existing_option_values.filtered(lambda v: v.external_id == str(record_option_value['product_option_value_id']))
+ name = unescape(record_option_value.get('name', ''))
+ if not option_value:
+ option_value = existing_option_values.create({
+ 'backend_id': backend.id,
+ 'external_id': record_option_value['product_option_value_id'],
+ 'opencart_name': name,
+ 'opencart_product_tmpl_id': binding.id,
+ })
+ # Keep options consistent with Opencart by renaming them
+ if option_value.opencart_name != name:
+ option_value.opencart_name = name
+ mapped_option_values += option_value
+
+ to_unlink = existing_option_values - mapped_option_values
+ to_unlink.unlink()
diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py
index 42813627..79e19595 100644
--- a/connector_opencart/models/sale_order/importer.py
+++ b/connector_opencart/models/sale_order/importer.py
@@ -2,11 +2,13 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from copy import deepcopy, copy
+from html import unescape
from odoo import fields, _
from odoo.addons.component.core import Component
from odoo.addons.connector.components.mapper import mapping
from odoo.exceptions import ValidationError
+from odoo.addons.queue_job.exception import RetryableJobError
class SaleOrderBatchImporter(Component):
@@ -52,31 +54,55 @@ class SaleOrderImportMapper(Component):
direct = [('order_id', 'external_id'),
('store_id', 'store_id'),
- # ('customerOrderId', 'customer_order_id'),
]
children = [('products', 'opencart_order_line_ids', 'opencart.sale.order.line'),
]
- # def _map_child(self, map_record, from_attr, to_attr, model_name):
- # return super(SaleOrderImportMapper, self)._map_child(map_record, from_attr, to_attr, model_name)
+ def _add_coupon_lines(self, map_record, values):
+ # Data from API
+ # 'coupons': [{'amount': '7.68', 'code': '1111'}],
+ record = map_record.source
+
+ coupons = record.get('coupons')
+ if not coupons:
+ return values
+
+ coupon_product = self.options.store.coupon_product_id or self.backend_record.coupon_product_id
+ if not coupon_product:
+ coupon_product = self.env.ref('connector_ecommerce.product_product_discount', raise_if_not_found=False)
+
+ if not coupon_product:
+ raise ValueError('Coupon %s on order requires coupon product in configuration.' % (coupons, ))
+ for coupon in coupons:
+ line_builder = self.component(usage='order.line.builder')
+ line_builder.price_unit = -float(coupon.get('amount', 0.0))
+ line_builder.product = coupon_product
+ # `order.line.builder` does not allow naming.
+ line_values = line_builder.get_line()
+ code = coupon.get('code')
+ if code:
+ line_values['name'] = '%s Code: %s' % (coupon_product.name, code)
+ values['order_line'].append((0, 0, line_values))
+ return values
def _add_shipping_line(self, map_record, values):
record = map_record.source
line_builder = self.component(usage='order.line.builder.shipping')
- line_builder.price_unit = 0.0
+ line_builder.price_unit = record.get('shipping_exclude_tax', 0.0)
if values.get('carrier_id'):
carrier = self.env['delivery.carrier'].browse(values['carrier_id'])
line_builder.product = carrier.product_id
+ line = (0, 0, line_builder.get_line())
+ values['order_line'].append(line)
- line = (0, 0, line_builder.get_line())
- values['order_line'].append(line)
return values
def finalize(self, map_record, values):
values.setdefault('order_line', [])
+ self._add_coupon_lines(map_record, values)
self._add_shipping_line(map_record, values)
values.update({
'partner_id': self.options.partner_id,
@@ -120,9 +146,8 @@ class SaleOrderImportMapper(Component):
[('name', '=', record_method)],
limit=1,
)
- assert method, ("method %s should exist because the import fails "
- "in SaleOrderImporter._before_import when it is "
- " missing" % record_method)
+ if not method:
+ raise ValueError('Payment Mode named "%s", cannot be found.' % (record_method, ))
return {'payment_mode_id': method.id}
@mapping
@@ -138,8 +163,11 @@ class SaleOrderImportMapper(Component):
return {'warehouse_id': warehouse.id}
@mapping
- def shipping_method(self, record):
- method = record['shipping_method'] or ''
+ def shipping_code(self, record):
+ method = record.get('shipping_code') or record.get('shipping_method')
+ if not method:
+ return {'carrier_id': False}
+
carrier_domain = [('opencart_code', '=', method.strip())]
company = self.options.store.company_id or self.backend_record.company_id
if company:
@@ -148,8 +176,8 @@ class SaleOrderImportMapper(Component):
]
carrier = self.env['delivery.carrier'].search(carrier_domain, limit=1)
if not carrier:
- raise ValueError('Delivery Carrier for methodCode "%s", cannot be found.' % (method, ))
- return {'carrier_id': carrier.id, 'shipping_method_code': method}
+ raise ValueError('Delivery Carrier for method Code "%s", cannot be found.' % (method, ))
+ return {'carrier_id': carrier.id}
@mapping
def company_id(self, record):
@@ -186,6 +214,9 @@ class SaleOrderImporter(Component):
def _partner_matches(self, partner, values):
for key, value in values.items():
+ if key in ('active', 'parent_id', 'type'):
+ continue
+
if key == 'state_id':
if value != partner.state_id.id:
return False
@@ -197,7 +228,7 @@ class SaleOrderImporter(Component):
return True
def _make_partner_name(self, firstname, lastname):
- name = (str(firstname) + ' ' + str(lastname)).strip()
+ name = (str(firstname or '').strip() + ' ' + str(lastname or '').strip()).strip()
if not name:
return 'Undefined'
return name
@@ -233,24 +264,31 @@ class SaleOrderImporter(Component):
], limit=1)
return {
- 'email': email,
- 'name': name,
- 'phone': phone,
- 'street': street,
- 'street2': street2,
- 'zip': zip_,
- 'city': city,
+ 'email': email.strip(),
+ 'name': name.strip(),
+ 'phone': phone.strip(),
+ 'street': street.strip(),
+ 'street2': street2.strip(),
+ 'zip': zip_.strip(),
+ 'city': city.strip(),
'state_id': state.id,
'country_id': country.id,
}
def _import_addresses(self):
- record = self.opencart_record
-
partner_values = self._get_partner_values()
- partner = self.env['res.partner'].search([
+ partners = self.env['res.partner'].search([
('email', '=', partner_values['email']),
- ], limit=1)
+ '|', ('active', '=', False), ('active', '=', True),
+ ], order='active DESC, id ASC')
+
+ partner = None
+ for possible in partners:
+ if self._partner_matches(possible, partner_values):
+ partner = possible
+ break
+ if not partner and partners:
+ partner = partners[0]
if not partner:
# create partner.
@@ -258,18 +296,25 @@ class SaleOrderImporter(Component):
if not self._partner_matches(partner, partner_values):
partner_values['parent_id'] = partner.id
- partner_values['active'] = False
- shipping_partner = self._create_partner(copy(partner_values))
+ shipping_values = copy(partner_values)
+ shipping_values['type'] = 'delivery'
+ shipping_partner = self._create_partner(shipping_values)
else:
shipping_partner = partner
invoice_values = self._get_partner_values(info_string='payment_')
+ invoice_values['type'] = 'invoice'
if (not self._partner_matches(partner, invoice_values)
and not self._partner_matches(shipping_partner, invoice_values)):
- partner_values['parent_id'] = partner.id
- partner_values['active'] = False
- invoice_partner = self._create_partner(copy(invoice_values))
+ # Try to find existing invoice address....
+ for possible in partners:
+ if self._partner_matches(possible, invoice_values):
+ invoice_partner = possible
+ break
+ else:
+ invoice_values['parent_id'] = partner.id
+ invoice_partner = self._create_partner(copy(invoice_values))
elif self._partner_matches(partner, invoice_values):
invoice_partner = partner
elif self._partner_matches(shipping_partner, invoice_values):
@@ -317,7 +362,18 @@ class SaleOrderImporter(Component):
return binding
def _import_dependencies(self):
+ record = self.opencart_record
self._import_addresses()
+ products_need_setup = []
+ for product in record.get('products', []):
+ if 'product_id' in product and product['product_id']:
+ needs_product_setup = self._import_dependency(product['product_id'], 'opencart.product.template')
+ if needs_product_setup:
+ products_need_setup.append(product['product_id'])
+
+ if products_need_setup and self.backend_record.so_require_product_setup:
+ # There are products that were either just imported, or
+ raise RetryableJobError('Products need setup. OpenCart Product IDs:' + str(products_need_setup), seconds=3600)
class SaleOrderLineImportMapper(Component):
@@ -328,33 +384,21 @@ class SaleOrderLineImportMapper(Component):
direct = [('quantity', 'product_uom_qty'),
('price', 'price_unit'),
- ('name', 'name'),
('order_product_id', 'external_id'),
]
- def _finalize_product_values(self, record, values):
- # This would be a good place to create a vendor or add a route...
- return values
-
- def _product_values(self, record):
- reference = record['model']
- values = {
- 'default_code': reference,
- 'name': record.get('name', reference),
- 'type': 'product',
- 'list_price': record.get('price', 0.0),
- 'categ_id': self.backend_record.product_categ_id.id,
- }
- return self._finalize_product_values(record, values)
+ @mapping
+ def name(self, record):
+ return {'name': unescape(record['name'])}
@mapping
def product_id(self, record):
- reference = record['model']
- product = self.env['product.product'].search([
- ('default_code', '=', reference)
- ], limit=1)
-
- if not product:
- # we could use a record like (0, 0, values)
- product = self.env['product.product'].create(self._product_values(record))
+ product_id = record['product_id']
+ binder = self.binder_for('opencart.product.template')
+ # do not unwrap, because it would be a product.template, but I need a specific variant
+ opencart_product_template = binder.to_internal(product_id, unwrap=False)
+ if record.get('option'):
+ product = opencart_product_template.opencart_sale_get_combination(record.get('option'))
+ else:
+ product = opencart_product_template.odoo_id.product_variant_id
return {'product_id': product.id, 'product_uom': product.uom_id.id}
diff --git a/connector_opencart/security/ir.model.access.csv b/connector_opencart/security/ir.model.access.csv
index 44dcff29..2a8d2dfd 100644
--- a/connector_opencart/security/ir.model.access.csv
+++ b/connector_opencart/security/ir.model.access.csv
@@ -4,6 +4,8 @@
"access_opencart_binding","opencart_binding connector manager","model_opencart_binding","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order","opencart_sale_order connector manager","model_opencart_sale_order","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order_line","opencart_sale_order_line connector manager","model_opencart_sale_order_line","connector.group_connector_manager",1,1,1,1
+"access_opencart_product_template","opencart_product_template connector manager","model_opencart_product_template","connector.group_connector_manager",1,1,1,1
+"access_opencart_product_template_attribute_value","opencart_product_template_attribute_value connector manager","model_opencart_product_template_attribute_value","connector.group_connector_manager",1,1,1,1
"access_opencart_stock_picking","opencart_stock_picking connector manager","model_opencart_stock_picking","connector.group_connector_manager",1,1,1,1
"access_opencart_sale_order_sale_salesman","opencart_sale_order","model_opencart_sale_order","sales_team.group_sale_salesman",1,0,0,0
"access_opencart_sale_order_sale_manager","opencart_sale_order","model_opencart_sale_order","sales_team.group_sale_manager",1,1,1,1
diff --git a/connector_opencart/views/opencart_backend_views.xml b/connector_opencart/views/opencart_backend_views.xml
index 360b9315..d60fda7b 100644
--- a/connector_opencart/views/opencart_backend_views.xml
+++ b/connector_opencart/views/opencart_backend_views.xml
@@ -37,6 +37,8 @@
+
+
@@ -131,6 +133,7 @@
+
diff --git a/connector_opencart/views/opencart_product_views.xml b/connector_opencart/views/opencart_product_views.xml
new file mode 100644
index 00000000..4cec551f
--- /dev/null
+++ b/connector_opencart/views/opencart_product_views.xml
@@ -0,0 +1,56 @@
+
+
+
+
+ opencart.product.template.form
+ opencart.product.template
+
+
+
+
+
+
+ opencart.product.template.tree
+ opencart.product.template
+
+
+
+
+
+
+
+
+
+
+
+
+ Opencart Products
+ opencart.product.template
+ form
+ tree,form
+
+
+
+
+
+
\ No newline at end of file
diff --git a/connector_opencart/views/product_views.xml b/connector_opencart/views/product_views.xml
new file mode 100644
index 00000000..3ccc3955
--- /dev/null
+++ b/connector_opencart/views/product_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ product.template.product.form.inherit
+ product.template
+
+
+
+
+
+
+
+
+
\ No newline at end of file