mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
IMP connector_opencart add model for opencart.store with importer and use it for order options
This commit is contained in:
@@ -21,6 +21,10 @@ class Opencart:
|
|||||||
def orders(self):
|
def orders(self):
|
||||||
return Orders(connection=self)
|
return Orders(connection=self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stores(self):
|
||||||
|
return Stores(connection=self)
|
||||||
|
|
||||||
def get_headers(self, url, method):
|
def get_headers(self, url, method):
|
||||||
headers = {}
|
headers = {}
|
||||||
if method in ('POST', 'PUT', ):
|
if method in ('POST', 'PUT', ):
|
||||||
@@ -100,3 +104,18 @@ class Orders(Resource):
|
|||||||
"tracking": tracking,
|
"tracking": tracking,
|
||||||
}
|
}
|
||||||
return dumps(payload)
|
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)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class OpencartModelBinder(Component):
|
|||||||
_name = 'opencart.binder'
|
_name = 'opencart.binder'
|
||||||
_inherit = ['base.binder', 'base.opencart.connector']
|
_inherit = ['base.binder', 'base.opencart.connector']
|
||||||
_apply_on = [
|
_apply_on = [
|
||||||
|
'opencart.store',
|
||||||
'opencart.sale.order',
|
'opencart.sale.order',
|
||||||
'opencart.sale.order.line',
|
'opencart.sale.order.line',
|
||||||
'opencart.stock.picking',
|
'opencart.stock.picking',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from . import delivery
|
from . import delivery
|
||||||
from . import opencart_backend
|
from . import opencart
|
||||||
from . import opencart_binding
|
|
||||||
from . import sale_order
|
from . import sale_order
|
||||||
from . import stock_picking
|
from . import stock_picking
|
||||||
|
|||||||
5
connector_opencart/models/opencart/__init__.py
Normal file
5
connector_opencart/models/opencart/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from . import backend
|
||||||
|
from . import backend_importer
|
||||||
|
from . import binding
|
||||||
|
from . import store
|
||||||
|
from . import store_importer
|
||||||
@@ -7,6 +7,7 @@ from logging import getLogger
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
from ...components.api.opencart import Opencart
|
from ...components.api.opencart import Opencart
|
||||||
|
|
||||||
_logger = getLogger(__name__)
|
_logger = getLogger(__name__)
|
||||||
@@ -79,6 +80,17 @@ class OpencartBackend(models.Model):
|
|||||||
with _super.work_on(model_name, opencart_api=opencart_api, **kwargs) as work:
|
with _super.work_on(model_name, opencart_api=opencart_api, **kwargs) as work:
|
||||||
yield 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
|
@api.model
|
||||||
def _scheduler_import_sale_orders(self):
|
def _scheduler_import_sale_orders(self):
|
||||||
# potential hook for customization (e.g. pad from date or provide its own)
|
# potential hook for customization (e.g. pad from date or provide its own)
|
||||||
19
connector_opencart/models/opencart/backend_importer.py
Normal file
19
connector_opencart/models/opencart/backend_importer.py
Normal file
@@ -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']
|
||||||
77
connector_opencart/models/opencart/store.py
Normal file
77
connector_opencart/models/opencart/store.py
Normal file
@@ -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))
|
||||||
27
connector_opencart/models/opencart/store_importer.py
Normal file
27
connector_opencart/models/opencart/store_importer.py
Normal file
@@ -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'
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import common
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import common
|
|
||||||
@@ -27,6 +27,7 @@ class OpencartSaleOrder(models.Model):
|
|||||||
inverse_name='opencart_order_id',
|
inverse_name='opencart_order_id',
|
||||||
string='Walmart Order Lines'
|
string='Walmart Order Lines'
|
||||||
)
|
)
|
||||||
|
store_id = fields.Many2one('opencart.store', string='Store')
|
||||||
|
|
||||||
total_amount = fields.Float(
|
total_amount = fields.Float(
|
||||||
string='Total amount',
|
string='Total amount',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from copy import deepcopy, copy
|
|||||||
from odoo import fields, _
|
from odoo import fields, _
|
||||||
from odoo.addons.component.core import Component
|
from odoo.addons.component.core import Component
|
||||||
from odoo.addons.connector.components.mapper import mapping
|
from odoo.addons.connector.components.mapper import mapping
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.addons.queue_job.exception import NothingToDoJob, FailedJobError
|
from odoo.addons.queue_job.exception import NothingToDoJob, FailedJobError
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@@ -41,13 +42,12 @@ class SaleOrderBatchImporter(Component):
|
|||||||
|
|
||||||
|
|
||||||
class SaleOrderImportMapper(Component):
|
class SaleOrderImportMapper(Component):
|
||||||
|
|
||||||
|
|
||||||
_name = 'opencart.sale.order.mapper'
|
_name = 'opencart.sale.order.mapper'
|
||||||
_inherit = 'opencart.import.mapper'
|
_inherit = 'opencart.import.mapper'
|
||||||
_apply_on = 'opencart.sale.order'
|
_apply_on = 'opencart.sale.order'
|
||||||
|
|
||||||
direct = [('order_id', 'external_id'),
|
direct = [('order_id', 'external_id'),
|
||||||
|
('store_id', 'store_id'),
|
||||||
# ('customerOrderId', 'customer_order_id'),
|
# ('customerOrderId', 'customer_order_id'),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ class SaleOrderImportMapper(Component):
|
|||||||
@mapping
|
@mapping
|
||||||
def name(self, record):
|
def name(self, record):
|
||||||
name = str(record['order_id'])
|
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:
|
if prefix:
|
||||||
name = prefix + name
|
name = prefix + name
|
||||||
return {'name': name}
|
return {'name': name}
|
||||||
@@ -99,13 +99,15 @@ class SaleOrderImportMapper(Component):
|
|||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def fiscal_position_id(self, record):
|
def fiscal_position_id(self, record):
|
||||||
if self.backend_record.fiscal_position_id:
|
fiscal_position = self.options.store.fiscal_position_id or self.backend_record.fiscal_position_id
|
||||||
return {'fiscal_position_id': self.backend_record.fiscal_position_id.id}
|
if fiscal_position:
|
||||||
|
return {'fiscal_position_id': fiscal_position.id}
|
||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def team_id(self, record):
|
def team_id(self, record):
|
||||||
if self.backend_record.team_id:
|
team = self.options.store.team_id or self.backend_record.team_id
|
||||||
return {'team_id': self.backend_record.team_id.id}
|
if team:
|
||||||
|
return {'team_id': team.id}
|
||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def payment_mode_id(self, record):
|
def payment_mode_id(self, record):
|
||||||
@@ -121,22 +123,37 @@ class SaleOrderImportMapper(Component):
|
|||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def project_id(self, record):
|
def project_id(self, record):
|
||||||
if self.backend_record.analytic_account_id:
|
analytic_account = self.options.store.analytic_account_id or self.backend_record.analytic_account_id
|
||||||
return {'project_id': self.backend_record.analytic_account_id.id}
|
if analytic_account:
|
||||||
|
return {'project_id': analytic_account.id}
|
||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def warehouse_id(self, record):
|
def warehouse_id(self, record):
|
||||||
if self.backend_record.warehouse_id:
|
warehouse = self.options.store.warehouse_id or self.backend_record.warehouse_id
|
||||||
return {'warehouse_id': self.backend_record.warehouse_id.id}
|
if warehouse:
|
||||||
|
return {'warehouse_id': warehouse.id}
|
||||||
|
|
||||||
@mapping
|
@mapping
|
||||||
def shipping_method(self, record):
|
def shipping_method(self, record):
|
||||||
method = record['shipping_method']
|
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:
|
if not carrier:
|
||||||
raise ValueError('Delivery Carrier for methodCode "%s", cannot be found.' % (method, ))
|
raise ValueError('Delivery Carrier for methodCode "%s", cannot be found.' % (method, ))
|
||||||
return {'carrier_id': carrier.id, 'shipping_method_code': 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
|
@mapping
|
||||||
def backend_id(self, record):
|
def backend_id(self, record):
|
||||||
return {'backend_id': self.backend_record.id}
|
return {'backend_id': self.backend_record.id}
|
||||||
@@ -275,14 +292,21 @@ class SaleOrderImporter(Component):
|
|||||||
"self.invoice_partner should have been defined "
|
"self.invoice_partner should have been defined "
|
||||||
"in SaleOrderImporter._import_addresses")
|
"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):
|
def _create_data(self, map_record, **kwargs):
|
||||||
# non dependencies
|
# non dependencies
|
||||||
|
# our current handling of partners doesn't require anything special for the store
|
||||||
self._check_special_fields()
|
self._check_special_fields()
|
||||||
|
store = self._get_store(map_record.source)
|
||||||
return super(SaleOrderImporter, self)._create_data(
|
return super(SaleOrderImporter, self)._create_data(
|
||||||
map_record,
|
map_record,
|
||||||
partner_id=self.partner.id,
|
partner_id=self.partner.id,
|
||||||
partner_invoice_id=self.invoice_partner.id,
|
partner_invoice_id=self.invoice_partner.id,
|
||||||
partner_shipping_id=self.shipping_partner.id,
|
partner_shipping_id=self.shipping_partner.id,
|
||||||
|
store=store,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -298,8 +322,7 @@ class SaleOrderImporter(Component):
|
|||||||
return binding
|
return binding
|
||||||
|
|
||||||
def _import_dependencies(self):
|
def _import_dependencies(self):
|
||||||
record = self.opencart_record
|
record = self.opencart_record # maybe iterate over products if we need to
|
||||||
|
|
||||||
self._import_addresses()
|
self._import_addresses()
|
||||||
|
|
||||||
class SaleOrderLineImportMapper(Component):
|
class SaleOrderLineImportMapper(Component):
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class StockPicking(models.Model):
|
|||||||
string="Opencart Bindings",
|
string="Opencart Bindings",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class StockPickingAdapter(Component):
|
class StockPickingAdapter(Component):
|
||||||
_name = 'opencart.stock.picking.adapter'
|
_name = 'opencart.stock.picking.adapter'
|
||||||
_inherit = 'opencart.adapter'
|
_inherit = 'opencart.adapter'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
"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_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_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","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_sale_order_line","opencart_sale_order_line connector manager","model_opencart_sale_order_line","connector.group_connector_manager",1,1,1,1
|
||||||
|
|||||||
|
@@ -7,6 +7,10 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Opencart Backend">
|
<form string="Opencart Backend">
|
||||||
<header>
|
<header>
|
||||||
|
<button name="synchronize_metadata"
|
||||||
|
type="object"
|
||||||
|
class="oe_highlight"
|
||||||
|
string="Synchronize Metadata"/>
|
||||||
</header>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
<label for="name" class="oe_edit_only"/>
|
<label for="name" class="oe_edit_only"/>
|
||||||
@@ -104,6 +108,59 @@
|
|||||||
<menuitem id="menu_opencart_backend"
|
<menuitem id="menu_opencart_backend"
|
||||||
name="Backends"
|
name="Backends"
|
||||||
parent="menu_opencart_root"
|
parent="menu_opencart_root"
|
||||||
|
sequence="10"
|
||||||
action="action_opencart_backend"/>
|
action="action_opencart_backend"/>
|
||||||
|
|
||||||
|
<!-- Store -->
|
||||||
|
<record id="view_opencart_store_form" model="ir.ui.view">
|
||||||
|
<field name="name">opencart.store.form</field>
|
||||||
|
<field name="model">opencart.store</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Opencart Store">
|
||||||
|
<header>
|
||||||
|
</header>
|
||||||
|
<sheet>
|
||||||
|
<label for="name" class="oe_edit_only"/>
|
||||||
|
<h1>
|
||||||
|
<field name="name" class="oe_inline" />
|
||||||
|
</h1>
|
||||||
|
<group name="main_configuration" string="Override Configuration">
|
||||||
|
<group name="order_configuration" string="Order Defaults">
|
||||||
|
<field name="warehouse_id"/>
|
||||||
|
<field name="analytic_account_id"/>
|
||||||
|
<field name="fiscal_position_id"/>
|
||||||
|
<field name="team_id"/>
|
||||||
|
<field name="sale_prefix"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_opencart_store_tree" model="ir.ui.view">
|
||||||
|
<field name="name">opencart.store.tree</field>
|
||||||
|
<field name="model">opencart.store</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Opencart Store">
|
||||||
|
<field name="backend_id"/>
|
||||||
|
<field name="name"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_opencart_store" model="ir.actions.act_window">
|
||||||
|
<field name="name">Opencart Stores</field>
|
||||||
|
<field name="res_model">opencart.store</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_id" ref="view_opencart_store_tree"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_opencart_store"
|
||||||
|
name="Stores"
|
||||||
|
parent="menu_opencart_root"
|
||||||
|
sequence="20"
|
||||||
|
action="action_opencart_store"/>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
Reference in New Issue
Block a user