mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[ADD] Commit of the modules used by C2C to move in our new public branch
(lp:c2c-addons/6.1 rev 2)
This commit is contained in:
1
base_product_merge/__init__.py
Normal file
1
base_product_merge/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import wizard
|
||||||
57
base_product_merge/__openerp__.py
Normal file
57
base_product_merge/__openerp__.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
|
||||||
|
# All Right Reserved
|
||||||
|
#
|
||||||
|
# Author : Guewen Baconnier (Camptocamp)
|
||||||
|
#
|
||||||
|
# WARNING: This program as such is intended to be used by professional
|
||||||
|
# programmers who take the whole responsability of assessing all potential
|
||||||
|
# consequences resulting from its eventual inadequacies and bugs
|
||||||
|
# End users who are looking for a ready-to-use solution with commercial
|
||||||
|
# garantees and support are strongly adviced to contract a Free Software
|
||||||
|
# Service Company
|
||||||
|
#
|
||||||
|
# This program is Free Software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
"name" : "Base Products Merge",
|
||||||
|
"version" : "1.0",
|
||||||
|
"author" : "Camptocamp",
|
||||||
|
"category" : "Generic Modules/Base",
|
||||||
|
"description":"""
|
||||||
|
To merge 2 products, select them in the list view and execute the Action "Merge Products".
|
||||||
|
|
||||||
|
The selected products are deactivated and a new one is created with :
|
||||||
|
- When a value is the same on each resources : the value
|
||||||
|
- When a value is different between the resources : you can choose the value to keep in a selection list
|
||||||
|
- When a value is set on a resource and is empty on the second one : the value is set on the resource
|
||||||
|
- All many2many relations of the 2 resources are created on the new resource.
|
||||||
|
- All the one2many relations (invoices, sale_orders, ...) are updated in order to link to the new resource.
|
||||||
|
|
||||||
|
""",
|
||||||
|
"website": "http://camptocamp.com",
|
||||||
|
"depends" : ['product'],
|
||||||
|
"init_xml" : [],
|
||||||
|
"demo_xml" : [],
|
||||||
|
"update_xml" : [
|
||||||
|
"wizard/base_product_merge_view.xml",
|
||||||
|
],
|
||||||
|
"active": False,
|
||||||
|
"installable": True
|
||||||
|
}
|
||||||
1
base_product_merge/wizard/__init__.py
Normal file
1
base_product_merge/wizard/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import base_product_merge
|
||||||
242
base_product_merge/wizard/base_product_merge.py
Normal file
242
base_product_merge/wizard/base_product_merge.py
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2011 Camptocamp SA
|
||||||
|
# @author Guewen Baconnier
|
||||||
|
# Original code from module base_partner_merge by Tiny and Camptocamp
|
||||||
|
#
|
||||||
|
# WARNING: This program as such is intended to be used by professional
|
||||||
|
# programmers who take the whole responsability of assessing all potential
|
||||||
|
# consequences resulting from its eventual inadequacies and bugs
|
||||||
|
# End users who are looking for a ready-to-use solution with commercial
|
||||||
|
# garantees and support are strongly adviced to contract a Free Software
|
||||||
|
# Service Company
|
||||||
|
#
|
||||||
|
# This program is Free Software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# TODO : create a base_merge module to provide abstractions for merges ?
|
||||||
|
|
||||||
|
from osv import fields, osv
|
||||||
|
from tools.translate import _
|
||||||
|
import tools
|
||||||
|
|
||||||
|
|
||||||
|
class base_product_merge(osv.osv_memory):
|
||||||
|
"""
|
||||||
|
Merges two products
|
||||||
|
"""
|
||||||
|
_name = 'base.product.merge'
|
||||||
|
_description = 'Merges two products'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
}
|
||||||
|
|
||||||
|
_values = {}
|
||||||
|
|
||||||
|
MERGE_SKIP_FIELDS = ['product_tmpl_id']
|
||||||
|
|
||||||
|
def _build_form(self, cr, uid, field_datas, value1, value2):
|
||||||
|
formxml = '''<?xml version="1.0"?>
|
||||||
|
<form string="%s">
|
||||||
|
<separator colspan="4" string="Select datas for new record"/>''' % _('Merge')
|
||||||
|
update_values = {}
|
||||||
|
update_fields = {}
|
||||||
|
columns = {}
|
||||||
|
|
||||||
|
for fid, fname, fdescription, ttype, required, relation, readonly in field_datas:
|
||||||
|
if fname in self.MERGE_SKIP_FIELDS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
val1 = value1[fname]
|
||||||
|
val2 = value2[fname]
|
||||||
|
my_selection = []
|
||||||
|
size = 24
|
||||||
|
|
||||||
|
if (val1 and val2) and (val1 == val2):
|
||||||
|
if ttype in ('many2one'):
|
||||||
|
update_values.update({fname: val1.id})
|
||||||
|
elif ttype in ('many2many'):
|
||||||
|
update_values.update({fname: [(6, 0, map(lambda x: x.id, val1))]})
|
||||||
|
else:
|
||||||
|
update_values.update({fname: val1})
|
||||||
|
|
||||||
|
if (val1 and val2) and (val1 != val2) and not readonly:
|
||||||
|
if ttype in ('char', 'text', 'selection'):
|
||||||
|
my_selection = [(val1, val1), (val2, val2)]
|
||||||
|
size = max(len(val1), len(val2))
|
||||||
|
if ttype in ('float', 'integer'):
|
||||||
|
my_selection = [(str(val1), str(val1)), (str(val2), str(val2))]
|
||||||
|
if ttype in ('many2one'):
|
||||||
|
my_selection = [(str(val1.id), val1.name),
|
||||||
|
(str(val2.id), val2.name)]
|
||||||
|
if ttype in ('many2many'):
|
||||||
|
update_values.update({fname: [(6, 0, list(set(map(lambda x: x.id, val1 + val2))))]})
|
||||||
|
if my_selection:
|
||||||
|
if not required:
|
||||||
|
my_selection.append((False, ''))
|
||||||
|
columns.update({fname: fields.selection(my_selection, fdescription, required=required, size=size)})
|
||||||
|
update_fields.update({fname: {'string': fdescription, 'type': 'selection', 'selection': my_selection, 'required': required}})
|
||||||
|
formxml += '\n<field name="%s"/><newline/>' % (fname,)
|
||||||
|
if (val1 and not val2) or (not val1 and val2):
|
||||||
|
if ttype == 'many2one':
|
||||||
|
update_values.update({fname: val1 and val1.id or val2 and val2.id})
|
||||||
|
elif ttype == 'many2many':
|
||||||
|
update_values.update({fname: [(6, 0, map(lambda x: x.id, val1 or val2))]})
|
||||||
|
elif ttype == 'one2many':
|
||||||
|
#skip one2many values
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
update_values.update({fname: val1 or val2})
|
||||||
|
|
||||||
|
formxml += """
|
||||||
|
<separator colspan="4"/>
|
||||||
|
<group col="4" colspan="4">
|
||||||
|
<label string="" colspan="2"/>
|
||||||
|
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
|
||||||
|
<button name="action_merge" string="Merge" type="object" icon="gtk-ok"/>
|
||||||
|
</group>
|
||||||
|
</form>"""
|
||||||
|
return formxml, update_fields, update_values, columns
|
||||||
|
|
||||||
|
def check_resources_to_merge(self, cr, uid, resource_ids, context):
|
||||||
|
""" Check validity of selected resources.
|
||||||
|
Hook for other checks
|
||||||
|
"""
|
||||||
|
if not len(resource_ids) == 2:
|
||||||
|
raise osv.except_osv(_('Error!'), _('You must select only two resources'))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||||
|
res = super(base_product_merge, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||||
|
resource_ids = context.get('active_ids') or []
|
||||||
|
|
||||||
|
self.check_resources_to_merge(cr, uid, resource_ids, context)
|
||||||
|
|
||||||
|
if not len(resource_ids) == 2:
|
||||||
|
return res
|
||||||
|
obj = self.pool.get('product.product')
|
||||||
|
cr.execute("SELECT id, name, field_description, ttype, required, relation, readonly from ir_model_fields where model in ('product.product', 'product.template')")
|
||||||
|
field_datas = cr.fetchall()
|
||||||
|
obj1 = obj.browse(cr, uid, resource_ids[0], context=context)
|
||||||
|
obj2 = obj.browse(cr, uid, resource_ids[1], context=context)
|
||||||
|
myxml, merge_fields, self._values, columns = self._build_form(cr, uid, field_datas, obj1, obj2)
|
||||||
|
self._columns.update(columns)
|
||||||
|
res['arch'] = myxml
|
||||||
|
res['fields'] = merge_fields
|
||||||
|
return res
|
||||||
|
|
||||||
|
def cast_many2one_fields(self, cr, uid, data_record, context=None):
|
||||||
|
""" Some fields are many2one and the ORM expect them to be integer or in the form
|
||||||
|
'relation,1' wher id is the id.
|
||||||
|
As some fields are displayed as selection in the view, we cast them in integer.
|
||||||
|
"""
|
||||||
|
cr.execute("SELECT name from ir_model_fields where model in ('product.product', 'product.template') and ttype='many2one'")
|
||||||
|
fields = cr.fetchall()
|
||||||
|
for field in fields:
|
||||||
|
if data_record.get(field[0], False):
|
||||||
|
data_record[field[0]] = int(data_record[field[0]])
|
||||||
|
return data_record
|
||||||
|
|
||||||
|
def action_merge(self, cr, uid, ids, context=None):
|
||||||
|
"""
|
||||||
|
Merges two resources and create 3rd and changes references of old resources with new
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user s ID for security checks,
|
||||||
|
@param ids: id of the wizard
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
|
||||||
|
@return : dict to open the new product in a view
|
||||||
|
"""
|
||||||
|
record_id = context and context.get('active_id', False) or False
|
||||||
|
pool = self.pool
|
||||||
|
if not record_id:
|
||||||
|
return {}
|
||||||
|
res = self.read(cr, uid, ids, context = context)[0]
|
||||||
|
|
||||||
|
res.update(self._values)
|
||||||
|
resource_ids = context.get('active_ids') or []
|
||||||
|
|
||||||
|
self.check_resources_to_merge(cr, uid, resource_ids, context)
|
||||||
|
|
||||||
|
resource1 = resource_ids[0]
|
||||||
|
resource2 = resource_ids[1]
|
||||||
|
|
||||||
|
obj, obj_parent = pool.get('product.product'), pool.get('product.template')
|
||||||
|
|
||||||
|
remove_field = {}
|
||||||
|
# for uniqueness constraint: empty the field in the old resources
|
||||||
|
c_names = []
|
||||||
|
for check_obj in (obj, obj_parent):
|
||||||
|
if hasattr(check_obj, '_sql_constraints'):
|
||||||
|
remove_field = {}
|
||||||
|
for const in check_obj._sql_constraints:
|
||||||
|
c_names.append(check_obj._name.replace('.', '_') + '_' + const[0])
|
||||||
|
if c_names:
|
||||||
|
c_names = tuple(map(lambda x: "'"+ x +"'", c_names))
|
||||||
|
cr.execute("""select column_name from \
|
||||||
|
information_schema.constraint_column_usage u \
|
||||||
|
join pg_constraint p on (p.conname=u.constraint_name) \
|
||||||
|
where u.constraint_name in (%s) and p.contype='u' """ % c_names)
|
||||||
|
for i in cr.fetchall():
|
||||||
|
remove_field[i[0]] = False
|
||||||
|
|
||||||
|
remove_field.update({'active': False})
|
||||||
|
|
||||||
|
obj.write(cr, uid, [resource1, resource2], remove_field, context=context)
|
||||||
|
|
||||||
|
res = self.cast_many2one_fields(cr, uid, res, context)
|
||||||
|
|
||||||
|
res_id = obj.create(cr, uid, res, context=context)
|
||||||
|
|
||||||
|
self.custom_updates(cr, uid, res_id, [resource1, resource2], context)
|
||||||
|
|
||||||
|
# For one2many fields on the resource
|
||||||
|
cr.execute("select name, model from ir_model_fields where relation in ('product.product', 'product.template') and ttype not in ('many2many', 'one2many');")
|
||||||
|
for name, model_raw in cr.fetchall():
|
||||||
|
if hasattr(pool.get(model_raw), '_auto'):
|
||||||
|
if not pool.get(model_raw)._auto:
|
||||||
|
continue
|
||||||
|
elif hasattr(pool.get(model_raw), '_check_time'):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if hasattr(pool.get(model_raw), '_columns'):
|
||||||
|
from osv import fields
|
||||||
|
if pool.get(model_raw)._columns.get(name, False) and isinstance(pool.get(model_raw)._columns[name], fields.many2one):
|
||||||
|
model = model_raw.replace('.', '_')
|
||||||
|
if name not in self.MERGE_SKIP_FIELDS:
|
||||||
|
cr.execute("update "+model+" set "+name+"="+str(res_id)+" where "+ tools.ustr(name) +" in ("+ tools.ustr(resource1) +", "+tools.ustr(resource2)+")")
|
||||||
|
|
||||||
|
value = {
|
||||||
|
'domain': str([('id', '=', res_id)]),
|
||||||
|
'view_type': 'form',
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
'res_model': 'product.product',
|
||||||
|
'view_id': False,
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_id': res_id
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
def custom_updates(self, cr, uid, resource_id, old_resources_ids, context):
|
||||||
|
"""Hook for special updates on old resources and new resource
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
base_product_merge()
|
||||||
|
|
||||||
39
base_product_merge/wizard/base_product_merge_view.xml
Normal file
39
base_product_merge/wizard/base_product_merge_view.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="view_base_product_merge_form">
|
||||||
|
<field name="name">base.product.merge.form</field>
|
||||||
|
<field name="model">base.product.merge</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Merge Products">
|
||||||
|
<label string="Merge Products"/>
|
||||||
|
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="action_base_product_merge_form" model="ir.actions.act_window">
|
||||||
|
<field name="name">Merge Products</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">base.product.merge</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="view_base_product_merge_form"/>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_base_product_merge_form" model="ir.values">
|
||||||
|
<field name="model_id" ref="product.model_product_product" />
|
||||||
|
<field name="object" eval="1" />
|
||||||
|
<field name="name">Merge Products</field>
|
||||||
|
<field name="key2">client_action_multi</field>
|
||||||
|
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_base_product_merge_form'))"/>
|
||||||
|
<field name="key">action</field>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
Reference in New Issue
Block a user