diff --git a/connector_opencart/components/api/opencart.py b/connector_opencart/components/api/opencart.py index a09dfc8e..3e5b0b9a 100644 --- a/connector_opencart/components/api/opencart.py +++ b/connector_opencart/components/api/opencart.py @@ -21,6 +21,10 @@ class Opencart: def orders(self): return Orders(connection=self) + @property + def stores(self): + return Stores(connection=self) + def get_headers(self, url, method): headers = {} if method in ('POST', 'PUT', ): @@ -100,3 +104,18 @@ class Orders(Resource): "tracking": tracking, } return dumps(payload) + + +class Stores(Resource): + """ + Retrieves Store details + """ + + path = 'stores' + + def all(self): + return self.connection.send_request(method='GET', url=self.url) + + 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 e1457cd9..d8bbaebd 100644 --- a/connector_opencart/components/binder.py +++ b/connector_opencart/components/binder.py @@ -16,6 +16,7 @@ class OpencartModelBinder(Component): _name = 'opencart.binder' _inherit = ['base.binder', 'base.opencart.connector'] _apply_on = [ + 'opencart.store', 'opencart.sale.order', 'opencart.sale.order.line', 'opencart.stock.picking', diff --git a/connector_opencart/models/__init__.py b/connector_opencart/models/__init__.py index f2bfeecb..4ed0f156 100644 --- a/connector_opencart/models/__init__.py +++ b/connector_opencart/models/__init__.py @@ -1,5 +1,4 @@ from . import delivery -from . import opencart_backend -from . import opencart_binding +from . import opencart from . import sale_order from . import stock_picking diff --git a/connector_opencart/models/opencart/__init__.py b/connector_opencart/models/opencart/__init__.py new file mode 100644 index 00000000..289a138e --- /dev/null +++ b/connector_opencart/models/opencart/__init__.py @@ -0,0 +1,5 @@ +from . import backend +from . import backend_importer +from . import binding +from . import store +from . import store_importer diff --git a/connector_opencart/models/opencart_backend/common.py b/connector_opencart/models/opencart/backend.py similarity index 89% rename from connector_opencart/models/opencart_backend/common.py rename to connector_opencart/models/opencart/backend.py index c6eb1298..f3b344ce 100644 --- a/connector_opencart/models/opencart_backend/common.py +++ b/connector_opencart/models/opencart/backend.py @@ -7,6 +7,7 @@ from logging import getLogger from contextlib import contextmanager from odoo import api, fields, models, _ +from odoo.exceptions import UserError from ...components.api.opencart import Opencart _logger = getLogger(__name__) @@ -79,6 +80,17 @@ class OpencartBackend(models.Model): with _super.work_on(model_name, opencart_api=opencart_api, **kwargs) as work: yield work + @api.multi + def synchronize_metadata(self): + try: + for backend in self: + self.env['opencart.store'].import_batch(backend) + return True + except Exception as e: + _logger.error(e) + raise UserError(_("Check your configuration, we can't get the data. " + "Here is the error:\n%s") % (e, )) + @api.model def _scheduler_import_sale_orders(self): # potential hook for customization (e.g. pad from date or provide its own) diff --git a/connector_opencart/models/opencart/backend_importer.py b/connector_opencart/models/opencart/backend_importer.py new file mode 100644 index 00000000..9cf1b2e4 --- /dev/null +++ b/connector_opencart/models/opencart/backend_importer.py @@ -0,0 +1,19 @@ +# © 2019 Hibou Corp. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.component.core import Component + + +class MetadataBatchImporter(Component): + """ Import the records directly, without delaying the jobs. + + Import the Opencart Stores + + They are imported directly because this is a rare and fast operation, + and we don't really bother if it blocks the UI during this time. + + """ + + _name = 'opencart.metadata.batch.importer' + _inherit = 'opencart.direct.batch.importer' + _apply_on = ['opencart.store'] diff --git a/connector_opencart/models/opencart_binding/common.py b/connector_opencart/models/opencart/binding.py similarity index 100% rename from connector_opencart/models/opencart_binding/common.py rename to connector_opencart/models/opencart/binding.py diff --git a/connector_opencart/models/opencart/store.py b/connector_opencart/models/opencart/store.py new file mode 100644 index 00000000..31e71e39 --- /dev/null +++ b/connector_opencart/models/opencart/store.py @@ -0,0 +1,77 @@ +# © 2019 Hibou Corp. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError +from odoo.addons.component.core import Component +from odoo.addons.queue_job.exception import RetryableJobError + + +class OpencartStore(models.Model): + _name = 'opencart.store' + _inherit = ['opencart.binding'] + _description = 'Opencart Store' + _parent_name = 'backend_id' + + name = fields.Char() + backend_id = fields.Many2one('opencart.backend', + string='Opencart Backend', + ondelete='cascade', + readonly=True) + + warehouse_id = fields.Many2one( + comodel_name='stock.warehouse', + string='Warehouse', + help='Warehouse to use for stock. (overridden from backend)', + ) + company_id = fields.Many2one( + comodel_name='res.company', + related='warehouse_id.company_id', + string='Company', + readonly=True, + ) + fiscal_position_id = fields.Many2one( + comodel_name='account.fiscal.position', + string='Fiscal Position', + help='Fiscal position to use on orders. (overridden from backend)', + ) + analytic_account_id = fields.Many2one( + comodel_name='account.analytic.account', + string='Analytic account', + help='If specified, this analytic account will be used to fill the ' + 'field on the sale order created by the connector. (overridden from backend)' + ) + team_id = fields.Many2one(comodel_name='crm.team', string='Sales Team', + help='(overridden from backend)') + sale_prefix = fields.Char( + string='Sale Prefix', + help="A prefix put before the name of imported sales orders.\n" + "For instance, if the prefix is 'OC-', the sales " + "order 36071 in Opencart, will be named 'OC-36071' " + "in Odoo. (overridden from backend)", + ) + + +class OpencartStoreAdapter(Component): + _name = 'opencart.store.adapter' + _inherit = 'opencart.adapter' + _apply_on = 'opencart.store' + + def search(self, filters=None): + api_instance = self.api_instance + stores_response = api_instance.stores.all() + if 'error' in stores_response and stores_response['error']: + raise ValidationError(str(stores_response)) + + if 'data' not in stores_response or not isinstance(stores_response['data'], list): + return [] + + stores = stores_response['data'] + return list(map(lambda s: s['store_id'], stores)) + + def read(self, id): + api_instance = self.api_instance + record = api_instance.stores.get(id) + if 'data' in record and record['data']: + return record['data'] + raise RetryableJobError('Store "' + str(id) + '" did not return an store response. ' + str(record)) diff --git a/connector_opencart/models/opencart/store_importer.py b/connector_opencart/models/opencart/store_importer.py new file mode 100644 index 00000000..79fcf834 --- /dev/null +++ b/connector_opencart/models/opencart/store_importer.py @@ -0,0 +1,27 @@ +# © 2019 Hibou Corp. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.component.core import Component +from odoo.addons.connector.components.mapper import mapping + + +class OpencartStoreImportMapper(Component): + _name = 'opencart.store.mapper' + _inherit = 'opencart.import.mapper' + _apply_on = 'opencart.store' + + direct = [ + ('config_name', 'name'), + ] + + @mapping + def backend_id(self, record): + return {'backend_id': self.backend_record.id} + + +class OpencartStoreImporter(Component): + """ Import one Opencart Store """ + + _name = 'opencart.store.importer' + _inherit = 'opencart.importer' + _apply_on = 'opencart.store' diff --git a/connector_opencart/models/opencart_backend/__init__.py b/connector_opencart/models/opencart_backend/__init__.py deleted file mode 100644 index e4193cf0..00000000 --- a/connector_opencart/models/opencart_backend/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import common diff --git a/connector_opencart/models/opencart_binding/__init__.py b/connector_opencart/models/opencart_binding/__init__.py deleted file mode 100644 index e4193cf0..00000000 --- a/connector_opencart/models/opencart_binding/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import common diff --git a/connector_opencart/models/sale_order/common.py b/connector_opencart/models/sale_order/common.py index 0216a6dc..48d0635a 100644 --- a/connector_opencart/models/sale_order/common.py +++ b/connector_opencart/models/sale_order/common.py @@ -27,6 +27,7 @@ class OpencartSaleOrder(models.Model): inverse_name='opencart_order_id', string='Walmart Order Lines' ) + store_id = fields.Many2one('opencart.store', string='Store') total_amount = fields.Float( string='Total amount', diff --git a/connector_opencart/models/sale_order/importer.py b/connector_opencart/models/sale_order/importer.py index 25999d92..507c8e3b 100644 --- a/connector_opencart/models/sale_order/importer.py +++ b/connector_opencart/models/sale_order/importer.py @@ -9,6 +9,7 @@ from copy import deepcopy, copy 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 NothingToDoJob, FailedJobError _logger = logging.getLogger(__name__) @@ -41,13 +42,12 @@ class SaleOrderBatchImporter(Component): class SaleOrderImportMapper(Component): - - _name = 'opencart.sale.order.mapper' _inherit = 'opencart.import.mapper' _apply_on = 'opencart.sale.order' direct = [('order_id', 'external_id'), + ('store_id', 'store_id'), # ('customerOrderId', 'customer_order_id'), ] @@ -88,7 +88,7 @@ class SaleOrderImportMapper(Component): @mapping def name(self, record): name = str(record['order_id']) - prefix = self.backend_record.sale_prefix + prefix = self.options.store.sale_prefix or self.backend_record.sale_prefix if prefix: name = prefix + name return {'name': name} @@ -99,13 +99,15 @@ class SaleOrderImportMapper(Component): @mapping def fiscal_position_id(self, record): - if self.backend_record.fiscal_position_id: - return {'fiscal_position_id': self.backend_record.fiscal_position_id.id} + fiscal_position = self.options.store.fiscal_position_id or self.backend_record.fiscal_position_id + if fiscal_position: + return {'fiscal_position_id': fiscal_position.id} @mapping def team_id(self, record): - if self.backend_record.team_id: - return {'team_id': self.backend_record.team_id.id} + team = self.options.store.team_id or self.backend_record.team_id + if team: + return {'team_id': team.id} @mapping def payment_mode_id(self, record): @@ -121,22 +123,37 @@ class SaleOrderImportMapper(Component): @mapping def project_id(self, record): - if self.backend_record.analytic_account_id: - return {'project_id': self.backend_record.analytic_account_id.id} + analytic_account = self.options.store.analytic_account_id or self.backend_record.analytic_account_id + if analytic_account: + return {'project_id': analytic_account.id} @mapping def warehouse_id(self, record): - if self.backend_record.warehouse_id: - return {'warehouse_id': self.backend_record.warehouse_id.id} + warehouse = self.options.store.warehouse_id or self.backend_record.warehouse_id + if warehouse: + return {'warehouse_id': warehouse.id} @mapping def shipping_method(self, record): method = record['shipping_method'] - carrier = self.env['delivery.carrier'].search([('opencart_code', '=', method)], limit=1) + carrier_domain = [('opencart_code', '=', method)] + company = self.options.store.company_id or self.backend_record.company_id + if company: + carrier_domain += [ + '|', ('company_id', '=', company.id), ('company_id', '=', False) + ] + 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} + @mapping + def company_id(self, record): + company = self.options.store.company_id or self.backend_record.company_id + if not company: + raise ValidationError('Company not found in Opencart Backend or Store') + return {'company_id': company.id} + @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} @@ -275,14 +292,21 @@ class SaleOrderImporter(Component): "self.invoice_partner should have been defined " "in SaleOrderImporter._import_addresses") + def _get_store(self, record): + store_binder = self.binder_for('opencart.store') + return store_binder.to_internal(record['store_id']) + def _create_data(self, map_record, **kwargs): # non dependencies + # our current handling of partners doesn't require anything special for the store self._check_special_fields() + store = self._get_store(map_record.source) return super(SaleOrderImporter, self)._create_data( map_record, partner_id=self.partner.id, partner_invoice_id=self.invoice_partner.id, partner_shipping_id=self.shipping_partner.id, + store=store, **kwargs ) @@ -298,8 +322,7 @@ class SaleOrderImporter(Component): return binding def _import_dependencies(self): - record = self.opencart_record - + record = self.opencart_record # maybe iterate over products if we need to self._import_addresses() class SaleOrderLineImportMapper(Component): diff --git a/connector_opencart/models/stock_picking/common.py b/connector_opencart/models/stock_picking/common.py index 01ab953c..9bb2252d 100644 --- a/connector_opencart/models/stock_picking/common.py +++ b/connector_opencart/models/stock_picking/common.py @@ -44,6 +44,7 @@ class StockPicking(models.Model): string="Opencart Bindings", ) + class StockPickingAdapter(Component): _name = 'opencart.stock.picking.adapter' _inherit = 'opencart.adapter' diff --git a/connector_opencart/security/ir.model.access.csv b/connector_opencart/security/ir.model.access.csv index c42e5c02..44dcff29 100644 --- a/connector_opencart/security/ir.model.access.csv +++ b/connector_opencart/security/ir.model.access.csv @@ -1,5 +1,6 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_opencart_backend","opencart_backend connector manager","model_opencart_backend","connector.group_connector_manager",1,1,1,1 +"access_opencart_store","opencart_store connector manager","model_opencart_store","connector.group_connector_manager",1,1,1,1 "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 diff --git a/connector_opencart/views/opencart_backend_views.xml b/connector_opencart/views/opencart_backend_views.xml index 028c06c2..579ddb20 100644 --- a/connector_opencart/views/opencart_backend_views.xml +++ b/connector_opencart/views/opencart_backend_views.xml @@ -7,6 +7,10 @@
+