diff --git a/beesdoo_base/views/partner.xml b/beesdoo_base/views/partner.xml
index 37ceed8..c17325f 100644
--- a/beesdoo_base/views/partner.xml
+++ b/beesdoo_base/views/partner.xml
@@ -54,4 +54,14 @@
+
+
+
+ { 'search_default_supplier': 1,
+ 'default_customer': 0,
+ 'default_supplier': 1,
+ 'default_is_company' : True,
+ 'default_company_type' : 'company', }
+
+
\ No newline at end of file
diff --git a/beesdoo_base/wizard/new_member_card.py b/beesdoo_base/wizard/new_member_card.py
index 18899e4..7a894c8 100644
--- a/beesdoo_base/wizard/new_member_card.py
+++ b/beesdoo_base/wizard/new_member_card.py
@@ -3,8 +3,8 @@ from openerp import models, fields, api
class NewMemberCardWizard(models.TransientModel):
"""
- A transient model for the creation of a new card.
- The user can only define the raison why a new card is
+ A transient model for the creation of a new card.
+ The user can only define the raison why a new card is
needed and the eater/worker that is concerned.
"""
_name = 'membercard.new.wizard'
diff --git a/beesdoo_migration_asbl_to_coop/__init__.py b/beesdoo_migration_asbl_to_coop/__init__.py
new file mode 100644
index 0000000..7f367d6
--- /dev/null
+++ b/beesdoo_migration_asbl_to_coop/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Openerp sa ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import migration
diff --git a/beesdoo_migration_asbl_to_coop/__openerp__.py b/beesdoo_migration_asbl_to_coop/__openerp__.py
new file mode 100644
index 0000000..48ee0f1
--- /dev/null
+++ b/beesdoo_migration_asbl_to_coop/__openerp__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+{
+ 'name': 'Import data from ASBL',
+ 'version': '0.9',
+ 'category': 'Import',
+ 'description': """
+ This module provide a tools to import data from ASBL
+ """,
+ 'author': 'Thibault Francois',
+ 'website': 'https://github.com/tfrancoi/',
+ 'depends': ['base', 'import_base', 'import_odoo'],
+ 'data': [
+ 'view/migration.xml'
+ ],
+ 'test': [], #TODO provide test
+ 'installable': True,
+ 'auto_install': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/beesdoo_migration_asbl_to_coop/migration.py b/beesdoo_migration_asbl_to_coop/migration.py
new file mode 100644
index 0000000..112b755
--- /dev/null
+++ b/beesdoo_migration_asbl_to_coop/migration.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+
+from openerp import models, fields, api
+from openerp.exceptions import Warning
+
+from openerp.addons.import_base.import_framework import *
+from openerp.addons.import_base.mapper import *
+
+
+class odoo_connection_data(models.TransientModel):
+
+ _name = 'beesdoo.import.asbl'
+
+ @api.multi
+ def migrate(self):
+ imp = migration_framework(self, self.env.cr, self.env.uid, "Odoo", 'beesdoo.import.asbl', dict(self.env.context))
+ imp.launch_import()
+
+
+class migration_framework(import_framework):
+ black_list_field = {
+
+ }
+
+ tables = ['product.category',
+ 'product.uom',
+ 'product.uom.categ',
+ 'pos.category',
+ 'res.partner',
+ 'product.template',
+ 'product.supplierinfo',
+ ]
+
+ table_domain = {
+ 'res.partner' : [('supplier', '=', True), '|', ('active', '=', True), ('active', '=', False)],
+ 'product.template' : ['|', ('active', '=', True), ('active', '=', False)]
+ }
+
+
+ def initialize(self):
+ self.connection = self.obj.env['import.odoo.connection'].search([], limit=1)
+ self.set_table_list(self.tables)
+ print self.connection.name
+
+ def _get_field(self, model):
+ fields = ['id']
+
+ for mapper_object in self.get_mapping()[model.model_name]['map'].values():
+ if isinstance(mapper_object, basestring):
+ fields.append(mapper_object)
+ else:
+ fields.extend(mapper_object.get_fields())
+ print "read field", fields
+ return fields
+
+ def res_to_dict(self, fields, datas):
+ datas = datas['datas']
+ res = []
+ for data in datas:
+ data_dict = {}
+ for i, field in enumerate(fields):
+ data_dict[field] = data[i]
+ res.append(data_dict)
+ return res
+
+ def get_data(self, table):
+ con = self.connection._get_connection()
+ obj = con.get_model(table)
+ fields = self._get_field(obj)
+ ids = obj.search(self.table_domain.get(table, []))
+ datas = obj.export_data(ids, fields, context={'lang' : 'fr_BE'})
+ return self.res_to_dict(fields, datas)
+
+ def _generate_xml_id(self, name, table):
+ """
+ @param name: name of the object, has to be unique in for a given table
+ @param table : table where the record we want generate come from
+ @return: a unique xml id for record, the xml_id will be the same given the same table and same name
+ To be used to avoid duplication of data that don't have ids
+ """
+ return name
+
+ def get_mapping(self):
+ return {
+ 'product.category': {
+ 'model' : 'product.category',
+ 'dependencies' : [],
+ 'map' : {
+ 'name' : 'name',
+ 'parent_id/id_parent' : 'parent_id/id',
+ 'type' : 'type',
+
+ }
+ },
+ 'product.uom.categ' : {
+ 'model' : 'product.uom.categ',
+ 'dependencies' : [],
+ 'map' : {
+ 'name' : 'name',
+ }
+ },
+ 'product.uom': {
+ 'model' : 'product.uom',
+ 'dependencies' : ['product.uom.categ'],
+ 'map' : {
+ 'name' : 'name',
+ 'category_id/id' : 'category_id/id',
+ 'rounding' : 'rounding',
+ 'uom_type' : 'uom_type',
+ 'factor' : 'factor',
+ 'factor_inv' : 'factor_inv',
+ }
+ },
+ 'pos.category': {
+ 'model' : 'pos.category',
+ 'dependencies' : [],
+ 'map' : {
+ 'id' : 'id',
+ 'name' : 'name',
+ 'parent_id/id_parent' : 'parent_id/id',
+ }
+ },
+ 'res.partner': {
+ 'model' : 'res.partner',
+ 'dependencies' : [],
+ 'map' : {
+ 'active' : 'active',
+ 'barcode' : 'barcode',
+ 'birthdate' : 'birthdate',
+ 'city' : 'city',
+ 'comment' : 'comment',
+ 'company_type' : 'company_type',
+ 'contact_address' : 'contact_address',
+ 'country_id/id' : 'country_id/id',
+ 'email' : 'email',
+ 'employee' : 'employee',
+ 'fax' : 'fax',
+ 'first_name' : 'first_name',
+ 'function' : 'function',
+ 'is_company' : 'is_company',
+ 'lang' : 'lang',
+ 'last_name' : 'last_name',
+ 'mobile' : 'mobile',
+ 'name' : 'name',
+ 'parent_id/id_parent' : 'parent_id/id',
+ 'phone' : 'phone',
+ 'ref' : 'ref',
+ 'street' : 'street',
+ 'street2' : 'street2',
+ 'supplier' : 'supplier',
+ 'website' : 'website',
+ 'zip' : 'zip',
+ 'supplier' : 'supplier',
+ 'customer' : 'customer',
+ 'vat' : 'vat',
+ }
+ },
+ 'beesdoo.product.label' : {
+ 'model' : 'beesdoo.product.label',
+ 'dependencies' : [],
+ 'map' : {
+ 'color_code' : 'color_code',
+ 'name' : 'name',
+ 'type' : 'type',
+ }
+ },
+ 'product.template': {
+ 'model' : 'product.template',
+ 'dependencies' : ['pos.category', 'product.category', 'beesdoo.product.label'],
+ 'map' : {
+ 'active' : 'active',
+ 'available_in_pos' : 'available_in_pos',
+ 'barcode' : 'barcode',
+ 'categ_id/id' : 'categ_id/id',
+ 'default_code' : 'default_code',
+ 'description' : 'description',
+ 'description_picking' : 'description_picking',
+ 'description_purchase' : 'description_purchase',
+ 'description_sale' : 'description_sale',
+ 'eco_label/id' : 'eco_label/id',
+ 'fair_label/id' : 'fair_label/id',
+ 'invoice_policy' : 'invoice_policy',
+ 'local_label/id' : 'local_label/id',
+ 'name' : 'name',
+ 'origin_label/id' : 'origin_label/id',
+ 'pos_categ_id/id' : 'pos_categ_id/id',
+ 'purchase_ok' : 'purchase_ok',
+ 'sale_delay' : 'sale_delay',
+ 'sale_ok' : 'sale_ok',
+ 'standard_price' : 'standard_price',
+ 'supplier_taxes_id/id' : 'supplier_taxes_id/id', #Taxes problème
+ 'taxes_id/id' : 'taxes_id/id',
+ 'to_weight' : 'to_weight',
+ 'type' : 'type',
+ 'uom_id/id' : 'uom_id/id',
+ 'uom_po_id/id' : 'uom_po_id/id',
+ 'weight' : 'weight',
+ 'list_price' : 'list_price',
+ }
+ },
+ 'product.supplierinfo': {
+ 'model' : 'product.supplierinfo',
+ 'dependencies' : ['product.template'],
+ 'map' : {
+ 'delay' : 'delay',
+ 'min_qty' : 'min_qty',
+ 'name/id' : 'name/id',
+ 'price' : 'price',
+ 'product_code' : 'product_code',
+ 'product_name' : 'product_name',
+ 'product_uom/id' : 'product_uom/id',
+ 'date_start' : 'date_start',
+ 'date_end' : 'date_end',
+ 'product_tmpl_id/id': 'product_tmpl_id/id',
+ }
+ },
+ }
+
+
+
diff --git a/beesdoo_migration_asbl_to_coop/view/migration.xml b/beesdoo_migration_asbl_to_coop/view/migration.xml
new file mode 100644
index 0000000..568c4f8
--- /dev/null
+++ b/beesdoo_migration_asbl_to_coop/view/migration.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ beesdoo.import.asbl.form
+ beesdoo.import.asbl
+
+
+
+
+
+
+ Import From ASBL
+ beesdoo.import.asbl
+ form
+ form
+ new
+
+
+
+
+
diff --git a/beesdoo_product/__openerp__.py b/beesdoo_product/__openerp__.py
index bc1c3ea..cdf8dd5 100644
--- a/beesdoo_product/__openerp__.py
+++ b/beesdoo_product/__openerp__.py
@@ -20,7 +20,7 @@
'version': '0.1',
# any module necessary for this one to work correctly
- 'depends': ['beesdoo_base', 'product'],
+ 'depends': ['beesdoo_base', 'product', 'point_of_sale'],
# always loaded
'data': [
diff --git a/beesdoo_product/models/beesdoo_product.py b/beesdoo_product/models/beesdoo_product.py
index ad95630..666c422 100644
--- a/beesdoo_product/models/beesdoo_product.py
+++ b/beesdoo_product/models/beesdoo_product.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
+from openerp.tools.translate import _
class BeesdooProduct(models.Model):
_inherit = "product.template"
@@ -9,6 +10,17 @@ class BeesdooProduct(models.Model):
fair_label = fields.Many2one('beesdoo.product.label', domain = [('type', '=', 'fair')])
origin_label = fields.Many2one('beesdoo.product.label', domain = [('type', '=', 'delivery')])
+ main_seller_id = fields.Many2one('res.partner', compute='_compute_main_seller_id', store=True)
+
+ @api.one
+ @api.depends('seller_ids', 'seller_ids.date_start')
+ def _compute_main_seller_id(self):
+ # Calcule le vendeur associé qui a la date de début la plus récente et plus petite qu’aujourd’hui
+ sellers_ids = self.seller_ids.sorted(key=lambda seller: seller.date_start, reverse=True)
+ self.main_seller_id = sellers_ids and sellers_ids[0].name or False
+
+
+
class BeesdooProductLabel(models.Model):
_name = "beesdoo.product.label"
diff --git a/beesdoo_product/views/beesdoo_product.xml b/beesdoo_product/views/beesdoo_product.xml
index 21b329b..b829117 100644
--- a/beesdoo_product/views/beesdoo_product.xml
+++ b/beesdoo_product/views/beesdoo_product.xml
@@ -1,5 +1,7 @@
+
+
bees.product.template.form
product.template
@@ -14,6 +16,17 @@
+
+ bees.product.template.form2
+ product.template
+
+
+
+
+
+
+
+
bees.product.label.form
beesdoo.product.label
diff --git a/beesdoo_purchase/__init__.py b/beesdoo_purchase/__init__.py
new file mode 100644
index 0000000..0f7cb6b
--- /dev/null
+++ b/beesdoo_purchase/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+import models
\ No newline at end of file
diff --git a/beesdoo_purchase/__openerp__.py b/beesdoo_purchase/__openerp__.py
new file mode 100644
index 0000000..37645c0
--- /dev/null
+++ b/beesdoo_purchase/__openerp__.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': "Bees Purchase",
+
+ 'summary': """
+ Extension du module Purchase""",
+
+ 'description': """
+ Long description of module's purpose
+ """,
+
+ 'author': "Beescoop - Cellule IT",
+ 'website': "https://github.com/beescoop/Obeesdoo",
+
+ # Categories can be used to filter modules in modules listing
+ # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
+ # for the full list
+ 'category': 'Purchase',
+ 'version': '0.1',
+
+ # any module necessary for this one to work correctly
+ 'depends': ['purchase','beesdoo_product'],
+
+ # always loaded
+ 'data': [
+ 'views/purchase_order.xml',
+ 'security/ir.model.access.csv',
+ ],
+ # only loaded in demonstration mode
+ 'demo': [],
+}
\ No newline at end of file
diff --git a/beesdoo_purchase/models/__init__.py b/beesdoo_purchase/models/__init__.py
new file mode 100644
index 0000000..633f866
--- /dev/null
+++ b/beesdoo_purchase/models/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+
diff --git a/beesdoo_purchase/security/ir.model.access.csv b/beesdoo_purchase/security/ir.model.access.csv
new file mode 100644
index 0000000..58262d4
--- /dev/null
+++ b/beesdoo_purchase/security/ir.model.access.csv
@@ -0,0 +1 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
\ No newline at end of file
diff --git a/beesdoo_purchase/views/purchase_order.xml b/beesdoo_purchase/views/purchase_order.xml
new file mode 100644
index 0000000..ff6296c
--- /dev/null
+++ b/beesdoo_purchase/views/purchase_order.xml
@@ -0,0 +1,14 @@
+
+
+
+ beesdoo.purchase.order.form.view
+ purchase.order
+
+
+
+ [('main_seller_id','=', parent.partner_id)]
+
+
+
+
+
\ No newline at end of file
diff --git a/import_base/__init__.py b/import_base/__init__.py
new file mode 100644
index 0000000..52366e5
--- /dev/null
+++ b/import_base/__init__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Openerp sa ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import import_framework
+import mapper
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/import_base/__openerp__.py b/import_base/__openerp__.py
new file mode 100644
index 0000000..08f06eb
--- /dev/null
+++ b/import_base/__openerp__.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+{
+ 'name': 'Framework for complex import',
+ 'version': '0.9',
+ 'category': 'Hidden/Dependency',
+ 'description': """
+ This module provide a class import_framework to help importing
+ complex data from other software
+ """,
+ 'author': 'OpenERP SA',
+ 'website': 'http://www.openerp.com',
+ 'depends': ['base'],
+ 'init_xml': [],
+ 'update_xml': [],
+ 'demo_xml': [],
+ 'test': [], #TODO provide test
+ 'installable': True,
+ 'auto_install': False,
+ 'certificate': '00141537995453',
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/import_base/import_framework.py b/import_base/import_framework.py
new file mode 100644
index 0000000..5deea22
--- /dev/null
+++ b/import_base/import_framework.py
@@ -0,0 +1,415 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+import pprint
+import mapper
+from openerp.tools.translate import _
+
+import datetime
+import logging
+import StringIO
+import traceback
+pp = pprint.PrettyPrinter(indent=4)
+
+
+
+
+class import_framework():
+ """
+ This class should be extends,
+ get_data and get_mapping have to extends
+ get_state_map and initialize can be extended
+ for advanced purpose get_default_hook can also be extended
+ @see dummy import for a minimal exemple
+ """
+
+ """
+ for import_object, this domain will avoid to find an already existing object
+ """
+ DO_NOT_FIND_DOMAIN = [('id', '=', 0)]
+
+ #TODO don't use context to pass credential parameters
+ def __init__(self, obj, cr, uid, instance_name, module_name, context=None):
+ self.external_id_field = 'id'
+ self.obj = obj
+ self.cr = cr
+ self.uid = uid
+ self.instance_name = instance_name
+ self.module_name = module_name
+ self.context = context or {}
+ self.table_list = []
+ self.logger = logging.getLogger(module_name)
+ self.initialize()
+
+ """
+ Abstract Method to be implemented in
+ the real instance
+ """
+ def initialize(self):
+ """
+ init before import
+ usually for the login
+ """
+ pass
+
+ def init_run(self):
+ """
+ call after intialize run in the thread, not in the main process
+ TO use for long initialization operation
+ """
+ pass
+
+ def get_data(self, table):
+ """
+ @return: a list of dictionaries
+ each dictionnaries contains the list of pair external_field_name : value
+ """
+ return [{}]
+
+ def get_link(self, from_table, ids, to_table):
+ """
+ @return: a dictionaries that contains the association between the id (from_table)
+ and the list (to table) of id linked
+ """
+ return {}
+
+ def get_external_id(self, data):
+ """
+ @return the external id
+ the default implementation return self.external_id_field (that has 'id') by default
+ if the name of id field is different, you can overwrite this method or change the value
+ of self.external_id_field
+ """
+ return data[self.external_id_field]
+
+ def get_mapping(self):
+ """
+ @return: { TABLE_NAME : {
+ 'model' : 'openerp.model.name',
+ #if true import the table if not just resolve dependencies, use for meta package, by default => True
+ #Not required
+ 'import' : True or False,
+ #Not required
+ 'dependencies' : [TABLE_1, TABLE_2],
+ #Not required
+ 'hook' : self.function_name, #get the val dict of the object, return the same val dict or False
+ 'map' : { @see mapper
+ 'openerp_field_name' : 'external_field_name', or val('external_field_name')
+ 'openerp_field_id/id' : ref(TABLE_1, 'external_id_field'), #make the mapping between the external id and the xml on the right
+ 'openerp_field2_id/id_parent' : ref(TABLE_1,'external_id_field') #indicate a self dependencies on openerp_field2_id
+ 'state' : map_val('state_equivalent_field', mapping), # use get_state_map to make the mapping between the value of the field and the value of the state
+ 'text_field' : concat('field_1', 'field_2', .., delimiter=':'), #concat the value of the list of field in one
+ 'description_field' : ppconcat('field_1', 'field_2', .., delimiter='\n\t'), #same as above but with a prettier formatting
+ 'field' : call(callable, arg1, arg2, ..), #call the function with all the value, the function should send the value : self.callable
+ 'field' : callable
+ 'field' : call(method, val('external_field') interface of method is self, val where val is the value of the field
+ 'field' : const(value) #always set this field to value
+ + any custom mapper that you will define
+ }
+ },
+
+ }
+ """
+ return {}
+
+ def default_hook(self, val):
+ """
+ this hook will be apply on each table that don't have hook
+ here we define the identity hook
+ """
+ return val
+
+ def _import_table(self, table):
+ self.logger.info('Import table %s' % table)
+ data = self.get_data(table)
+ map = self.get_mapping()[table]['map']
+ hook = self.get_mapping()[table].get('hook', self.default_hook)
+ model = self.get_mapping()[table]['model']
+
+ final_data = []
+ for val in data:
+ res = hook(val)
+ if res:
+ final_data.append(res)
+ return self._save_data(model, dict(map), final_data, table)
+
+ def _save_data(self, model, mapping, datas, table):
+ """
+ @param model: the model of the object to import
+ @param table : the external table where the data come from
+ @param mapping : definition of the mapping
+ @see: get_mapping
+ @param datas : list of dictionnaries
+ datas = [data_1, data_2, ..]
+ data_i is a map external field_name => value
+ and each data_i have a external id => in data_id['id']
+ """
+ self.logger.info(' Importing %s into %s' % (table, model))
+ if not datas:
+ return (0, 'No data found')
+ mapping['id'] = 'id_new'
+ res = []
+
+
+ self_dependencies = []
+ for k in mapping.keys():
+ if '_parent' in k:
+ self_dependencies.append((k[:-7], mapping.pop(k)))
+ for data in datas:
+ for k, field_name in self_dependencies:
+ data[k] = data.get(field_name) and self._generate_xml_id(data.get(field_name), table)
+
+ data['id_new'] = self._generate_xml_id(self.get_external_id(data), table)
+ fields, values = self._fields_mapp(data, mapping, table)
+ res.append(values)
+
+ model_obj = self.obj.pool.get(model)
+ if not model_obj:
+ raise ValueError(_("%s is not a valid model name") % model)
+ self.logger.info(_("fields imported : ") + str(fields))
+ (p, r, warning, s) = model_obj.import_data(self.cr, self.uid, fields, res, mode='update', current_module=self.module_name, noupdate=False, context=self.context)
+ self.logger.info('%s %s %s %s %s' % ("Done", p, r, warning, s))
+ for (field, field_name) in self_dependencies:
+ self.logger.info('Import parent %s' % field)
+ self._import_self_dependencies(model_obj, field, datas)
+ return (len(res), warning)
+
+ def _import_self_dependencies(self, obj, parent_field, datas):
+ """
+ @param parent_field: the name of the field that generate a self_dependencies, we call the object referenced in this
+ field the parent of the object
+ @param datas: a list of dictionnaries
+ Dictionnaries need to contains
+ id_new : the xml_id of the object
+ field_new : the xml_id of the parent
+ """
+ fields = ['id', parent_field]
+ for data in datas:
+ if data.get(parent_field):
+ values = [data['id_new'], data[parent_field]]
+ res = obj.import_data(self.cr, self.uid, fields, [values], mode='update', current_module=self.module_name, noupdate=False, context=self.context)
+
+ def _preprocess_mapping(self, mapping):
+ """
+ Preprocess the mapping :
+ after the preprocces, everything is
+ callable in the val of the dictionary
+
+ use to allow syntaxical sugar like 'field': 'external_field'
+ instead of 'field' : value('external_field')
+ """
+ map = dict(mapping)
+ for key, value in map.items():
+ if isinstance(value, basestring):
+ map[key] = mapper.value(value)
+ #set parent for instance of dbmapper
+ elif isinstance(value, mapper.dbmapper):
+ value.set_parent(self)
+ return map
+
+
+ def _fields_mapp(self,dict_sugar, openerp_dict, table):
+ """
+ call all the mapper and transform data
+ to be compatible with import_data
+ """
+ fields=[]
+ data_lst = []
+ mapping = self._preprocess_mapping(openerp_dict)
+ for key,val in mapping.items():
+ if key not in fields and dict_sugar:
+ fields.append(key)
+ value = val(dict(dict_sugar))
+ data_lst.append(value)
+ return fields, data_lst
+
+ def _generate_xml_id(self, name, table):
+ """
+ @param name: name of the object, has to be unique in for a given table
+ @param table : table where the record we want generate come from
+ @return: a unique xml id for record, the xml_id will be the same given the same table and same name
+ To be used to avoid duplication of data that don't have ids
+ """
+ sugar_instance = self.instance_name
+ name = name.replace('.', '_').replace(',', '_')
+ return sugar_instance + "_" + table + "_" + name
+
+
+ """
+ Public interface of the framework
+ those function can be use in the callable function defined in the mapping
+ """
+ def xml_id_exist(self, table, external_id):
+ """
+ Check if the external id exist in the openerp database
+ in order to check if the id exist the table where it come from
+ should be provide
+ @return the xml_id generated if the external_id exist in the database or false
+ """
+ if not external_id:
+ return False
+
+ xml_id = self._generate_xml_id(external_id, table)
+ id = self.obj.pool.get('ir.model.data').search(self.cr, self.uid, [('name', '=', xml_id), ('module', '=', self.module_name)])
+ return id and xml_id or False
+
+ def name_exist(self, table, name, model):
+ """
+ Check if the object with the name exist in the openerp database
+ in order to check if the id exist the table where it come from
+ should be provide and the model of the object
+ """
+ fields = ['name']
+ data = [name]
+ return self.import_object(fields, data, model, table, name, [('name', '=', name)])
+
+ def get_mapped_id(self, table, external_id, context=None):
+ """
+ @return return the databse id linked with the external_id
+ """
+ if not external_id:
+ return False
+
+ xml_id = self._generate_xml_id(external_id, table)
+ return self.obj.pool.get('ir.model.data').get_object_reference(self.cr, self.uid, self.module_name, xml_id)[1]
+
+ def import_object_mapping(self, mapping, data, model, table, name, domain_search=False):
+ """
+ same as import_objects but instead of two list fields and data,
+ this method take a dictionnaries : external_field : value
+ and the mapping similar to the one define in 'map' key
+ @see import_object, get_mapping
+ """
+ fields, datas = self._fields_mapp(data, mapping, table)
+ return self.import_object(fields, datas, model, table, name, domain_search)
+
+ def import_object(self, fields, data, model, table, name, domain_search=False):
+ """
+ This method will import an object in the openerp, usefull for field that is only a char in sugar and is an object in openerp
+ use import_data that will take care to create/update or do nothing with the data
+ this method return the xml_id
+
+ To be use, when you want to create an object or link if already exist
+ use DO_NOT_LINK_DOMAIN to create always a new object
+ @param fields: list of fields needed to create the object without id
+ @param data: the list of the data, in the same order as the field
+ ex : fields = ['firstname', 'lastname'] ; data = ['John', 'Mc donalds']
+ @param model: the openerp's model of the create/update object
+ @param table: the table where data come from in sugarcrm, no need to fit the real name of openerp name, just need to be unique
+ @param unique_name: the name of the object that we want to create/update get the id
+ @param domain_search : the domain that should find the unique existing record
+
+ @return: the xml_id of the ressources
+ """
+ domain_search = not domain_search and [('name', 'ilike', name)] or domain_search
+ obj = self.obj.pool.get(model)
+ if not obj: #if the model doesn't exist
+ return False
+
+ xml_id = self._generate_xml_id(name, table)
+ xml_ref = self.mapped_id_if_exist(model, domain_search, table, name)
+ fields.append('id')
+ data.append(xml_id)
+ obj.import_data(self.cr, self.uid, fields, [data], mode='update', current_module=self.module_name, noupdate=True, context=self.context)
+ return xml_ref or xml_id
+
+
+ def mapped_id_if_exist(self, model, domain, table, name):
+ """
+ To be use when we want link with and existing object, if the object don't exist
+ just ignore.
+ @param domain : search domain to find existing record, should return a unique record
+ @param xml_id: xml_id give to the mapping
+ @param name: external_id or name of the object to create if there is no id
+ @param table: the name of the table of the object to map
+ @return : the xml_id if the record exist in the db, False otherwise
+ """
+ obj = self.obj.pool.get(model)
+ ids = obj.search(self.cr, self.uid, domain, context=self.context)
+ if ids:
+ xml_id = self._generate_xml_id(name, table)
+ ir_model_data_obj = obj.pool.get('ir.model.data')
+ id = ir_model_data_obj._update(self.cr, self.uid, model,
+ self.module_name, {}, mode='update', xml_id=xml_id,
+ noupdate=True, res_id=ids[0], context=self.context)
+ return xml_id
+ return False
+
+
+ def set_table_list(self, table_list):
+ """
+ Set the list of table to import, this method should be call before run
+ @param table_list: the list of external table to import
+ ['Leads', 'Opportunity']
+ """
+ self.table_list = table_list
+
+ def launch_import(self):
+ """
+ Import all data into openerp,
+ this is the Entry point to launch the process of import
+
+
+ """
+ self.data_started = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ error = False
+ result = []
+ try:
+ self.init_run()
+ imported = set() #to invoid importing 2 times the sames modules
+ for table in self.table_list:
+ to_import = self.get_mapping()[table].get('import', True)
+ if not table in imported:
+ res = self._resolve_dependencies(self.get_mapping()[table].get('dependencies', []), imported)
+ result.extend(res)
+ if to_import:
+ (position, warning) = self._import_table(table)
+ result.append((table, position, warning))
+ imported.add(table)
+ self.cr.commit()
+
+ except Exception, err:
+ sh = StringIO.StringIO()
+ traceback.print_exc(file=sh)
+ error = sh.getvalue()
+ self.logger.error(error)
+
+
+ self.date_ended = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ def _resolve_dependencies(self, dep, imported):
+ """
+ import dependencies recursively
+ and avoid to import twice the same table
+ """
+ result = []
+ for dependency in dep:
+ if not dependency in imported:
+ to_import = self.get_mapping()[dependency].get('import', True)
+ res = self._resolve_dependencies(self.get_mapping()[dependency].get('dependencies', []), imported)
+ result.extend(res)
+ if to_import:
+ r = self._import_table(dependency)
+ (position, warning) = r
+ result.append((dependency, position, warning))
+ imported.add(dependency)
+ return result
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/import_base/mapper.py b/import_base/mapper.py
new file mode 100644
index 0000000..e17a423
--- /dev/null
+++ b/import_base/mapper.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+from openerp import tools
+
+class mapper(object):
+ """
+ super class for all mapper class
+ They are call before import data
+ to transform the mapping into real value that we
+ will import
+
+ the call function receive a dictionary with external data
+ 'external_field' : value
+ """
+ def __call__(self, external_values):
+ raise NotImplementedError()
+
+ def get_fields(self):
+ return []
+
+class dbmapper(mapper):
+ """
+ Super class for mapper that need to access to
+ data base or any function of the import_framework
+
+ self.parent contains a reference to the instance of
+ the import framework
+ """
+ def set_parent(self, parent):
+ self.parent = parent
+
+
+class concat(mapper):
+ """
+ Use : contact('field_name1', 'field_name2', delimiter='_')
+ concat value of fields using the delimiter, delimiter is optional
+ and by default is a space
+ """
+ def __init__(self, *arg, **delimiter):
+ self.arg = arg
+ self.delimiter = delimiter and delimiter.get('delimiter', ' ') or ' '
+
+ def __call__(self, external_values):
+ return self.delimiter.join(map(lambda x : tools.ustr(external_values.get(x,'')), self.arg))
+
+ def get_fields(self):
+ return self.arg
+
+class ppconcat(concat):
+ """
+ Use : contact('field_name1', 'field_name2', delimiter='_')
+ concat external field name and value of fields using the delimiter,
+ delimiter is optional and by default is a two line feeds
+
+ """
+ def __init__(self, *arg, **delimiter):
+ self.arg = arg
+ self.delimiter = delimiter and delimiter.get('delimiter', ' ') or '\n\n'
+
+ def __call__(self, external_values):
+ return self.delimiter.join(map(lambda x : x + ": " + tools.ustr(external_values.get(x,'')), self.arg))
+
+class const(mapper):
+ """
+ Use : const(arg)
+ return always arg
+ """
+ def __init__(self, val):
+ self.val = val
+
+ def __call__(self, external_values):
+ return self.val
+
+class value(mapper):
+ """
+ Use : value(external_field_name)
+ Return the value of the external field name
+ this is equivalent to the a single string
+
+ usefull for call if you want your call get the value
+ and don't care about the name of the field
+ call(self.method, value('field1'))
+ """
+ def __init__(self, val, default='', fallback=False):
+ self.val = val
+ self.default = default
+ self.fallback = fallback
+
+ def __call__(self, external_values):
+ val = external_values.get(self.val, self.default)
+ if self.fallback and (not val or val == self.default):
+ val = external_values.get(self.fallback, self.default)
+ return val
+
+ def get_fields(self):
+ return [self.val]
+
+
+class map_val(mapper):
+ """
+ Use : map_val(external_field, val_mapping)
+ where val_mapping is a dictionary
+ with external_val : openerp_val
+
+ usefull for selection field like state
+ to map value
+ """
+ def __init__(self, val, map, default='draft'):
+ self.val = value(val)
+ self.map = map
+ self.default = default
+
+ def __call__(self, external_values):
+ return self.map.get(self.val(external_values), self.default)
+
+ def get_fields(self):
+ return self.val.get_fields()
+
+class map_val_default(mapper):
+ """
+ Use : map_val(external_field, val_mapping)
+ where val_mapping is a dictionary
+ with external_val : openerp_val
+
+ usefull for selection field like state
+ to map value
+ """
+ def __init__(self, val, map):
+ self.val = value(val)
+ self.map = map
+
+ def __call__(self, external_values):
+ value = self.map.get(self.val(external_values), self.val(external_values))
+ return value
+
+ def get_fields(self):
+ return self.val.get_fields()
+
+class ref(dbmapper):
+ """
+ Use : ref(table_name, external_id)
+ return the xml_id of the ressource
+
+ to associate an already imported object with the current object
+ """
+ def __init__(self, table, field_name):
+ self.table = table
+ self.field_name = field_name
+
+ def __call__(self, external_values):
+ return self.parent.xml_id_exist(self.table, external_values.get(self.field_name))
+
+ def get_fields(self):
+ return self.field_name
+
+class refbyname(dbmapper):
+ """
+ Use : refbyname(table_name, external_name, res.model)
+ same as ref but use the name of the ressource to find it
+ """
+ def __init__(self, table, field_name, model):
+ self.table = table
+ self.field_name = field_name
+ self.model = model
+
+ def __call__(self, external_values):
+ v = external_values.get(self.field_name, '')
+ return self.parent.name_exist(self.table, v , self.model)
+
+ def get_fields(self):
+ return self.field_name
+
+class call(mapper):
+ """
+ Use : call(function, arg1, arg2)
+ to call the function with external val follow by the arg specified
+ """
+ def __init__(self, fun, *arg):
+ self.fun = fun
+ self.arg = arg
+
+ def __call__(self, external_values):
+ args = []
+ for arg in self.arg:
+ if isinstance(arg, mapper):
+ args.append(arg(external_values))
+ else:
+ args.append(arg)
+ return self.fun(external_values, *args)
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
+
+def val(field_name):
+ def val_fun(data_line):
+ return data_line[field_name]
+
+ return val_fun
+
+def const(const):
+ def val_fun(data_line):
+ return const
diff --git a/import_odoo/__init__.py b/import_odoo/__init__.py
new file mode 100644
index 0000000..7f1a5f6
--- /dev/null
+++ b/import_odoo/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Openerp sa ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import odoo_connector
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/import_odoo/__openerp__.py b/import_odoo/__openerp__.py
new file mode 100644
index 0000000..18fc142
--- /dev/null
+++ b/import_odoo/__openerp__.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+{
+ 'name': 'Framework to import from other odoo instance',
+ 'version': '0.9',
+ 'category': 'Import',
+ 'description': """
+ This module provide a class import_framework to help importing
+ data from odoo
+ """,
+ 'author': 'Thibault Francois',
+ 'website': 'https://github.com/tfrancoi/',
+ 'depends': ['base', 'import_base'],
+ 'data': [
+ 'view/connector.xml'
+ ],
+ 'test': [], #TODO provide test
+ 'installable': True,
+ 'auto_install': False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/import_odoo/odoo_connector.py b/import_odoo/odoo_connector.py
new file mode 100644
index 0000000..205d50a
--- /dev/null
+++ b/import_odoo/odoo_connector.py
@@ -0,0 +1,38 @@
+'''
+Created on 25 nov. 2014
+
+@author: openerp
+'''
+from openerp import models, fields, api
+import openerplib
+from openerp.exceptions import Warning
+
+
+
+
+class odoo_connection_data(models.Model):
+
+ _name = 'import.odoo.connection'
+
+ name = fields.Char("Name", required=True)
+ host = fields.Char("Host", required=True)
+ port = fields.Integer("Port", required=True, default=8069)
+ database = fields.Char("Database", required=True)
+ user = fields.Char("Login", required=True, default="admin")
+ password = fields.Char("Password", required=True)
+ protocol = fields.Selection([('xmlrpc', 'Xmlrpc'), ('jsonrpc', 'Jsonrpc'),('xmlrpcs', 'Xmlrpcs'), ('jsonrpcs', 'Jsonrpcs')], string="Protocol", default="xmlrpc")
+ active = fields.Boolean("Active", default=True)
+
+ @api.multi
+ def test_connection(self):
+ connection = self._get_connection()
+ connection.check_login(force=True)
+ raise Warning("Connection Successful")
+
+ def _get_connection(self):
+ return openerplib.get_connection(hostname=self.host,
+ port=self.port,
+ database=self.database,
+ login=self.user,
+ password=self.password,
+ protocol=self.protocol)
diff --git a/import_odoo/view/connector.xml b/import_odoo/view/connector.xml
new file mode 100644
index 0000000..357e500
--- /dev/null
+++ b/import_odoo/view/connector.xml
@@ -0,0 +1,67 @@
+
+
+
+
+ import.odoo.connection.tree
+ import.odoo.connection
+
+
+
+
+
+
+
+
+
+
+ import.odoo.connection.form
+ import.odoo.connection
+
+
+
+
+
+
+
+
+ Odoo Connector
+ import.odoo.connection
+ form
+ tree,form
+
+
+
+
+
+
+
+
+
+