Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Magentoerpconnect catalog simple2 #200

Open
wants to merge 31 commits into
base: 8.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
daa0ddd
Prepare ground for a simple product catalog export
sebastienbeau Jun 18, 2014
7e18a3f
Add a finalizer and mapper ConnectorUnit for products imports
guewen Sep 8, 2015
b3a4f94
[ADD] add module 'magentoerpconnect_catalog_simple'
damdam-s Sep 8, 2015
5389da4
Organize the folders with subdirectories
guewen Sep 8, 2015
6e486e4
[FIX] remove 'status' field from view because 'status' field does not…
damdam-s Sep 9, 2015
51ba931
fixup! Add a finalizer and mapper ConnectorUnit for products imports
guewen Sep 9, 2015
e6ce09b
[IMP] add import of attribute set when importing product
damdam-s Sep 9, 2015
62aa15b
[IMP] use 'import_dependency' method instead of rewriting importation
damdam-s Sep 10, 2015
cd5feb7
[IMP] import attribute.set in base connector
damdam-s Sep 10, 2015
7f7c89b
[FIX] special binder for magento object which are not inherits from odoo
damdam-s Sep 11, 2015
249d0bf
[IMP] add export functionnality (default required fields in Magento)
damdam-s Sep 11, 2015
eb4e634
[IMP] do not allow to edit 'magento.attribute.set' in form view
damdam-s Sep 11, 2015
6aa1a51
[IMP] export/import tax_class_id field on magento
damdam-s Sep 11, 2015
91cffa2
[IMP] add translation exporter for product.product
damdam-s Sep 14, 2015
77b0fed
[IMP] remove readonly on some fields for export functionality
damdam-s Sep 16, 2015
066e8ff
[IMP] export translations
damdam-s Sep 16, 2015
ae62c9f
[FIX] tests
damdam-s Sep 17, 2015
cf60293
[FIX] test
damdam-s Sep 17, 2015
cf4f1e7
[IMP] fix tests because of new required fields
damdam-s Sep 17, 2015
a9a4ea4
[FIX] remove required on attribute_set_id field on product.template o…
damdam-s Sep 17, 2015
c8a5bad
[FIX] do not export a product twice
damdam-s Sep 17, 2015
39bac0c
[IMP] start testing product export
damdam-s Sep 17, 2015
76bb17e
[FIX] tests
damdam-s Sep 22, 2015
2fa8367
[FIX] tests
damdam-s Sep 23, 2015
26c67f9
[IMP] test write
damdam-s Sep 23, 2015
9793f9b
[FIX] export inventory on create product in Magento to be able to see it
damdam-s Oct 13, 2015
3f00f95
[IMP] add autobinding functionality
damdam-s Oct 13, 2015
40dfe4a
[IMP] add access rights
damdam-s Oct 14, 2015
6f46f5b
[IMP] export attribute set
damdam-s Oct 28, 2015
3ee9bb8
[FIX] do not autobind when importing a product
damdam-s Nov 2, 2015
cb46a23
[FIX] flake8
damdam-s Nov 27, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions magentoerpconnect/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ def delay_export_all_bindings(session, model_name, record_id, vals):
fields=fields)


def delay_unlink_all_bindings(session, model_name, record_id):
""" Delay jobs which delete all the bindings of a record.

In this case, it is called on records of normal models and will delay
the deletion for all the bindings.
"""
if session.context.get('connector_no_export'):
return
record = session.env[model_name].browse(record_id)
for binding in record.magento_bind_ids:
delay_unlink(session, binding._model._name, binding.id)


def delay_unlink(session, model_name, record_id):
""" Delay a job which delete a record on Magento.

Expand Down
18 changes: 17 additions & 1 deletion magentoerpconnect/magento_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def _get_stock_field_id(self):
limit=1)
return field

tax_imported = fields.Boolean(default=False)
version = fields.Selection(selection='select_versions', required=True)
location = fields.Char(
string='Location',
Expand Down Expand Up @@ -201,16 +202,31 @@ def check_magento_structure(self):

@api.multi
def synchronize_metadata(self):
mag_tax_class_obj = self.env['magento.tax.class']
default_tax_list = [
{'name': 'default', 'magento_id': 1},
{'name': 'Taxable Goods', 'magento_id': 2},
{'name': 'Shipping', 'magento_id': 4},
]
session = ConnectorSession(self.env.cr, self.env.uid,
context=self.env.context)
for backend in self:
for model in ('magento.website',
'magento.store',
'magento.storeview'):
'magento.storeview',
'magento.attribute.set'):
# import directly, do not delay because this
# is a fast operation, a direct return is fine
# and it is simpler to import them sequentially
import_batch(session, model, backend.id)

# create magento.tax.class records as we don't import them
if not backend.tax_imported:
for tax_dict in default_tax_list:
tax_dict.update(backend_id=backend.id)
mag_tax_class_obj.create(tax_dict)
backend.tax_imported = True

return True

@api.multi
Expand Down
6 changes: 6 additions & 0 deletions magentoerpconnect/magento_model_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
of the new records to review
in the menu 'Connectors > Checkpoint'.
</p>
<group>
<label string="Magento taxes already imported ?" class="oe_inline"/>
<div>
<field name="tax_imported"/>
</div>
</group>
<group>
<label string="Import all customer groups" class="oe_inline"/>
<div>
Expand Down
189 changes: 188 additions & 1 deletion magentoerpconnect/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import logging
import urllib2
import base64
import xmlrpclib
import sys
from collections import defaultdict
from openerp import models, fields, api, _
from openerp.addons.connector.connector import ConnectorUnit
from openerp.addons.connector.queue.job import job, related_action
from openerp.addons.connector.event import on_record_write
from openerp.addons.connector.unit.synchronizer import (Importer,
Expand All @@ -44,6 +44,7 @@
)
from .unit.mapper import normalize_datetime
from .unit.import_synchronizer import (DelayedBatchImporter,
DirectBatchImporter,
MagentoImporter,
TranslationImporter,
AddCheckpoint,
Expand All @@ -60,6 +61,104 @@ def chunks(items, length):
yield items[index:index + length]


class MagentoTaxClass(models.Model):
_name = 'magento.tax.class'
_inherit = 'magento.binding'
_description = 'Magento Tax Class'

name = fields.Char()


class MagentoAttributeSet(models.Model):
_name = 'magento.attribute.set'
_inherit = 'magento.binding'
_description = 'Magento Attribute Set'

name = fields.Char()


@magento
class AttributeSetAdapter(GenericAdapter):
_model_name = 'magento.attribute.set'
_magento_model = 'product_attribute_set'

def create(self, name, skeleton):
return self._call('%s.create' % self._magento_model,
[name, skeleton])

def list(self):
""" Search records according to some criteria
and returns a list of ids

:rtype: list
"""
return self._call('%s.list' % self._magento_model, [])

def read(self, id, attributes=None):
""" Returns the information of a record

:rtype: dict
"""
results = self.list()
res = [result for result in results if result['set_id'] == id]
if res:
return res[0]
return {}


@magento
class AttributeSetBatchImporter(DirectBatchImporter):
""" Import the records directly, without delaying the jobs.

Import the Attribute Set

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.
(that's also a mean to rapidly check the connectivity with Magento).
"""
_model_name = [
'magento.attribute.set'
]

def run(self, filters=None):
""" Run the synchronization """
records = self.backend_adapter.list()
for record in records:
importer = self.unit_for(MagentoImporter)
importer.run(record['set_id'], record=record)


@magento
class AttributeSetMapper(ImportMapper):
_model_name = 'magento.attribute.set'

direct = [('name', 'name')]

@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}


@magento
class AttributeSetImporter(MagentoImporter):
_model_name = ['magento.attribute.set']

def run(self, magento_id, force=False, record=None):
""" Run the synchronization

:param magento_id: identifier of the record on Magento
"""
if record:
self.magento_record = record
return super(AttributeSetImporter, self).run(magento_id, force=force)

def _get_magento_data(self):
if self.magento_record:
return self.magento_record
else:
return super(AttributeSetImporter, self)._get_magento_data()


class MagentoProductProduct(models.Model):
_name = 'magento.product.product'
_inherit = 'magento.binding'
Expand All @@ -78,6 +177,22 @@ def product_type_get(self):
# ('downloadable', 'Downloadable Product'),
]

@api.model
def get_default_magento_tax(self):
mag_tax_obj = self.env['magento.tax.class']
tax_id = False
if self.backend_id:
tax_id = mag_tax_obj.search(
[('backend_id', '=', self.backend_id.id)])[0]
else:
tax_id = mag_tax_obj.search([])[0]
return tax_id

tax_class_id = fields.Many2one(comodel_name='magento.tax.class',
string='Tax class',
required=True,
ondelete='restrict',
default=get_default_magento_tax)
openerp_id = fields.Many2one(comodel_name='product.product',
string='Product',
required=True,
Expand Down Expand Up @@ -119,6 +234,16 @@ def product_type_get(self):
help="Check this to exclude the product "
"from stock synchronizations.",
)
visibility = fields.Selection(
selection=[('1', 'Not Visible Individually'),
('2', 'Catalog'),
('3', 'Search'),
('4', 'Catalog, Search'),
],
string='Visibility',
default='4',
required=True,
)

RECOMPUTE_QTY_STEP = 1000 # products at a time

Expand Down Expand Up @@ -199,6 +324,13 @@ class ProductProduct(models.Model):
)


class ProductTemplate(models.Model):
_inherit = 'product.template'

attribute_set_id = fields.Many2one('magento.attribute.set',
string='Attribute Set')


@magento
class ProductProductAdapter(GenericAdapter):
_model_name = 'magento.product.product'
Expand Down Expand Up @@ -236,6 +368,11 @@ def search(self, filters=None, from_date=None, to_date=None):
in self._call('%s.list' % self._magento_model,
[filters] if filters else [{}])]

def create(self, product_type, attr_set_id, sku, data):
# Only ol_catalog_product.create works for export configurable product
return self._call('ol_catalog_product.create',
[product_type, attr_set_id, sku, data])

def read(self, id, storeview_id=None, attributes=None):
""" Returns the information of a record

Expand Down Expand Up @@ -418,6 +555,34 @@ def run(self, binding_id, magento_record):
"""


@magento
class WithCatalogProductImportMapper(ImportMapper):
""" Called at the end of the product Mapper

Does nothing, but is replaced in ``magentoerpconnect_catalog_simple``
which adds fields in the import.

"""
_model_name = 'magento.product.product'


@magento
class CatalogImportMapperFinalizer(ConnectorUnit):
""" Finalize values for a product

Does nothing else than calling :class:`WithCatalogProductImportMapper`
but is meant to be extended if required, for instance to remove values
from the imported values when we are importing products.
"""
_model_name = ['magento.product.product']

def finalize(self, map_record, values, options):
mapper = self.unit_for(WithCatalogProductImportMapper)
map_record = mapper.map_record(map_record.source)
values.update(map_record.values(**options))
return values


@magento
class ProductImportMapper(ImportMapper):
_model_name = 'magento.product.product'
Expand All @@ -431,8 +596,21 @@ class ProductImportMapper(ImportMapper):
('type_id', 'product_type'),
(normalize_datetime('created_at'), 'created_at'),
(normalize_datetime('updated_at'), 'updated_at'),
('visibility', 'visibility'),
]

@mapping
def map_attribute_set(self, record):
binder = self.binder_for(model='magento.attribute.set')
binding_id = binder.to_openerp(record['set'])
return {'attribute_set_id': binding_id}

@mapping
def map_tax_class(self, record):
binder = self.binder_for(model='magento.tax.class')
binding_id = binder.to_openerp(record['tax_class_id'])
return {'tax_class_id': binding_id}

@mapping
def is_active(self, record):
mapper = self.unit_for(IsActiveProductImportMapper)
Expand Down Expand Up @@ -502,6 +680,11 @@ def bundle_mapping(self, record):
bundle_mapper = self.unit_for(BundleProductImportMapper)
return bundle_mapper.map_record(record).values(**self.options)

def finalize(self, map_record, values):
values = super(ProductImportMapper, self).finalize(map_record, values)
finalizer = self.unit_for(CatalogImportMapperFinalizer)
return finalizer.finalize(map_record, values, self.options)


@magento
class ProductImporter(MagentoImporter):
Expand All @@ -527,6 +710,10 @@ def _import_dependencies(self):
if record['type_id'] == 'bundle':
self._import_bundle_dependencies()

if record.get('set', False):
self._import_dependency(record['set'],
'magento.attribute.set')

def _validate_product_type(self, data):
""" Check if the product type is in the selection (so we can
prevent the `except_orm` and display a better error message).
Expand Down
15 changes: 15 additions & 0 deletions magentoerpconnect/product_category.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class MagentoProductCategory(models.Model):
inverse_name='magento_parent_id',
string='Magento Child Categories',
)
is_active = fields.Boolean(string='Active in Magento',
default=True)
include_in_menu = fields.Boolean(string='Include in Magento menu',
default=False)


class ProductCategory(models.Model):
Expand Down Expand Up @@ -91,6 +95,15 @@ def _call(self, method, arguments):
else:
raise

def create(self, parent_id, data):
return self._call('%s.create' % self._magento_model,
[parent_id, data])

def write(self, id, data, storeview=False):
""" Update records on the external system """
return self._call('%s.update' % self._magento_model,
[int(id), data, storeview])

def search(self, filters=None, from_date=None, to_date=None):
""" Search records according to some criteria and return a
list of ids
Expand Down Expand Up @@ -239,6 +252,8 @@ class ProductCategoryImportMapper(ImportMapper):

direct = [
('description', 'description'),
('is_active', 'is_active'),
('include_in_menu', 'include_in_menu')
]

@mapping
Expand Down
3 changes: 3 additions & 0 deletions magentoerpconnect/product_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@
<group>
<field name="backend_id"/>
<field name="magento_id"/>
<field name="tax_class_id"/>
<field name="created_at" readonly="1"/>
<field name="updated_at" readonly="1"/>
<field name="visibility" />
<field name="product_type" readonly="1"/>
<field name="website_ids" />
</group>
<group string="Inventory Options">
<field name="no_stock_sync"/>
Expand Down
Loading