Merge lp:~akretion-team/openerp-product-variant/product-variant-multi-attribute into lp:openerp-product-variant/trunk

Proposed by Sébastien BEAU - http://www.akretion.com
Status: Needs review
Proposed branch: lp:~akretion-team/openerp-product-variant/product-variant-multi-attribute
Merge into: lp:openerp-product-variant/trunk
Diff against target: 2965 lines (+1659/-870)
32 files modified
__unported__/product_variant_multi_advanced/__openerp__.py (+1/-1)
__unported__/product_variant_multi_advanced/product.py (+5/-5)
product_display/__init__.py (+23/-0)
product_display/__openerp__.py (+49/-0)
product_display/product.py (+47/-0)
product_display/product_view.xml (+28/-0)
product_variant_display_generator/__init__.py (+23/-0)
product_variant_display_generator/__openerp__.py (+45/-0)
product_variant_display_generator/product_variant_display.py (+130/-0)
product_variant_display_generator/product_view.xml (+44/-0)
product_variant_generator/__init__.py (+1/-0)
product_variant_generator/__openerp__.py (+8/-6)
product_variant_generator/product_data.xml (+29/-0)
product_variant_generator/product_variant.py (+455/-516)
product_variant_generator/product_view.xml (+97/-252)
product_variant_generator/security/ir.model.access.csv (+2/-6)
product_variant_generator/wizard/__init__.py (+24/-0)
product_variant_generator/wizard/product_template_add_option.py (+69/-0)
product_variant_generator/wizard/product_template_add_option_view.xml (+29/-0)
product_variant_generator_price/__init__.py (+23/-0)
product_variant_generator_price/__openerp__.py (+40/-0)
product_variant_generator_price/product_variant.py (+119/-0)
product_variant_generator_price/product_view.xml (+79/-0)
product_variant_multi/demo_data.xml (+0/-84)
product_variant_simple/__init__.py (+3/-0)
product_variant_simple/__openerp__.py (+47/-0)
product_variant_simple/product.py (+32/-0)
product_variant_simple/product_view.xml (+92/-0)
product_variant_simple/security/security.xml (+13/-0)
sale_product_display/__init__.py (+23/-0)
sale_product_display/__openerp__.py (+40/-0)
sale_product_display/sale.py (+39/-0)
To merge this branch: bzr merge lp:~akretion-team/openerp-product-variant/product-variant-multi-attribute
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp Needs Resubmitting
Review via email: mp+223611@code.launchpad.net

Description of the change

It's a total refactor of product variant multi.
The idea is too split the module in order to be more flexible.

As you can see a new concept have been added "Product display", this are the equivalent of "Configurable Product" on Magento side.

We do not map the "Product Template" with "Configurable Product" because it's not the same concept. Template are here for generating all of the variant, and the "Product Display" is just a way to show your product in a catalog.

For example, you can have 1 template (t-shirt color: red/yellow/blue size:S/M/L/XL) and then on magento you want to create 3 configurables products, one for each color. this case can be supported as you will create 3 displays from the product template.

To post a comment you must log in.
249. By Sébastien BEAU - http://www.akretion.com

[FIX] add missing file data for having 2 default template for code/name

250. By Chafique DELLI

[REF] refactor module product_variant_generator_price

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

Hello,

The management of the project has moved to Github: https://github.com/OCA/product-variant

Please migrate your merge proposal to Github. You may want to check https://github.com/OCA/maintainers-tools/wiki/How-to-move-a-Merge-Proposal-to-GitHub for an explanation on how to proceed.

Thanks for contributing to the project

review: Needs Resubmitting

Unmerged revisions

250. By Chafique DELLI

[REF] refactor module product_variant_generator_price

249. By Sébastien BEAU - http://www.akretion.com

[FIX] add missing file data for having 2 default template for code/name

248. By Sébastien BEAU - http://www.akretion.com

[CLEAN] pep8 clean up

247. By Sébastien BEAU - http://www.akretion.com

[REF] refactor module as product.variant.dimension have been removed

246. By Sébastien BEAU - http://www.akretion.com

[CLEAN] pep8 clean up

245. By Sébastien BEAU - http://www.akretion.com

[REF] rename generate_display_from_dim_id into main_dim_id

244. By Sébastien BEAU - http://www.akretion.com

[CLEAN] pep8 clean up

243. By Sébastien BEAU - http://www.akretion.com

[REF] refactor product template code/name generation. Now you can create template generator and use/share it

242. By Sébastien BEAU - http://www.akretion.com

[REF] UI refactor, having the dimension is too much as we already have the attribute set

241. By Sébastien BEAU - http://www.akretion.com

[CLEAN] pep8 clean up

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '__unported__'
2=== renamed directory 'product_variant_multi_advanced' => '__unported__/product_variant_multi_advanced'
3=== modified file '__unported__/product_variant_multi_advanced/__openerp__.py'
4--- product_variant_multi_advanced/__openerp__.py 2013-09-19 11:46:57 +0000
5+++ __unported__/product_variant_multi_advanced/__openerp__.py 2014-06-24 09:37:35 +0000
6@@ -39,6 +39,6 @@
7 'init_xml': [],
8 'update_xml': ['product.xml'],
9 'demo_xml': [],
10- 'installable': True,
11+ 'installable': False,
12 'active': False,
13 }
14
15=== modified file '__unported__/product_variant_multi_advanced/product.py'
16--- product_variant_multi_advanced/product.py 2013-09-19 15:50:40 +0000
17+++ __unported__/product_variant_multi_advanced/product.py 2014-06-24 09:37:35 +0000
18@@ -41,13 +41,13 @@
19 duplicated_fields = ['description_sale', 'name']
20
21
22-class product_template(orm.Model):
23+class ProductTemplate(orm.Model):
24 _inherit = "product.template"
25
26 def button_generate_variants(self, cr, uid, ids, context=None):
27 if context is None:
28 context = {}
29- super(product_template, self).button_generate_variants(cr, uid, ids, context=context)
30+ super(ProductTemplate, self).button_generate_variants(cr, uid, ids, context=context)
31 product_ids = self.get_products_from_product_template(cr, uid, ids, context=context)
32 # generate/update sale description
33 _logger.info("Starting to generate/update product sale descriptions...")
34@@ -57,7 +57,7 @@
35 return True
36
37
38-class product_product(orm.Model):
39+class ProductProduct(orm.Model):
40 _inherit = "product.product"
41
42 def write(self, cr, uid, ids, vals, context=None):
43@@ -65,7 +65,7 @@
44 ids = [ids]
45 if context is None:
46 context = {}
47- res = super(product_product, self).write(cr, uid, ids, vals.copy(), context=context)
48+ res = super(ProductProduct, self).write(cr, uid, ids, vals.copy(), context=context)
49
50 ids_simple = self.search(
51 cr, uid,
52@@ -98,7 +98,7 @@
53 # and not on the product_product
54
55 #take care to use vals.copy() if not the vals will be changed by calling the super method
56- ids = super(product_product, self).create(cr, uid, vals.copy(), context=context)
57+ ids = super(ProductProduct, self).create(cr, uid, vals.copy(), context=context)
58 ####### write the value in the product_product
59 ctx = context.copy()
60 ctx['iamthechild'] = True
61
62=== added directory 'product_display'
63=== added file 'product_display/__init__.py'
64--- product_display/__init__.py 1970-01-01 00:00:00 +0000
65+++ product_display/__init__.py 2014-06-24 09:37:35 +0000
66@@ -0,0 +1,23 @@
67+# -*- coding: utf-8 -*-
68+###############################################################################
69+#
70+# Module for OpenERP
71+# Copyright (C) 2013 Akretion (http://www.akretion.com).
72+# @author Chafique Delli <chafique.delli@akretion.com>
73+#
74+# This program is free software: you can redistribute it and/or modify
75+# it under the terms of the GNU Affero General Public License as
76+# published by the Free Software Foundation, either version 3 of the
77+# License, or (at your option) any later version.
78+#
79+# This program is distributed in the hope that it will be useful,
80+# but WITHOUT ANY WARRANTY; without even the implied warranty of
81+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82+# GNU Affero General Public License for more details.
83+#
84+# You should have received a copy of the GNU Affero General Public License
85+# along with this program. If not, see <http://www.gnu.org/licenses/>.
86+#
87+###############################################################################
88+
89+import product
90
91=== added file 'product_display/__openerp__.py'
92--- product_display/__openerp__.py 1970-01-01 00:00:00 +0000
93+++ product_display/__openerp__.py 2014-06-24 09:37:35 +0000
94@@ -0,0 +1,49 @@
95+# -*- coding: utf-8 -*-
96+##############################################################################
97+#
98+# Author: Chafique Delli
99+# Copyright 2013 Akretion
100+#
101+# This program is free software: you can redistribute it and/or modify
102+# it under the terms of the GNU Affero General Public License as
103+# published by the Free Software Foundation, either version 3 of the
104+# License, or (at your option) any later version.
105+#
106+# This program is distributed in the hope that it will be useful,
107+# but WITHOUT ANY WARRANTY; without even the implied warranty of
108+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
109+# GNU Affero General Public License for more details.
110+#
111+# You should have received a copy of the GNU Affero General Public License
112+# along with this program. If not, see <http://www.gnu.org/licenses/>.
113+#
114+##############################################################################
115+{'name' : 'Product Display',
116+ 'version' : '1.0',
117+ "author": "Akretion",
118+ 'category': 'Sales Management',
119+ 'description': """
120+Product Display
121+================
122+
123+A simple module for handling the product display.
124+It provides view for product display,
125+so they can be created manually. It doesn't provide
126+all the bells and whistles of the product_variant_display
127+module (creator wizards, ...).
128+
129+""",
130+ 'author' : 'Akretion',
131+ 'maintainer': 'Akretion',
132+ 'website': 'http://www.akretion.com/',
133+ 'depends' : [
134+ 'product',
135+ ],
136+ 'data': [
137+ 'product_view.xml',
138+ ],
139+ 'test': [],
140+ 'installable': True,
141+ 'auto_install': False,
142+ 'application': True,
143+ }
144
145=== added file 'product_display/product.py'
146--- product_display/product.py 1970-01-01 00:00:00 +0000
147+++ product_display/product.py 2014-06-24 09:37:35 +0000
148@@ -0,0 +1,47 @@
149+# -*- coding: utf-8 -*-
150+###############################################################################
151+#
152+# Module for OpenERP
153+# Copyright (C) 2013 Akretion (http://www.akretion.com).
154+# @author Chafique Delli <chafique.delli@akretion.com>
155+#
156+# This program is free software: you can redistribute it and/or modify
157+# it under the terms of the GNU Affero General Public License as
158+# published by the Free Software Foundation, either version 3 of the
159+# License, or (at your option) any later version.
160+#
161+# This program is distributed in the hope that it will be useful,
162+# but WITHOUT ANY WARRANTY; without even the implied warranty of
163+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
164+# GNU Affero General Public License for more details.
165+#
166+# You should have received a copy of the GNU Affero General Public License
167+# along with this program. If not, see <http://www.gnu.org/licenses/>.
168+#
169+###############################################################################
170+
171+from openerp.osv import orm, fields
172+
173+
174+class ProductProduct(orm.Model):
175+ _inherit = 'product.product'
176+
177+ _columns = {
178+ 'display_for_product_ids': fields.many2many(
179+ 'product.product',
180+ 'product_template_option_rel',
181+ 'product_display_id',
182+ 'product_id',
183+ string='Products'),
184+ 'display_ids': fields.many2many(
185+ 'product.product',
186+ 'product_template_option_rel',
187+ 'product_id',
188+ 'product_display_id',
189+ string='Products Display'),
190+ 'is_display': fields.boolean('Is Display'),
191+ }
192+
193+ _defaults = {
194+ 'is_display': False,
195+ }
196
197=== added file 'product_display/product_view.xml'
198--- product_display/product_view.xml 1970-01-01 00:00:00 +0000
199+++ product_display/product_view.xml 2014-06-24 09:37:35 +0000
200@@ -0,0 +1,28 @@
201+<?xml version="1.0" encoding="utf-8"?>
202+<openerp>
203+ <data>
204+
205+
206+ <!--Product Display -->
207+
208+ <record id="product_display_form_view" model="ir.ui.view">
209+ <field name="name">product.display.form</field>
210+ <field name="model">product.product</field>
211+ <field name="inherit_id" ref="product.product_normal_form_view" />
212+ <field name="arch" type="xml">
213+ <xpath expr="//label[@for='sale_ok']" position="after">
214+ <field name="is_display"/>
215+ <label for="is_display" />
216+ </xpath>
217+ <notebook position="inside">
218+ <page string="Product Display" attrs="{'invisible':[('is_display','=',False)]}">
219+ <group colspan="2" col="2">
220+ <field name="display_for_product_ids" nolabel="1"/>
221+ </group>
222+ </page>
223+ </notebook>
224+ </field>
225+ </record>
226+
227+ </data>
228+</openerp>
229
230=== added directory 'product_variant_display_generator'
231=== added file 'product_variant_display_generator/__init__.py'
232--- product_variant_display_generator/__init__.py 1970-01-01 00:00:00 +0000
233+++ product_variant_display_generator/__init__.py 2014-06-24 09:37:35 +0000
234@@ -0,0 +1,23 @@
235+# -*- coding: utf-8 -*-
236+###############################################################################
237+#
238+# Module for OpenERP
239+# Copyright (C) 2013 Akretion (http://www.akretion.com).
240+# @author Chafique Delli <chafique.delli@akretion.com>
241+#
242+# This program is free software: you can redistribute it and/or modify
243+# it under the terms of the GNU Affero General Public License as
244+# published by the Free Software Foundation, either version 3 of the
245+# License, or (at your option) any later version.
246+#
247+# This program is distributed in the hope that it will be useful,
248+# but WITHOUT ANY WARRANTY; without even the implied warranty of
249+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
250+# GNU Affero General Public License for more details.
251+#
252+# You should have received a copy of the GNU Affero General Public License
253+# along with this program. If not, see <http://www.gnu.org/licenses/>.
254+#
255+###############################################################################
256+
257+import product_variant_display
258
259=== added file 'product_variant_display_generator/__openerp__.py'
260--- product_variant_display_generator/__openerp__.py 1970-01-01 00:00:00 +0000
261+++ product_variant_display_generator/__openerp__.py 2014-06-24 09:37:35 +0000
262@@ -0,0 +1,45 @@
263+# -*- coding: utf-8 -*-
264+###############################################################################
265+#
266+# Module for OpenERP
267+# Copyright (C) 2013 Akretion (http://www.akretion.com).
268+# @author Chafique Delli <chafique.delli@akretion.com>
269+#
270+# This program is free software: you can redistribute it and/or modify
271+# it under the terms of the GNU Affero General Public License as
272+# published by the Free Software Foundation, either version 3 of the
273+# License, or (at your option) any later version.
274+#
275+# This program is distributed in the hope that it will be useful,
276+# but WITHOUT ANY WARRANTY; without even the implied warranty of
277+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
278+# GNU Affero General Public License for more details.
279+#
280+# You should have received a copy of the GNU Affero General Public License
281+# along with this program. If not, see <http://www.gnu.org/licenses/>.
282+#
283+###############################################################################
284+{
285+ "name": "Product Variant Display Generator",
286+ "version": "1.0",
287+ "author": "OpenERP SA, Akretion",
288+ "category": "Sales Management",
289+ "license": "AGPL-3",
290+ "summary": "Products Displays with multi-dimension variants ",
291+ "description": """
292+Multi-axial display product support for OpenERP
293+===============================================
294+
295+
296+ """,
297+ "depends" : [
298+ "product_custom_attributes",
299+ "product_variant_generator",
300+ "product_display"
301+ ],
302+ "data" : ["product_view.xml",
303+ ],
304+ "application": True,
305+ "active": False,
306+ "installable": True,
307+}
308
309=== added file 'product_variant_display_generator/product_variant_display.py'
310--- product_variant_display_generator/product_variant_display.py 1970-01-01 00:00:00 +0000
311+++ product_variant_display_generator/product_variant_display.py 2014-06-24 09:37:35 +0000
312@@ -0,0 +1,130 @@
313+# -*- coding: utf-8 -*-
314+###############################################################################
315+#
316+# Module for OpenERP
317+# Copyright (C) 2013 Akretion (http://www.akretion.com).
318+# @author Chafique Delli <chafique.delli@akretion.com>
319+#
320+# This program is free software: you can redistribute it and/or modify
321+# it under the terms of the GNU Affero General Public License as
322+# published by the Free Software Foundation, either version 3 of the
323+# License, or (at your option) any later version.
324+#
325+# This program is distributed in the hope that it will be useful,
326+# but WITHOUT ANY WARRANTY; without even the implied warranty of
327+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
328+# GNU Affero General Public License for more details.
329+#
330+# You should have received a copy of the GNU Affero General Public License
331+# along with this program. If not, see <http://www.gnu.org/licenses/>.
332+#
333+###############################################################################
334+
335+from openerp.osv import orm, fields
336+
337+
338+import logging
339+_logger = logging.getLogger(__name__)
340+
341+
342+class ProductTemplate(orm.Model):
343+ _inherit = "product.template"
344+
345+ _columns = {
346+ 'generate_main_display': fields.boolean('Generate Main Display'),
347+ 'main_dim_id': fields.many2one(
348+ 'attribute.attribute',
349+ string='Main Dimension',
350+ help=('This dimension will be used for generating '
351+ 'the product display')),
352+ 'display_variant_ids': fields.one2many(
353+ 'product.product',
354+ 'product_tmpl_id',
355+ domain=[
356+ ('is_display', '=', True),
357+ '|',
358+ ('active', '=', True),
359+ ('active', '=', False),
360+ ], string='Display Variants'),
361+ 'product_variant_ids': fields.one2many(
362+ 'product.product',
363+ 'product_tmpl_id',
364+ domain=[
365+ ('is_display', '=', False),
366+ '|',
367+ ('active', '=', True),
368+ ('active', '=', False),
369+ ], string='Product Variants'),
370+ }
371+
372+ def _get_combinaison(self, cr, uid, product_temp, context=None):
373+ if context.get('product_display'):
374+ fields = [dim.name for dim in product_temp.dimension_ids]
375+ num_of_fields = len(fields)
376+ combinaisons = []
377+ if product_temp.main_dim_id:
378+ for value in product_temp.value_ids:
379+ if value.dimension_id.id == product_temp.main_dim_id.id:
380+ combinaisons.append(
381+ [value.option_id.id] + [None]*(num_of_fields - 1)
382+ )
383+ if product_temp.generate_main_display:
384+ combinaisons.append([None]*num_of_fields)
385+ return combinaisons
386+ return super(ProductTemplate, self)._get_combinaison(
387+ cr, uid, product_temp, context=context)
388+
389+ def _prepare_variant_vals(self, cr, uid, product_temp, combinaison,
390+ context=None):
391+ product_obj = self.pool['product.product']
392+ vals = super(ProductTemplate, self)._prepare_variant_vals(
393+ cr, uid, product_temp, combinaison, context=context)
394+ if context.get('product_display'):
395+ vals['is_display'] = True
396+ domain = [
397+ ['product_tmpl_id', '=', vals['product_tmpl_id']],
398+ ['is_display', '=', False],
399+ ]
400+ dimension = product_temp.main_dim_id
401+ if dimension and dimension.name in vals:
402+ domain.append([dimension.name, '=', vals[dimension.name]])
403+ product_ids = product_obj.search(cr, uid, domain, context=context)
404+ vals['display_for_product_ids'] = [(6, 0, product_ids)]
405+ return vals
406+
407+ def _create_variant(self, cr, uid, product_temp, existing_product_ids,
408+ context=None):
409+ created_product_ids = super(ProductTemplate, self)._create_variant(
410+ cr, uid, product_temp, existing_product_ids, context=context)
411+ if created_product_ids:
412+ self.pool['product.product'].update_existing_product_display(
413+ cr, uid, created_product_ids, context=context)
414+
415+ ctx = context.copy()
416+ ctx['product_display'] = True
417+ created_product_display_ids = super(ProductTemplate, self).\
418+ _create_variant(cr, uid, product_temp, existing_product_ids,
419+ context=ctx)
420+ return created_product_ids + created_product_display_ids
421+
422+
423+class ProductProduct(orm.Model):
424+ _inherit = 'product.product'
425+
426+ def update_existing_product_display(self, cr, uid, ids, context=None):
427+ ids = self.search(cr, uid, [
428+ ['id', 'in', ids],
429+ ['is_display', '=', True]
430+ ], context=context)
431+
432+ for product in self.browse(cr, uid, ids, context=context):
433+ domain = [
434+ ['product_tmpl_id', '=', product.product_tmpl_id.id],
435+ ['is_display', '=', False],
436+ ]
437+ dim = product.main_dim_id
438+ if dim and product[dim.name]:
439+ domain.append([dim.name, '=', product[dim.name].name])
440+ product_ids = self.search(cr, uid, domain, context=context)
441+ product.write({'display_for_product_ids': [(6, 0, product_ids)]})
442+ return True
443
444=== added file 'product_variant_display_generator/product_view.xml'
445--- product_variant_display_generator/product_view.xml 1970-01-01 00:00:00 +0000
446+++ product_variant_display_generator/product_view.xml 2014-06-24 09:37:35 +0000
447@@ -0,0 +1,44 @@
448+<?xml version="1.0" encoding="utf-8"?>
449+<openerp>
450+ <data>
451+
452+<!--
453+ Module for OpenERP
454+ The licence is in the file __openerp__.py
455+ @author Chafique Delli <chafique.delli@akretion.com>
456+-->
457+
458+ <!-- PRODUCT TEMPLATE -->
459+ <record id="product_variant_multi_product_display_form_view" model="ir.ui.view">
460+ <field name="name">product.variant.multi.product.display.form</field>
461+ <field name="model">product.template</field>
462+ <field name="inherit_id" ref="product_variant_generator.product_variant_multi_product_template_form_view" />
463+ <field name="arch" type="xml">
464+ <xpath expr="//field[@name='do_not_update_variant']/.." position="after">
465+ <group string="Displays" colspan="1" col="4" attrs="{'invisible':[('is_multi_variants','=',False)]}">
466+ <field name="generate_main_display"/>
467+ <field name="dimension_ids" invisible="1"/>
468+ <field name="main_dim_id" domain="[('id', 'in', dimension_ids and dimension_ids[0][2])]"/>
469+ <field name="display_variant_ids" nolabel="1" colspan="4">
470+ <tree string="Display Variants">
471+ <field name="code" />
472+ <field name="name" />
473+ <field name="variants" string="Dimension Values" />
474+ </tree>
475+ </field>
476+ </group>
477+ </xpath>
478+ <field name="variant_ids" position="replace">
479+ <field name="product_variant_ids" nolabel="1" colspan="4">
480+ <tree string="Product Variants">
481+ <field name="code" />
482+ <field name="name" />
483+ <field name="variants" string="Dimension Values" />
484+ </tree>
485+ </field>
486+ </field>
487+ </field>
488+ </record>
489+
490+ </data>
491+</openerp>
492
493=== renamed directory 'product_variant_multi' => 'product_variant_generator'
494=== modified file 'product_variant_generator/__init__.py'
495--- product_variant_multi/__init__.py 2013-09-19 11:46:57 +0000
496+++ product_variant_generator/__init__.py 2014-06-24 09:37:35 +0000
497@@ -22,3 +22,4 @@
498 # flake8: noqa
499
500 from . import product_variant
501+from . import wizard
502
503=== modified file 'product_variant_generator/__openerp__.py'
504--- product_variant_multi/__openerp__.py 2013-09-19 11:46:57 +0000
505+++ product_variant_generator/__openerp__.py 2014-06-24 09:37:35 +0000
506@@ -20,7 +20,7 @@
507 #
508 ##############################################################################
509 {
510- "name": "Product Variant Multi",
511+ "name": "Product Variant Generator",
512 "version": "1.0",
513 "author": "OpenERP SA, Akretion",
514 "category": "Sales Management",
515@@ -44,8 +44,6 @@
516 the space of the variants. You could also choose to populate only some combinations
517 by hand instead.
518
519-Each variant can have an extra price that will be taken into account when computing
520-the base listed price. Yet to be implemented: a price extra per variant dimension value.
521 Finally, this module is better used along with the product_variant_configurator which
522 will help the salesman selecting the appropriate variant in the sale order line
523 using dimension criteria instead of having to crawl the full space of variants.
524@@ -60,11 +58,15 @@
525 and only from product.template if not found on product.product. But at least you
526 will have been warned.
527 """,
528- "depends": ["product"],
529- "demo": ["demo_data.xml"],
530- "data": [
531+ "depends" : [
532+ "product_custom_attributes",
533+ "product_variant_simple"
534+ ],
535+ "data" : [
536+ "wizard/product_template_add_option_view.xml",
537 "security/ir.model.access.csv",
538 "product_view.xml",
539+ "product_data.xml",
540 ],
541 "application": True,
542 "active": False,
543
544=== added file 'product_variant_generator/product_data.xml'
545--- product_variant_generator/product_data.xml 1970-01-01 00:00:00 +0000
546+++ product_variant_generator/product_data.xml 2014-06-24 09:37:35 +0000
547@@ -0,0 +1,29 @@
548+<?xml version="1.0" encoding="UTF-8"?>
549+<openerp>
550+ <data noupdate="0">
551+
552+ <record id="product_name_generator" model="string.template">
553+ <field name="name">Default Name Generator</field>
554+ <field name="type">product_name</field>
555+ <field name="code">name = []
556+for dimension in o.dimension_ids:
557+ if o[dimension.name].name:
558+ name.append("%s - %s" %(dimension.field_description, o[dimension.name].name))
559+result = " | ".join(name)</field>
560+ </record>
561+
562+ <record id="product_code_generator" model="string.template">
563+ <field name="name">Default Code Generator</field>
564+ <field name="type">product_code</field>
565+ <field name="code">code = []
566+if o.base_default_code:
567+ code.append(o.base_default_code)
568+for dimension in o.dimension_ids:
569+ if o[dimension.name].name:
570+ code.append(o[dimension.name].name)
571+result = " - ".join(code)</field>
572+ </record>
573+
574+ </data>
575+</openerp>
576+
577
578=== modified file 'product_variant_generator/product_variant.py'
579--- product_variant_multi/product_variant.py 2013-09-19 15:50:40 +0000
580+++ product_variant_generator/product_variant.py 2014-06-24 09:37:35 +0000
581@@ -7,6 +7,7 @@
582 # @author Sebatien Beau <sebastien.beau@akretion.com>
583 # @author Raphaël Valyi <raphael.valyi@akretion.com>
584 # @author Alexis de Lattre <alexis.delattre@akretion.com>
585+# @author Chafique Delli <chafique.delli@akretion.com>
586 # update to use a single "Generate/Update" button & price computation code
587 #
588 # This program is free software: you can redistribute it and/or modify
589@@ -25,215 +26,308 @@
590 ##############################################################################
591
592 from openerp.osv import fields, osv, orm
593-import openerp.addons.decimal_precision as dp
594 # Lib to eval python code with security
595+from openerp.tools.translate import _
596+from collections import defaultdict
597+from openerp.tools import config
598 from openerp.tools.safe_eval import safe_eval
599-from openerp.tools.translate import _
600+import datetime
601+
602
603 import logging
604 _logger = logging.getLogger(__name__)
605
606
607-class product_variant_dimension_type(orm.Model):
608- _name = "product.variant.dimension.type"
609- _description = "Dimension Type"
610+class AttributeAttribute(orm.Model):
611+ _inherit = 'attribute.attribute'
612
613 _columns = {
614- 'description': fields.char('Description', size=64, translate=True),
615- 'name': fields.char('Dimension Type Name', size=64, required=True),
616- 'sequence': fields.integer('Sequence', help=("The product 'variants' code will "
617- "use this to order the dimension values")),
618- 'option_ids': fields.one2many('product.variant.dimension.option', 'dimension_id',
619- 'Dimension Options'),
620- 'product_tmpl_id': fields.many2many('product.template', 'product_template_dimension_rel',
621- 'dimension_id', 'template_id', 'Product Template'),
622- 'allow_custom_value': fields.boolean('Allow Custom Value',
623- help=("If true, custom values can be entered "
624- "in the product configurator")),
625- 'mandatory_dimension': fields.boolean('Mandatory Dimension',
626- help=("If false, variant products will be created "
627- "with and without this dimension")),
628+ 'sequence': fields.integer(
629+ 'Sequence',
630+ help=("The product 'variants' code will "
631+ "use this to order the dimension values")),
632+ 'mandatory_dimension': fields.boolean(
633+ 'Mandatory Dimension',
634+ help=("If false, variant products will be created "
635+ "with and without this dimension")),
636+ 'is_dimension': fields.boolean('Is dimension', help='Help note'),
637 }
638
639 _defaults = {
640 'mandatory_dimension': 1,
641- }
642-
643- _order = "sequence, name"
644-
645- def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=None):
646- if not context.get('product_tmpl_id', False):
647- args = None
648- return super(product_variant_dimension_type,
649- self).name_search(cr, uid, '', args, 'ilike', None, None)
650-
651-
652-class product_variant_dimension_option(orm.Model):
653- _name = "product.variant.dimension.option"
654- _description = "Dimension Option"
655-
656- def _get_dimension_values(self, cr, uid, ids, context=None):
657- dimvalue_obj = self.pool.get('product.variant.dimension.value')
658- return dimvalue_obj.search(cr, uid, [('dimension_id', 'in', ids)], context=context)
659-
660- _columns = {
661- 'name': fields.char('Dimension Option Name', size=64, required=True),
662- 'code': fields.char('Code', size=64),
663- 'sequence': fields.integer('Sequence'),
664- 'dimension_id': fields.many2one('product.variant.dimension.type',
665- 'Dimension Type', ondelete='cascade'),
666- }
667-
668- _order = "dimension_id, sequence, name"
669-
670-
671-class product_variant_dimension_value(orm.Model):
672- _name = "product.variant.dimension.value"
673+ 'attribute_type': 'select',
674+ }
675+
676+ _order = "sequence"
677+
678+
679+class DimensionValue(orm.Model):
680+ _name = "dimension.value"
681 _description = "Dimension Value"
682
683 def unlink(self, cr, uid, ids, context=None):
684+ product_obj = self.pool['product.product']
685 for value in self.browse(cr, uid, ids, context=context):
686- if value.product_ids:
687- product_names = [product.name for product in value.product_ids]
688+ product_ids = product_obj.search(cr, uid, [
689+ ['product_tmpl_id', '=', value.product_tmpl_id.id],
690+ [value.dimension_id.name, '=', value.option_id.name],
691+ ], context=context)
692+ if product_ids:
693+ products = product_obj.browse(cr, uid, product_ids,
694+ context=context)
695+ product_names = [product.name for product in products]
696 product_list = '\n - ' + '\n - '.join(product_names)
697- raise osv.except_osv(_('Dimension value can not be removed'),
698- _("The value %s is used by the products : %s \n "
699- "Please remove these products before removing the value.")
700- % (value.option_id.name, product_list))
701- return super(product_variant_dimension_value, self).unlink(cr, uid, ids, context)
702+ raise osv.except_osv(
703+ _('Dimension value can not be removed'),
704+ _("The value %s is used by the products : %s \n "
705+ "Please remove these products before removing "
706+ "the value.") % (value.option_id.name, product_list))
707+ return super(DimensionValue, self).\
708+ unlink(cr, uid, ids, context)
709
710 def _get_values_from_types(self, cr, uid, ids, context=None):
711- dimvalue_obj = self.pool.get('product.variant.dimension.value')
712- return dimvalue_obj.search(cr, uid, [('dimension_id', 'in', ids)], context=context)
713-
714- def _get_values_from_options(self, cr, uid, ids, context=None):
715- dimvalue_obj = self.pool.get('product.variant.dimension.value')
716- return dimvalue_obj.search(cr, uid, [('option_id', 'in', ids)], context=context)
717+ dimvalue_obj = self.pool.get('dimension.value')
718+ return dimvalue_obj.search(cr, uid, [
719+ ('dimension_id', 'in', ids),
720+ ], context=context)
721
722 _columns = {
723- 'option_id': fields.many2one('product.variant.dimension.option', 'Option', required=True),
724- 'name': fields.related('option_id', 'name', type='char',
725- relation='product.variant.dimension.option',
726- string="Dimension Value", readonly=True),
727+ # TODO option_id add param in context
728+ # to have a full name 'dimension + option'
729+ # exemple address on sale order
730+ 'option_id': fields.many2one(
731+ 'attribute.option',
732+ 'Option',
733+ required=True),
734+ 'name': fields.related(
735+ 'option_id',
736+ 'name',
737+ type='char',
738+ string="Dimension Value",
739+ readonly=True),
740 'sequence': fields.integer('Sequence'),
741- 'price_extra': fields.float('Sale Price Extra',
742- digits_compute=dp.get_precision('Sale Price')),
743- 'price_margin': fields.float('Sale Price Margin',
744- digits_compute=dp.get_precision('Sale Price')),
745- 'cost_price_extra': fields.float('Cost Price Extra',
746- digits_compute=dp.get_precision('Purchase Price')),
747- 'dimension_id': fields.related('option_id', 'dimension_id', type="many2one",
748- relation="product.variant.dimension.type",
749- string="Dimension Type",
750- readonly=True,
751- store={'product.variant.dimension.value':
752- (lambda self, cr, uid, ids, c={}:
753- ids, ['option_id'], 10),
754- 'product.variant.dimension.option':
755- (_get_values_from_options, ['dimension_id'], 20)}),
756- 'product_tmpl_id': fields.many2one('product.template', 'Product Template',
757- ondelete='cascade'),
758- 'dimension_sequence': fields.related('dimension_id', 'sequence', type='integer',
759- relation='product.variant.dimension.type',
760- #used for ordering purposes in the "variants"
761- string="Related Dimension Sequence",
762- store={'product.variant.dimension.type':
763- (_get_values_from_types, ['sequence'], 10)}),
764- 'product_ids': fields.many2many('product.product', 'product_product_dimension_rel',
765- 'dimension_id', 'product_id', 'Variant', readonly=True),
766- 'active': fields.boolean('Active', help=("If false, this value will not be "
767- "used anymore to generate variants.")),
768+ 'dimension_id': fields.many2one(
769+ 'attribute.attribute',
770+ 'Dimension',
771+ required=True),
772+ 'product_tmpl_id': fields.many2one(
773+ 'product.template',
774+ 'Product Template',
775+ ondelete='cascade'),
776+ 'dimension_sequence': fields.related(
777+ 'dimension_id',
778+ 'sequence',
779+ type='integer',
780+ string="Related Dimension Sequence",
781+ store={
782+ 'attribute.attribute': (
783+ _get_values_from_types,
784+ ['sequence'],
785+ 10),
786+ 'dimension.value': (
787+ lambda self, cr, uid, ids, c={}: ids,
788+ ['dimension_id'],
789+ 10),
790+ }),
791+ 'active': fields.boolean(
792+ 'Active',
793+ help=("If false, this value will not be "
794+ "used anymore to generate variants.")),
795 }
796
797 _defaults = {
798 'active': True,
799 }
800
801- _sql_constraints = [('opt_dim_tmpl_uniq',
802- 'UNIQUE(option_id, dimension_id, product_tmpl_id)',
803- _("The combination option and dimension type "
804- "already exists for this product template !")), ]
805-
806- _order = "dimension_sequence, dimension_id, sequence, option_id"
807-
808-
809-class product_template(orm.Model):
810+ _sql_constraints = [
811+ (
812+ 'opt_dim_tmpl_uniq',
813+ 'UNIQUE(option_id, dimension_id, product_tmpl_id)',
814+ _("The combination option and dimension type "
815+ "already exists for this product template !")
816+ ),
817+ ]
818+
819+ _order = "dimension_sequence, sequence, option_id"
820+
821+ def on_dimension_change(self, cr, uid, ids, dimension_id, context=None):
822+ dim_obj = self.pool['attribute.attribute']
823+ dim = dim_obj.browse(cr, uid, dimension_id, context=context)
824+ return {
825+ 'domain': {
826+ 'option_id': [
827+ ('id', 'in', [option.id for option in dim.option_ids])
828+ ]
829+ },
830+ 'value': {'option_id': False},
831+ }
832+
833+
834+class StringTemplate(orm.Model):
835+ _name = 'string.template'
836+
837+ def __get_type(self, cr, uid, context=None):
838+ return self._get_type(cr, uid, context=context)
839+
840+ def _get_type(self, cr, uid, context=None):
841+ return [
842+ ('product_code', 'Product Code'),
843+ ('product_name', 'Product Name'),
844+ ]
845+
846+ _columns = {
847+ 'name': fields.char('name', required=True),
848+ 'type': fields.selection(__get_type, 'Type', required=True),
849+ 'code': fields.text('Code', required=True),
850+ }
851+
852+ _defaults = {
853+ 'code': """# Python code. Use result='YOUR_RESULT' to return your value.
854+ # You can use the following variables :
855+ # - self: ORM model of the record which is checked
856+ # - o: browse_record of product template
857+ # - pool: ORM model pool (i.e. self.pool)
858+ # - datetime: Python datetime module
859+ # - cr: database cursor
860+ # - uid: current user id
861+ # - context: current context
862+ """
863+ }
864+
865+ def _eval_context(self, cr, uid, obj, context=None):
866+ if context is None:
867+ context = {}
868+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
869+ return {
870+ 'self': self.pool.get(obj._name),
871+ 'o': obj,
872+ 'pool': self.pool,
873+ 'cr': cr,
874+ 'uid': uid,
875+ 'user': user,
876+ 'datetime': datetime,
877+ # copy context to prevent side-effects of eval
878+ 'context': context.copy(),
879+ }
880+
881+ def _build(self, cr, uid, template_id, obj, context=None):
882+ if isinstance(template_id, (tuple, list)):
883+ template_id = template_id[0]
884+ template = self.browse(cr, uid, template_id, context=context)
885+ expr = template.code
886+ space = self._eval_context(cr, uid, obj, context=context)
887+ try:
888+ safe_eval(expr,
889+ space,
890+ mode='exec',
891+ nocopy=True) # nocopy allows to return 'result'
892+ except Exception, e:
893+ if config['debug_mode']: raise
894+ raise orm.except_orm(
895+ _('Error'),
896+ _('Error when evaluating the template:\n %s \n(%s)')
897+ % (template.name, e))
898+ return space.get('result', False)
899+
900+
901+class ProductTemplate(orm.Model):
902 _inherit = "product.template"
903-
904 _order = "name"
905
906+ def _get_dimension_ids(self, cr, uid, ids, field_name, args, context=None):
907+ result = {}
908+ for product_tmpl in self.browse(cr, uid, ids, context=context):
909+ attr_ids = []
910+ if product_tmpl.attribute_set_id:
911+ for group in product_tmpl.attribute_set_id.attribute_group_ids:
912+ for attr in group.attribute_ids:
913+ attr_ids.append(attr.attribute_id.id)
914+ result[product_tmpl.id] = attr_ids
915+ return result
916+
917 _columns = {
918- 'name': fields.char('Name', size=128, translate=True, select=True, required=False),
919- 'dimension_type_ids': fields.many2many('product.variant.dimension.type',
920- 'product_template_dimension_rel',
921- 'template_id', 'dimension_id', 'Dimension Types'),
922- 'value_ids': fields.one2many('product.variant.dimension.value',
923- 'product_tmpl_id',
924- 'Dimension Values'),
925- 'variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Variants'),
926- 'variant_model_name': fields.char('Variant Model Name', size=64, required=True,
927- help=('[_o.dimension_id.name_] will be replaced with the'
928- ' name of the dimension and [_o.option_id.code_] '
929- 'by the code of the option. Example of Variant '
930- 'Model Name : "[_o.dimension_id.name_] - '
931- '[_o.option_id.code_]"')),
932- 'variant_model_name_separator': fields.char('Variant Model Name Separator', size=64,
933- help=('Add a separator between the elements '
934- 'of the variant name')),
935- 'code_generator': fields.char('Code Generator', size=256,
936- help=('enter the model for the product code, all parameter'
937- ' between [_o.my_field_] will be replace by the '
938- 'product field. Example product_code model : '
939- 'prefix_[_o.variants_]_suffixe ==> result : '
940- 'prefix_2S2T_suffix')),
941+ 'name': fields.char(
942+ 'Name', size=128,
943+ translate=True,
944+ select=True,
945+ required=False),
946+ 'value_ids': fields.one2many(
947+ 'dimension.value',
948+ 'product_tmpl_id',
949+ 'Dimension Values'),
950+ 'dimension_ids': fields.function(
951+ _get_dimension_ids,
952+ string='Dimension',
953+ type='many2many',
954+ relation='attribute.attribute'),
955+ 'variant_ids': fields.one2many(
956+ 'product.product',
957+ 'product_tmpl_id',
958+ 'Variants'),
959+ 'template_name_id': fields.many2one(
960+ 'string.template',
961+ 'Template Name',
962+ required=True,
963+ domain=[('type', '=', 'product_name')],
964+ ondelete='restrict'),
965+ 'template_code_id': fields.many2one(
966+ 'string.template',
967+ 'Template Code',
968+ required=True,
969+ domain=[('type', '=', 'product_code')],
970+ ondelete='restrict'),
971+ 'base_default_code': fields.char(
972+ 'Base Default Code',
973+ size=256,
974+ help=('Base Default Code of the template '
975+ 'used for generating the product code')),
976 'is_multi_variants': fields.boolean('Is Multi Variants'),
977- 'variant_track_production': fields.boolean('Track Production Lots on variants ?'),
978- 'variant_track_incoming': fields.boolean('Track Incoming Lots on variants ?'),
979- 'variant_track_outgoing': fields.boolean('Track Outgoing Lots on variants ?'),
980- 'do_not_update_variant': fields.boolean("Don't Update Variant"),
981- 'do_not_generate_new_variant': fields.boolean("Don't Generate New Variant"),
982+ 'variant_track_production': fields.boolean(
983+ 'Track Production Lots on variants ?'),
984+ 'variant_track_incoming': fields.boolean(
985+ 'Track Incoming Lots on variants ?'),
986+ 'variant_track_outgoing': fields.boolean(
987+ 'Track Outgoing Lots on variants ?'),
988+ 'do_not_update_variant': fields.boolean(
989+ "Don't Update Variant"),
990+ 'do_not_generate_new_variant': fields.boolean(
991+ "Don't Generate New Variant"),
992 }
993
994+ def _get_default_template_name(self, cr, uid, context=None):
995+ tmpl_id = self.pool['string.template'].search(cr, uid, [
996+ ('type', '=', 'product_name'),
997+ ], context=context)
998+ return tmpl_id and tmpl_id[0] or False
999+
1000+ def _get_default_template_code(self, cr, uid, context=None):
1001+ tmpl_id = self.pool['string.template'].search(cr, uid, [
1002+ ('type', '=', 'product_code'),
1003+ ], context=context)
1004+ return tmpl_id and tmpl_id[0] or False
1005+
1006 _defaults = {
1007- 'variant_model_name': '[_o.dimension_id.name_] - [_o.option_id.name_]',
1008- 'variant_model_name_separator': ' - ',
1009 'is_multi_variants': False,
1010- 'code_generator': ("[_'-'.join([x.option_id.name for x in o.dimension_value_ids] "
1011- "or ['CONF'])_]"),
1012+ 'template_name_id': _get_default_template_name,
1013+ 'template_code_id': _get_default_template_code,
1014 }
1015
1016 def unlink(self, cr, uid, ids, context=None):
1017 if context and context.get('unlink_from_product_product', False):
1018 for template in self.browse(cr, uid, ids, context):
1019 if not template.is_multi_variants:
1020- super(product_template, self).unlink(cr, uid, [template.id], context)
1021+ super(ProductTemplate, self).\
1022+ unlink(cr, uid, [template.id], context)
1023 else:
1024 for template in self.browse(cr, uid, ids, context):
1025 if template.variant_ids == []:
1026- super(product_template, self).unlink(cr, uid, [template.id], context)
1027+ super(ProductTemplate, self).\
1028+ unlink(cr, uid, [template.id], context)
1029 else:
1030- raise osv.except_osv(_("Cannot delete template"),
1031- _("This template has existing corresponding products..."))
1032- return True
1033-
1034- def add_all_option(self, cr, uid, ids, context=None):
1035- #Reactive all unactive values
1036- value_obj = self.pool.get('product.variant.dimension.value')
1037- for template in self.browse(cr, uid, ids, context=context):
1038- values_ids = value_obj.search(cr, uid, [['product_tmpl_id', '=', template.id],
1039- '|', ['active', '=', False],
1040- ['active', '=', True]], context=context)
1041- value_obj.write(cr, uid, values_ids,
1042- {'active': True},
1043- context=context)
1044- values = value_obj.browse(cr, uid, values_ids, context=context)
1045- existing_option_ids = [value.option_id.id for value in values]
1046- vals = {'value_ids': []}
1047- for dim in template.dimension_type_ids:
1048- for option in dim.option_ids:
1049- if not option.id in existing_option_ids:
1050- vals['value_ids'] += [[0, 0, {'option_id': option.id}]]
1051- self.write(cr, uid, [template.id], vals, context=context)
1052+ raise osv.except_osv(
1053+ _("Cannot delete template"),
1054+ _("This template has existing corresponding "
1055+ "products..."))
1056 return True
1057
1058 def get_products_from_product_template(self, cr, uid, ids, context=None):
1059@@ -245,12 +339,14 @@
1060 default = {}
1061 default = default.copy()
1062 default.update({'variant_ids': False, })
1063- new_id = super(product_template, self).copy(cr, uid, id, default, context)
1064+ new_id = super(ProductTemplate, self).\
1065+ copy(cr, uid, id, default, context=context)
1066
1067- val_obj = self.pool.get('product.variant.dimension.value')
1068+ val_obj = self.pool.get('dimension.value')
1069 template = self.read(cr, uid, new_id, ['value_ids'], context=context)
1070 # Making sure the values we duplicated are no longer linked via the
1071- # m2m 'product_ids' with the product.product variants from the original template
1072+ # m2m 'product_ids' with the product.product variants
1073+ # from the original template
1074 val_obj.write(cr, uid, template['value_ids'], {
1075 'product_ids': [(6, 0, [])],
1076 }, context=context)
1077@@ -260,384 +356,227 @@
1078 def copy_translations(self, cr, uid, old_id, new_id, context=None):
1079 if context is None:
1080 context = {}
1081- # avoid recursion through already copied records in case of circular relationship
1082+ # avoid recursion through already copied records in case
1083+ # of circular relationship
1084 seen_map = context.setdefault('__copy_translations_seen', {})
1085 if old_id in seen_map.setdefault(self._name, []):
1086 return
1087 seen_map[self._name].append(old_id)
1088- return super(product_template, self).copy_translations(cr, uid, old_id, new_id,
1089- context=context)
1090+ return super(ProductTemplate, self).\
1091+ copy_translations(cr, uid, old_id, new_id, context=context)
1092
1093- def _create_variant_list(self, cr, ids, uid, vals, context=None):
1094+ def _create_variant_list(self, cr, uid, vals, context=None):
1095
1096 def cartesian_product(args):
1097 if len(args) == 1:
1098 return [x and [x] or [] for x in args[0]]
1099- return [(i and [i] or []) + j for j in cartesian_product(args[1:]) for i in args[0]]
1100+ return [(i and [i] or []) + j for j in cartesian_product(args[1:])
1101+ for i in args[0]]
1102
1103 return cartesian_product(vals)
1104
1105+ def _get_combinaison(self, cr, uid, product_temp, context=None):
1106+ res = defaultdict(list)
1107+
1108+ for value in product_temp.value_ids:
1109+ res[value.dimension_id].append(value.option_id.id)
1110+
1111+ temp_val_list = []
1112+ for dim in res:
1113+ temp_val_list += [
1114+ res[dim]
1115+ + (not dim.mandatory_dimension and [None] or [])
1116+ ]
1117+
1118+ # example temp_val_list is equal to [['red', 'blue', 'yellow'],
1119+ # ['L', 'XL', 'M']]
1120+ # In reallity it's not a string value but the id of the value
1121+
1122+ if not temp_val_list:
1123+ return []
1124+
1125+ combinaisons = self._create_variant_list(
1126+ cr, uid, temp_val_list, context)
1127+ return combinaisons
1128+
1129+ def _get_combinaisons_to_create(self, cr, uid, product_temp,
1130+ existing_product_ids, context=None):
1131+ variants_obj = self.pool['product.product']
1132+
1133+ fields = set([dimension_value.dimension_id.name
1134+ for dimension_value in product_temp.value_ids])
1135+
1136+ combinaisons = self._get_combinaison(
1137+ cr, uid, product_temp, context=context)
1138+
1139+ for combinaison in combinaisons:
1140+ combinaison.sort()
1141+
1142+ existing_products = variants_obj.read(
1143+ cr, uid, existing_product_ids, fields, context=context)
1144+
1145+ existing_combinaisons = []
1146+ for existing_product in existing_products:
1147+ existing_combinaison = []
1148+ for field in fields:
1149+ if existing_product[field]:
1150+ existing_combinaison.append(existing_product[field][0])
1151+ else:
1152+ existing_combinaison.append(None)
1153+ existing_combinaison.sort()
1154+ existing_combinaisons.append(existing_combinaison)
1155+
1156+ combinaisons_to_create = [x for x in combinaisons
1157+ if not x in existing_combinaisons]
1158+
1159+ _logger.debug("variant existing : %s, variant to create : %s",
1160+ len(existing_combinaisons),
1161+ len(combinaisons_to_create))
1162+ return combinaisons_to_create
1163+
1164+ def _prepare_variant_vals(self, cr, uid, product_temp, combinaison,
1165+ context=None):
1166+ option_obj = self.pool['attribute.option']
1167+ vals = {
1168+ 'name': product_temp.name,
1169+ 'track_production': product_temp.variant_track_production,
1170+ 'track_incoming': product_temp.variant_track_incoming,
1171+ 'track_outgoing': product_temp.variant_track_outgoing,
1172+ 'product_tmpl_id': product_temp.id,
1173+ }
1174+ option_ids = [option_id for option_id in combinaison if option_id]
1175+ for option in option_obj.browse(cr, uid, option_ids, context=context):
1176+ vals[option.attribute_id.name] = option.id
1177+ return vals
1178+
1179+ def _create_variant(self, cr, uid, product_temp, existing_product_ids,
1180+ context=None):
1181+ variants_obj = self.pool['product.product']
1182+ created_product_ids = []
1183+ combinaisons_to_create = self.\
1184+ _get_combinaisons_to_create(
1185+ cr, uid, product_temp, existing_product_ids,
1186+ context=context)
1187+
1188+ count = 0
1189+ for combinaison in combinaisons_to_create:
1190+ count += 1
1191+ vals = self._prepare_variant_vals(
1192+ cr, uid, product_temp, combinaison,
1193+ context=context)
1194+
1195+ cr.execute("SAVEPOINT pre_variant_save")
1196+ try:
1197+ product_id = variants_obj.create(cr, uid, vals, {
1198+ 'generate_from_template': True,
1199+ })
1200+ created_product_ids.append(product_id)
1201+ if count % 50 == 0:
1202+ _logger.debug("product created : %s", count)
1203+ except Exception, e:
1204+ if config['debug_mode']:
1205+ raise
1206+ _logger.error("Error creating product variant: %s",
1207+ e, exc_info=True)
1208+ _logger.debug("Values used to attempt creation of "
1209+ "product variant: %s", vals)
1210+ cr.execute("ROLLBACK TO SAVEPOINT pre_variant_save")
1211+ cr.execute("RELEASE SAVEPOINT pre_variant_save")
1212+
1213+ _logger.debug("product created : %s", len(created_product_ids))
1214+ return created_product_ids
1215+
1216+ def _generate_variant_for_template(self, cr, uid, product_temp,
1217+ context=None):
1218+ variants_obj = self.pool['product.product']
1219+ created_product_ids = []
1220+
1221+ existing_product_ids = variants_obj.search(cr, uid, [
1222+ ('product_tmpl_id', '=', product_temp.id)
1223+ ], context=context)
1224+
1225+ if not product_temp.do_not_generate_new_variant:
1226+ created_product_ids = self._create_variant(
1227+ cr, uid, product_temp, existing_product_ids,
1228+ context=context)
1229+
1230+ product_ids = existing_product_ids + created_product_ids
1231+
1232+ _logger.debug("Starting to generate/update variant names...")
1233+ variants_obj.update_variant(cr, uid, product_ids, context=context)
1234+ return True
1235+
1236 def button_generate_variants(self, cr, uid, ids, context=None):
1237- variants_obj = self.pool.get('product.product')
1238-
1239- for product_temp in self.browse(cr, uid, ids, context):
1240- res = {}
1241- temp_val_list = []
1242- for value in product_temp.value_ids:
1243- if res.get(value.dimension_id, False):
1244- res[value.dimension_id] += [value.id]
1245- else:
1246- res[value.dimension_id] = [value.id]
1247- for dim in res:
1248- temp_val_list += [res[dim] + (not dim.mandatory_dimension and [None] or [])]
1249-
1250- existing_product_ids = variants_obj.search(cr, uid,
1251- [('product_tmpl_id', '=', product_temp.id)])
1252- created_product_ids = []
1253- if temp_val_list and not product_temp.do_not_generate_new_variant:
1254- list_of_variants = self._create_variant_list(cr, uid, ids, temp_val_list, context)
1255- existing_product_dim_value = variants_obj.read(cr, uid, existing_product_ids,
1256- ['dimension_value_ids'])
1257- list_of_variants_existing = [x['dimension_value_ids']
1258- for x in existing_product_dim_value]
1259- for x in list_of_variants_existing:
1260- x.sort()
1261- for x in list_of_variants:
1262- x.sort()
1263- list_of_variants_to_create = [x for x in list_of_variants
1264- if not x in list_of_variants_existing]
1265-
1266- _logger.debug("variant existing : %s, variant to create : %s",
1267- len(list_of_variants_existing),
1268- len(list_of_variants_to_create))
1269- count = 0
1270- for variant in list_of_variants_to_create:
1271- count += 1
1272-
1273- vals = {
1274- 'name': product_temp.name,
1275- 'track_production': product_temp.variant_track_production,
1276- 'track_incoming': product_temp.variant_track_incoming,
1277- 'track_outgoing': product_temp.variant_track_outgoing,
1278- 'product_tmpl_id': product_temp.id,
1279- 'dimension_value_ids': [(6, 0, variant)],
1280- }
1281-
1282- cr.execute("SAVEPOINT pre_variant_save")
1283- try:
1284- product_id = variants_obj.create(cr, uid, vals,
1285- {'generate_from_template': True})
1286- created_product_ids.append(product_id)
1287- if count % 50 == 0:
1288- _logger.debug("product created : %s", count)
1289- except Exception, e:
1290- _logger.error("Error creating product variant: %s",
1291- e, exc_info=True)
1292- _logger.debug("Values used to attempt creation of product variant: %s",
1293- vals)
1294- cr.execute("ROLLBACK TO SAVEPOINT pre_variant_save")
1295- cr.execute("RELEASE SAVEPOINT pre_variant_save")
1296-
1297- _logger.debug("product created : %s", count)
1298-
1299- if not product_temp.do_not_update_variant:
1300- product_ids = existing_product_ids + created_product_ids
1301- else:
1302- product_ids = created_product_ids
1303-
1304- # FIRST, Generate/Update variant names ('variants' field)
1305- _logger.debug("Starting to generate/update variant names...")
1306- self.pool.get('product.product').build_variants_name(cr, uid, product_ids,
1307- context=context)
1308- _logger.debug("End of the generation/update of variant names.")
1309- # SECOND, Generate/Update product codes and properties (we may need variants name)
1310- _logger.debug("Starting to generate/update product codes and properties...")
1311- self.pool.get('product.product').build_product_code_and_properties(cr, uid,
1312- product_ids,
1313- context=context)
1314- _logger.debug("End of the generation/update of product codes and properties.")
1315- # THIRD, Generate/Update product names (we may need variants name for that)
1316- _logger.debug("Starting to generate/update product names...")
1317- self.pool.get('product.product').build_product_name(cr, uid, product_ids,
1318- context=context)
1319- _logger.debug("End of generation/update of product names.")
1320+ for product_temp in self.browse(cr, uid, ids, context=context):
1321+ self._generate_variant_for_template(
1322+ cr, uid, product_temp, context=context)
1323 return True
1324
1325
1326-class product_product(orm.Model):
1327+class ProductProduct(orm.Model):
1328 _inherit = "product.product"
1329
1330 def init(self, cr):
1331- #For the first installation if you already have product in your database,
1332- # the name of the existing product will be empty, so we fill it
1333- cr.execute("update product_product set name=name_template where name is null;")
1334+ #TODO do it only for the first installation
1335+ #For the first installation if you already have product in
1336+ # your database, the name of the existing product will be empty,
1337+ # so we fill it
1338+ cr.execute("UPDATE product_product SET name=name_template "
1339+ "WHERE name is null;")
1340 return True
1341
1342 def unlink(self, cr, uid, ids, context=None):
1343 if not context:
1344 context = {}
1345 context['unlink_from_product_product'] = True
1346- return super(product_product, self).unlink(cr, uid, ids, context)
1347-
1348- def build_product_name(self, cr, uid, ids, context=None):
1349- return self.build_product_field(cr, uid, ids, 'name', context=None)
1350-
1351- def build_product_field(self, cr, uid, ids, field, context=None):
1352- def get_description_sale(product):
1353- return self.parse(cr, uid, product,
1354- product.product_tmpl_id.description_sale,
1355- context=context)
1356-
1357- def get_name(product):
1358- return (product.product_tmpl_id.name or '') + ' ' + (product.variants or '')
1359-
1360- if not context:
1361- context = {}
1362+ return super(ProductProduct, self).unlink(cr, uid, ids, context)
1363+
1364+ def update_variant(self, cr, uid, ids, context=None):
1365+ lang_obj = self.pool.get('res.lang')
1366+ lang_ids = lang_obj.search(cr, uid, [
1367+ ('translatable', '=', True),
1368+ ], context=context)
1369+ for lang in lang_obj.browse(cr, uid, lang_ids, context=context):
1370+ context['lang'] = lang.code
1371+ for product in self.browse(cr, uid, ids, context=context):
1372+ self._update_variant(cr, uid, product, context=context)
1373+ return True
1374+
1375+ def _update_variant(self, cr, uid, product, context=None):
1376+ vals = self._prepare_update_vals(cr, uid, product, context=context)
1377+ vals = self._remove_not_updated(cr, uid, product, vals, context=context)
1378+ if vals:
1379+ product.write(vals)
1380+ return True
1381+
1382+ def _prepare_update_vals(self, cr, uid, product, context=None):
1383 context['is_multi_variants'] = True
1384- obj_lang = self.pool.get('res.lang')
1385- lang_ids = obj_lang.search(cr, uid, [('translatable', '=', True)], context=context)
1386- lang_code = [x['code']
1387- for x in obj_lang.read(cr, uid, lang_ids, ['code'], context=context)]
1388- for code in lang_code:
1389- context['lang'] = code
1390- for product in self.browse(cr, uid, ids, context=context):
1391- new_field_value = eval("get_" + field + "(product)") # TODO convert to safe_eval
1392- cur_field_value = safe_eval("product." + field, {'product': product})
1393- if new_field_value != cur_field_value:
1394- self.write(cr, uid, [product.id], {field: new_field_value}, context=context)
1395- return True
1396-
1397- def parse(self, cr, uid, o, text, context=None):
1398- if not text:
1399- return ''
1400- vals = text.split('[_')
1401- description = ''
1402- for val in vals:
1403- if '_]' in val:
1404- sub_val = val.split('_]')
1405- try:
1406- description += (safe_eval(sub_val[0], {'o': o, 'context': context})
1407- or ''
1408- ) + sub_val[1]
1409- except AttributeError:
1410- raise osv.except_osv(_('Bad expression'),
1411- _("One of your expressions contains "
1412- "a non existing attribute: %s"
1413- ) % sub_val[0])
1414- else:
1415- description += val
1416- return description
1417-
1418- def generate_product_code(self, cr, uid, product_obj, code_generator, context=None):
1419- '''I wrote this stupid function to be able to inherit it in a custom module !'''
1420- return self.parse(cr, uid, product_obj, code_generator, context=context)
1421-
1422- def build_product_code_and_properties(self, cr, uid, ids, context=None):
1423- for product in self.browse(cr, uid, ids, context=context):
1424- new_default_code = self.generate_product_code(cr, uid, product,
1425- product.product_tmpl_id.code_generator,
1426- context=context)
1427- current_values = {
1428- 'default_code': product.default_code,
1429- #'track_production': product.track_production,
1430- #'track_outgoing': product.track_outgoing,
1431- #'track_incoming': product.track_incoming,
1432- }
1433- new_values = {
1434- 'default_code': new_default_code,
1435- #'track_production': product.product_tmpl_id.variant_track_production,
1436- #'track_outgoing': product.product_tmpl_id.variant_track_outgoing,
1437- #'track_incoming': product.product_tmpl_id.variant_track_incoming,
1438- }
1439- if new_values != current_values:
1440- self.write(cr, uid, [product.id], new_values, context=context)
1441- return True
1442-
1443- def product_ids_variant_changed(self, cr, uid, ids, res, context=None):
1444- '''it's a hook for product_variant_multi advanced'''
1445- return True
1446-
1447- def generate_variant_name(self, cr, uid, product_id, context=None):
1448- '''Do the generation of the variant name in a dedicated function, so that we can
1449- inherit this function to hack the code generation'''
1450- product = self.browse(cr, uid, product_id, context=context)
1451- model = product.variant_model_name
1452- r = map(lambda dim: [dim.dimension_id.sequence,
1453- self.parse(cr, uid, dim, model, context=context)],
1454- product.dimension_value_ids)
1455- r.sort()
1456- r = [x[1] for x in r]
1457- new_variant_name = (product.variant_model_name_separator or '').join(r)
1458- return new_variant_name
1459-
1460- def build_variants_name(self, cr, uid, ids, context=None):
1461- for product in self.browse(cr, uid, ids, context=context):
1462- new_variant_name = self.generate_variant_name(cr, uid, product.id, context=context)
1463- if new_variant_name != product.variants:
1464- self.write(cr, uid, [product.id], {'variants': new_variant_name}, context=context)
1465- return True
1466-
1467- def _check_dimension_values(self, cr, uid, ids):
1468- # TODO: check that all dimension_types of the product_template
1469- # have a corresponding dimension_value ??
1470- for product in self.browse(cr, uid, ids, {}):
1471- buffer = []
1472- for value in product.dimension_value_ids:
1473- buffer.append(value.dimension_id)
1474- unique_set = set(buffer)
1475- if len(unique_set) != len(buffer):
1476- raise orm.except_orm(_('Constraint error :'),
1477- _("On product '%s', there are several dimension values "
1478- "for the same dimension type.") % product.name)
1479- return True
1480-
1481- def compute_product_dimension_extra_price(self, cr, uid, product_id,
1482- product_price_extra=False, dim_price_margin=False,
1483- dim_price_extra=False, context=None):
1484- if context is None:
1485- context = {}
1486- dimension_extra = 0.0
1487- product = self.browse(cr, uid, product_id, context=context)
1488- for dim in product.dimension_value_ids:
1489- if product_price_extra and dim_price_margin and dim_price_extra:
1490- dimension_extra += (safe_eval('product.' + product_price_extra,
1491- {'product': product})
1492- * safe_eval('dim.' + dim_price_margin,
1493- {'dim': dim})
1494- + safe_eval('dim.' + dim_price_extra,
1495- {'dim': dim}))
1496- elif not product_price_extra and not dim_price_margin and dim_price_extra:
1497- dimension_extra += safe_eval('dim.' + dim_price_extra, {'dim': dim})
1498- elif product_price_extra and dim_price_margin and not dim_price_extra:
1499- dimension_extra += (safe_eval('product.' + product_price_extra,
1500- {'product': product})
1501- * safe_eval('dim.' + dim_price_margin,
1502- {'dim': dim}))
1503- elif product_price_extra and not dim_price_margin and dim_price_extra:
1504- dimension_extra += (safe_eval('product.' + product_price_extra,
1505- {'product': product})
1506- + safe_eval('dim.' + dim_price_extra, {'dim': dim}))
1507-
1508- if 'uom' in context:
1509- product_uom_obj = self.pool.get('product.uom')
1510- uom = product.uos_id or product.uom_id
1511- dimension_extra = product_uom_obj._compute_price(cr, uid, uom.id,
1512- dimension_extra, context['uom'])
1513- return dimension_extra
1514-
1515- def compute_dimension_extra_price(self, cr, uid, ids, result, product_price_extra=False,
1516- dim_price_margin=False, dim_price_extra=False, context=None):
1517- if context is None:
1518- context = {}
1519- for product in self.browse(cr, uid, ids, context=context):
1520- dimension_extra = self.compute_product_dimension_extra_price(
1521- cr, uid, product.id,
1522- product_price_extra=product_price_extra,
1523- dim_price_margin=dim_price_margin,
1524- dim_price_extra=dim_price_extra,
1525- context=context)
1526- result[product.id] += dimension_extra
1527- return result
1528-
1529- def price_get(self, cr, uid, ids, ptype='list_price', context=None):
1530- if context is None:
1531- context = {}
1532- result = super(product_product, self).price_get(cr, uid, ids, ptype, context=context)
1533- if ptype == 'list_price':
1534- #TODO check if the price_margin on the dimension is very usefull,
1535- # maybe we will remove it
1536- result = self.compute_dimension_extra_price(
1537- cr, uid, ids, result,
1538- product_price_extra='price_extra',
1539- dim_price_margin='price_margin',
1540- dim_price_extra='price_extra',
1541- context=context)
1542- elif ptype == 'standard_price':
1543- result = self.compute_dimension_extra_price(
1544- cr, uid, ids, result,
1545- product_price_extra='cost_price_extra',
1546- dim_price_extra='cost_price_extra',
1547- context=context)
1548- return result
1549-
1550- def _product_lst_price(self, cr, uid, ids, name, arg, context=None):
1551- if context is None:
1552- context = {}
1553- result = super(product_product, self)._product_lst_price(cr, uid, ids, name, arg,
1554- context=context)
1555- result = self.compute_dimension_extra_price(cr, uid, ids, result,
1556- product_price_extra='price_extra',
1557- dim_price_margin='price_margin',
1558- dim_price_extra='price_extra',
1559- context=context)
1560- return result
1561-
1562- def copy(self, cr, uid, id, default=None, context=None):
1563- if default is None:
1564- default = {}
1565- default = default.copy()
1566- default.update({'variant_ids': False})
1567- return super(product_product, self).copy(cr, uid, id, default, context)
1568-
1569- def _product_compute_weight_volume(self, cr, uid, ids, fields, arg, context=None):
1570- result = {}
1571- for product in self.browse(cr, uid, ids, context=context):
1572- result[product.id] = p = {}
1573- p['total_weight'] = product.weight + product.additional_weight
1574- p['total_weight_net'] = product.weight_net + product.additional_weight_net
1575- p['total_volume'] = product.volume + product.additional_volume
1576- return result
1577+ string_template_obj = self.pool['string.template']
1578+ vals = {
1579+ 'variants': string_template_obj._build(
1580+ cr, uid, product.template_name_id.id, product),
1581+ 'default_code': string_template_obj._build(
1582+ cr, uid, product.template_code_id.id, product),
1583+ }
1584+ vals['name'] = "%s %s" % (
1585+ product.product_tmpl_id.name or '',
1586+ vals['variants'])
1587+ return vals
1588+
1589+ def _remove_not_updated(self, cr, uid, product, vals, context=None):
1590+ vals_to_write = {}
1591+ for key in vals:
1592+ if vals[key] != product[key]:
1593+ vals_to_write[key] = vals[key]
1594+ return vals_to_write
1595
1596 _columns = {
1597- 'name': fields.char('Name', size=128, translate=True, select=True),
1598- 'variants': fields.char('Variants', size=128),
1599- 'dimension_value_ids': fields.many2many(
1600- 'product.variant.dimension.value',
1601- 'product_product_dimension_rel',
1602- 'product_id', 'dimension_id',
1603- 'Dimensions',
1604- domain="[('product_tmpl_id','=',product_tmpl_id)]"),
1605- 'cost_price_extra': fields.float('Purchase Extra Cost',
1606- digits_compute=dp.get_precision('Purchase Price')),
1607- 'lst_price': fields.function(_product_lst_price,
1608- method=True,
1609- type='float',
1610- string='List Price',
1611- digits_compute=dp.get_precision('Sale Price')),
1612- #the way the weight are implemented are not clean at all,
1613- #we should redesign the module product form the addons
1614- #in order to get something correclty.
1615- #indeed some field of the template have to be overwrited
1616- #like weight, name, weight_net, volume.
1617- #in order to have a consitent api we should use the same field for getting the weight,
1618- #now we have to use "weight" or "total_weight"
1619- #not clean at all with external syncronization
1620- 'total_weight': fields.function(_product_compute_weight_volume,
1621- method=True,
1622- type='float',
1623- string='Total Gross Weight',
1624- help="The gross weight in Kg.",
1625- multi='weight_volume'),
1626- 'total_weight_net': fields.function(_product_compute_weight_volume,
1627- method=True,
1628- type='float',
1629- string='Total Net Weight',
1630- help="The net weight in Kg.",
1631- multi='weight_volume'),
1632- 'total_volume': fields.function(_product_compute_weight_volume,
1633- method=True,
1634- type='float',
1635- string='Total Volume',
1636- help="The volume in m3.",
1637- multi='weight_volume'),
1638- 'additional_weight': fields.float('Additional Gross weight',
1639- help="The additional gross weight in Kg."),
1640- 'additional_weight_net': fields.float('Additional Net weight',
1641- help="The additional net weight in Kg."),
1642- 'additional_volume': fields.float('Additional Volume',
1643- help="The additional volume in Kg."),
1644+ 'name': fields.char(
1645+ 'Name',
1646+ size=128,
1647+ translate=True,
1648+ select=True),
1649+ 'variants': fields.char(
1650+ 'Variants',
1651+ size=128),
1652 }
1653-
1654- _constraints = [
1655- (_check_dimension_values, 'Error msg in raise', ['dimension_value_ids']),
1656- ]
1657
1658=== modified file 'product_variant_generator/product_view.xml'
1659--- product_variant_multi/product_view.xml 2013-04-22 21:00:07 +0000
1660+++ product_variant_generator/product_view.xml 2014-06-24 09:37:35 +0000
1661@@ -3,224 +3,108 @@
1662 <data>
1663
1664 <!--
1665- "Product variant multi" module for OpenERP
1666+ "Product variant generator" module for OpenERP
1667 The licence is in the file __openerp__.py
1668 @author Alexis de Lattre <alexis.delattre@akretion.com>
1669 @author Sebastien Beau <sebastien.beau@akretion.com>
1670+ @author Chafique Delli <chafique.delli@akretion.com>
1671 -->
1672
1673-
1674- <menuitem name="Variant Dimensions" id="menu_variant_dimension" parent="product.prod_config_main" />
1675-
1676-
1677- <!-- DIMENSION OPTION -->
1678- <record id="product_variant_multi_dimension_option_seach" model="ir.ui.view">
1679- <field name="name">product.variant.multi.dimension.option.search</field>
1680- <field name="model">product.variant.dimension.option</field>
1681- <field name="arch" type="xml">
1682- <search string="Dimension Options Search">
1683- <field name="name"/>
1684- <field name="dimension_id"/>
1685- </search>
1686- </field>
1687- </record>
1688-
1689- <record id="product_variant_multi_dimension_option_tree" model="ir.ui.view">
1690- <field name="name">product.variant.multi.dimension.option.tree</field>
1691- <field name="model">product.variant.dimension.option</field>
1692- <field name="arch" type="xml">
1693- <tree string="Dimension Options" editable="top">
1694- <field name="dimension_id" invisible="not context.get('dimension_option_main_view', False)" />
1695- <field name="name" />
1696- <field name="sequence" />
1697- <field name="code" />
1698- </tree>
1699- </field>
1700- </record>
1701-
1702- <record id="product_variant_multi_dimension_option_form" model="ir.ui.view">
1703- <field name="name">product.variant.multi.dimension.option.form</field>
1704- <field name="model">product.variant.dimension.option</field>
1705- <field name="arch" type="xml">
1706- <form string="Dimension Options">
1707- <field name="dimension_id" invisible="not context.get('dimension_option_main_view', False)" />
1708- <newline />
1709- <field name="sequence" />
1710- <field name="name" />
1711- <field name="code" />
1712- </form>
1713- </field>
1714- </record>
1715-
1716- <record id="action_dimension_option" model="ir.actions.act_window">
1717- <field name="name">Dimension Options</field>
1718- <field name="res_model">product.variant.dimension.option</field>
1719- <field name="view_type">form</field>
1720- <field name="view_mode">tree,form</field>
1721- <field name="context">{'dimension_option_main_view': True}</field>
1722- </record>
1723-
1724- <menuitem id="menu_variant_dimension_option" parent="menu_variant_dimension" action="action_dimension_option" />
1725-
1726-
1727 <!-- DIMENSION VALUES -->
1728- <record id="product_variant_multi_dimension_value_search" model="ir.ui.view">
1729- <field name="name">product.variant.multi.dimension.value.search</field>
1730- <field name="model">product.variant.dimension.value</field>
1731- <field name="arch" type="xml">
1732- <search string="Dimension Values Search">
1733- <field name="product_tmpl_id"/>
1734- <field name="option_id"/>
1735- </search>
1736- </field>
1737- </record>
1738-
1739-
1740- <record id="product_variant_multi_dimension_value_tree" model="ir.ui.view">
1741- <field name="name">product.variant.multi.dimension.value.tree</field>
1742- <field name="model">product.variant.dimension.value</field>
1743+ <record id="dimension_value_tree" model="ir.ui.view">
1744+ <field name="model">dimension.value</field>
1745 <field name="arch" type="xml">
1746 <tree string="Dimension Values" editable="top">
1747- <field name="product_tmpl_id" invisible="not context.get('dimension_value_main_view', False)" />
1748+ <field name="product_tmpl_id"
1749+ invisible="not context.get('dimension_value_main_view', False)"/>
1750 <field name="active" />
1751- <field name="dimension_id" />
1752 <field name="option_id" />
1753 <field name="sequence" />
1754- <field name="cost_price_extra" />
1755- <field name="price_extra" />
1756 </tree>
1757 </field>
1758 </record>
1759
1760 <record id="product_variant_multi_dimension_value_form" model="ir.ui.view">
1761- <field name="name">product.variant.multi.dimension.value.form</field>
1762- <field name="model">product.variant.dimension.value</field>
1763+ <field name="model">dimension.value</field>
1764 <field name="arch" type="xml">
1765 <form string="Dimension Values">
1766- <field name="product_tmpl_id" invisible="not context.get('dimension_value_main_view', False)" />
1767+ <field name="product_tmpl_id"
1768+ invisible="not context.get('dimension_value_main_view', False)"/>
1769 <field name="active" />
1770- <field name="dimension_id" />
1771 <newline />
1772 <field name="option_id" />
1773 <field name="sequence" />
1774- <field name="cost_price_extra" />
1775- <field name="price_extra" />
1776- <field name="price_margin" />
1777 </form>
1778 </field>
1779 </record>
1780
1781 <record id="action_dimension_value" model="ir.actions.act_window">
1782 <field name="name">Dimension values</field>
1783- <field name="res_model">product.variant.dimension.value</field>
1784+ <field name="res_model">dimension.value</field>
1785 <field name="view_type">form</field>
1786 <field name="view_mode">tree,form</field>
1787 <field name="context">{'dimension_value_main_view': True}</field>
1788 </record>
1789
1790-
1791- <!-- DIMENSION TYPE -->
1792- <record id="product_variant_multi_dimension_type_search" model="ir.ui.view">
1793- <field name="name">product.variant.multi.dimension.type.search</field>
1794- <field name="model">product.variant.dimension.type</field>
1795- <field name="arch" type="xml">
1796- <search string="Dimension Type Search">
1797- <field name="name"/>
1798- </search>
1799- </field>
1800- </record>
1801-
1802- <record id="product_variant_multi_dimension_type_tree" model="ir.ui.view">
1803- <field name="name">product.variant.multi.dimension.type.tree</field>
1804- <field name="model">product.variant.dimension.type</field>
1805- <field name="arch" type="xml">
1806- <tree string="Dimension Types">
1807- <field name="name" />
1808- <field name="description" />
1809- </tree>
1810- </field>
1811- </record>
1812-
1813- <record id="product_variant_multi_dimension_type_form" model="ir.ui.view">
1814- <field name="name">product.variant.multi.dimension.type.form</field>
1815- <field name="model">product.variant.dimension.type</field>
1816- <field name="arch" type="xml">
1817- <form string="Dimension Types" version="7.0">
1818- <sheet>
1819- <div class="oe_title">
1820- <label for="name" string="Dimension Type Name" class="oe_edit_only"/>
1821- <h1>
1822- <field name="name" />
1823- </h1>
1824- </div>
1825- <group>
1826- <field name="description" />
1827- <field name="sequence" />
1828- <field name="allow_custom_value" />
1829- <field name="mandatory_dimension" />
1830- <field name="option_ids" nolabel="1" colspan="4"/>
1831- </group>
1832- </sheet>
1833- </form>
1834- </field>
1835- </record>
1836-
1837-
1838- <record id="action_dimension_type" model="ir.actions.act_window">
1839- <field name="name">Dimension Types</field>
1840- <field name="res_model">product.variant.dimension.type</field>
1841- <field name="view_type">form</field>
1842- <field name="view_mode">tree,form</field>
1843- <field name="context">{'dimension_type_main_view': True}</field>
1844- </record>
1845-
1846- <menuitem id="menu_variant_dimension_types" parent="menu_variant_dimension" action="action_dimension_type" />
1847-
1848+ <record id="attribute_attribute_form_view" model="ir.ui.view">
1849+ <field name="model">attribute.attribute</field>
1850+ <field name="inherit_id" ref="base_custom_attributes.attribute_attribute_form_view"/>
1851+ <field name="arch" type="xml">
1852+ <field name="translate" position="after">
1853+ <field name="is_dimension"/>
1854+ <field name="sequence"
1855+ attrs="{'invisible': [('is_dimension','=', False)]}"/>
1856+ <field name="mandatory_dimension"
1857+ attrs="{'invisible': [('is_dimension','=', False)]}"/>
1858+ </field>
1859+ </field>
1860+ </record>
1861
1862 <!-- PRODUCT TEMPLATE -->
1863-
1864-<!-- TODO remove useless view of product.template in the module product or fusion this view with the existing view in product, it's depend how the inheritage on this view will be managed by the other module-->
1865-
1866-<!--TODO add a wizard to add the option-->
1867-<!--<button name="add_some_option" string="Add " type="object" colspan="2"/>-->
1868-
1869- <record id="product_search_form_view_template" model="ir.ui.view">
1870- <field name="name">product.search.form.template</field>
1871- <field name="model">product.template</field>
1872- <field name="arch" type="xml">
1873- <search string="Product Template">
1874- <field name="name"/>
1875- </search>
1876- </field>
1877- </record>
1878-
1879 <record id="product_variant_multi_product_template_form_view" model="ir.ui.view">
1880- <field name="name">product.variant.multi.product.template.form</field>
1881 <field name="model">product.template</field>
1882 <field name="inherit_id" ref="product.product_template_form_view" />
1883 <field name="arch" type="xml">
1884 <xpath expr="//field[@name='categ_id']/.." position="after">
1885 <group>
1886 <field name="is_multi_variants"/>
1887+ <field name="attribute_set_id"
1888+ attrs="{'invisible':[('is_multi_variants','=',False)]}"/>
1889 </group>
1890 </xpath>
1891 <xpath expr="//form/notebook" position="inside">
1892 <page string="Variants">
1893 <group colspan="1" col="4" attrs="{'invisible':[('is_multi_variants','=',False)]}">
1894- <field name="dimension_type_ids" nolabel="1" colspan="4" />
1895- <button name="add_all_option" string="Add All Options" type="object" colspan="2"/>
1896- <field name="value_ids" nolabel="1" colspan="4" />
1897- <field name="variant_model_name" colspan="4"/>
1898- <field name="variant_model_name_separator" colspan="4"/>
1899- <field name="code_generator" colspan="4"/>
1900+ <button name="%(product_variant_generator.action_add_option)d"
1901+ string="Add Option"
1902+ type="action"/>
1903+ <field name="value_ids" nolabel="1" colspan="4">
1904+ <tree string="Values" editable="top">
1905+ <field name="active"/>
1906+ <field name="dimension_id"
1907+ on_change="on_dimension_change(dimension_id, context)"
1908+ domain="[('dimension_id','in',parent.dimension_ids)]" />
1909+ <field name="option_id"/>
1910+ </tree>
1911+ </field>
1912+ <field name="base_default_code" colspan="4"/>
1913+ <field name="template_name_id" colspan="4"/>
1914+ <field name="template_code_id" colspan="4"/>
1915 <field name="variant_track_production" colspan="4"/>
1916 <field name="variant_track_incoming" colspan="4"/>
1917 <field name="variant_track_outgoing" colspan="4"/>
1918 <field name="do_not_generate_new_variant" colspan="4"/>
1919 <field name="do_not_update_variant" colspan="4"/>
1920- <button name="button_generate_variants" string="Generate / Update Variants" type="object" colspan="4"/>
1921+ <button name="button_generate_variants"
1922+ string="Generate / Update Variants"
1923+ type="object"
1924+ colspan="4"/>
1925 </group>
1926- <field name="variant_ids" string="Variants" nolabel="1" colspan="1">
1927+ <separator string="Variants"/>
1928+ <field name="variant_ids"
1929+ string="Variants"
1930+ nolabel="1"
1931+ colspan="1">
1932 <tree string="Variants">
1933 <field name="code" />
1934 <field name="name" />
1935@@ -240,27 +124,8 @@
1936 <field name="view_mode">tree,form</field>
1937 </record>
1938
1939- <menuitem action="product_template" id="menu_template" parent="product.prod_config_main" />
1940-
1941-
1942 <!-- PRODUCT VARIANTS -->
1943-
1944- <!-- TODO remove useless view of product.product in the module product or move this correct view in product, it's depend how the heritage on this view will be managed by the other module -->
1945-
1946- <record id="product_search_form_view_variants" model="ir.ui.view">
1947- <field name="name">product.search.form.variants</field>
1948- <field name="model">product.product</field>
1949- <field name="inherit_id" ref="product.product_search_form_view"/>
1950- <field name="arch" type="xml">
1951- <field name="categ_id" position="after">
1952- <field name="product_tmpl_id"/>
1953- <field name="variants"/>
1954- </field>
1955- </field>
1956- </record>
1957-
1958 <record id="product_variant_form_view" model="ir.ui.view">
1959- <field name="name">product.variant.form</field>
1960 <field name="model">product.product</field>
1961 <field name="arch" type="xml">
1962 <form string="Product Variant" version="7.0">
1963@@ -280,27 +145,12 @@
1964 <field name="default_code" />
1965 <field name="active" />
1966 </group>
1967- <notebook colspan="4">
1968- <page string="Dimensions">
1969- <separator string="Dimension Values" colspan="4"/>
1970- <field name="dimension_value_ids" context="{'product_tmpl_id':product_tmpl_id}" nolabel="1" colspan="4"/>
1971- </page>
1972- <page string="Prices">
1973- <group>
1974- <field name="list_price" string="Template Sale Price" readonly="1"/>
1975- <field name="price_margin" />
1976- <field name="price_extra" />
1977- <field name="cost_price_extra"/>
1978- </group>
1979- </page>
1980- </notebook>
1981 </sheet>
1982 </form>
1983 </field>
1984 </record>
1985
1986 <record id="product_variant_tree_view" model="ir.ui.view">
1987- <field name="name">product.variant.tree</field>
1988 <field name="model">product.product</field>
1989 <field name="arch" type="xml">
1990 <tree string="Product Variant">
1991@@ -308,37 +158,11 @@
1992 <field name="name"/>
1993 <field name="product_tmpl_id"/>
1994 <field name="variants"/>
1995- <field name="price_extra"/>
1996- <field name="cost_price_extra"/>
1997 </tree>
1998 </field>
1999 </record>
2000
2001- <record id="product_variant" model="ir.actions.act_window">
2002- <field name="name">Product Variants</field>
2003- <field name="res_model">product.product</field>
2004- <field name="view_type">form</field>
2005- <field name="view_mode">tree,form</field>
2006- </record>
2007-
2008- <record id="action_variant_tree" model="ir.actions.act_window.view">
2009- <field name="sequence" eval="10" />
2010- <field name="view_mode">tree</field>
2011- <field name="act_window_id" ref="product_variant" />
2012- <field name="view_id" ref="product_variant_tree_view" />
2013- </record>
2014-
2015- <record id="action_variant_form" model="ir.actions.act_window.view">
2016- <field name="sequence" eval="20" />
2017- <field name="view_mode">form</field>
2018- <field name="act_window_id" ref="product_variant" />
2019- <field name="view_id" ref="product_variant_form_view" />
2020- </record>
2021-
2022- <menuitem action="product_variant" id="menu_variant" parent="product.prod_config_main" />
2023-
2024 <record id="product_normal_variant_form_view" model="ir.ui.view">
2025- <field name="name">product.normal.variant.form</field>
2026 <field name="model">product.product</field>
2027 <field name="inherit_id" ref="product.product_normal_form_view" />
2028 <field name="arch" type="xml">
2029@@ -349,33 +173,54 @@
2030 <field name="is_multi_variants" readonly="1"/>
2031 <label for="is_multi_variants" />
2032 </div>
2033- <field name="standard_price" position="replace">
2034- <group name='cost_prices' colspan="2" col="2">
2035- <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/>
2036- <field name="cost_price_extra" groups="product.group_product_variant"/>
2037- </group>
2038- </field>
2039- <group name="Weights" position="replace">
2040- <group colspan="4" col="6" name="Weights" groups="product.group_stock_packaging">
2041- <group name="template_weights" string="Template Weights">
2042- <field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
2043- <field name="weight" attrs="{'readonly':[('type','=','service')]}"/>
2044- <field name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
2045- </group>
2046- <group name="variant_weights" string="Variant Weights" attrs="{'invisible':[('is_multi_variants','=',False)]}">
2047- <field digits="(14, 3)" name="additional_volume" attrs="{'readonly':[('type','=','service')]}"/>
2048- <field name="additional_weight" attrs="{'readonly':[('type','=','service')]}"/>
2049- <field name="additional_weight_net" attrs="{'readonly':[('type','=','service')]}"/>
2050- </group>
2051- <group name="total_weights" string="Total Weights" attrs="{'invisible':[('is_multi_variants','=',False)]}">
2052- <field digits="(14, 3)" name="total_volume"/>
2053- <field name="total_weight"/>
2054- <field name="total_weight_net"/>
2055- </group>
2056- </group>
2057- </group>
2058- </field>
2059- </record>
2060+ </field>
2061+ </record>
2062+
2063+ <record id="view_model_string_template_search" model="ir.ui.view">
2064+ <field name="model">string.template</field>
2065+ <field name="arch" type="xml">
2066+ <search string="String Template">
2067+ <field name="name"/>
2068+ </search>
2069+ </field>
2070+ </record>
2071+
2072+ <record id="view_model_string_template_tree" model="ir.ui.view">
2073+ <field name="model">string.template</field>
2074+ <field name="arch" type="xml">
2075+ <tree string="String Template">
2076+ <field name="name"/>
2077+ <field name="type"/>
2078+ </tree>
2079+ </field>
2080+ </record>
2081+
2082+ <record id="view_model_string_template_form" model="ir.ui.view">
2083+ <field name="model">string.template</field>
2084+ <field name="arch" type="xml">
2085+ <form string="Label" version="7.0">
2086+ <field name="name"/>
2087+ <field name="type"/>
2088+ <field name="code"/>
2089+ </form>
2090+ </field>
2091+ </record>
2092+
2093+ <record id="string_template_form_action" model="ir.actions.act_window">
2094+ <field name="res_model">string.template</field>
2095+ <field name="view_type">form</field>
2096+ <field name="view_mode">tree,form</field>
2097+ <field name="view_id" ref="view_model_string_template_tree"/>
2098+ <field name="search_view_id" ref="view_model_string_template_search"/>
2099+ <field name="help"></field>
2100+ </record>
2101+
2102+ <menuitem
2103+ name="Product Code/Name generator"
2104+ action="string_template_form_action"
2105+ id="menu_string_template_action"
2106+ parent="product.prod_config_main"
2107+ sequence="40"/>
2108
2109 </data>
2110 </openerp>
2111
2112=== modified file 'product_variant_generator/security/ir.model.access.csv'
2113--- product_variant_multi/security/ir.model.access.csv 2013-04-22 21:00:07 +0000
2114+++ product_variant_generator/security/ir.model.access.csv 2014-06-24 09:37:35 +0000
2115@@ -1,7 +1,3 @@
2116 "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
2117-"access_product_variant_dimension_type_user","Read access on product.variant.dimension.type for employees","model_product_variant_dimension_type","base.group_user",1,0,0,0
2118-"access_product_variant_dimension_value_user","Read access on product.variant.dimension.value for employees","model_product_variant_dimension_value","base.group_user",1,0,0,0
2119-"access_product_variant_dimension_option_user","Read access on product.variant.dimension.option for employees","model_product_variant_dimension_option","base.group_user",1,0,0,0
2120-"access_product_variant_dimension_type_sale_manager","Full rights on product.variant.dimension.type for sale manager","model_product_variant_dimension_type","base.group_sale_manager",1,1,1,1
2121-"access_product_variant_dimension_value_sale_manager","Full rights on product.variant.dimension.value for sale manager","model_product_variant_dimension_value","base.group_sale_manager",1,1,1,1
2122-"access_product_variant_dimension_option_sale_manager","Full rights on product.variant.dimension.option for sale manager","model_product_variant_dimension_option","base.group_sale_manager",1,1,1,1
2123+"access_dimension_value_user","Read access on dimension.value for employees","model_dimension_value","base.group_user",1,0,0,0
2124+"access_dimension_value_sale_manager","Full rights on dimension.value for sale manager","model_dimension_value","base.group_sale_manager",1,1,1,1
2125
2126=== added directory 'product_variant_generator/wizard'
2127=== added file 'product_variant_generator/wizard/__init__.py'
2128--- product_variant_generator/wizard/__init__.py 1970-01-01 00:00:00 +0000
2129+++ product_variant_generator/wizard/__init__.py 2014-06-24 09:37:35 +0000
2130@@ -0,0 +1,24 @@
2131+# -*- coding: utf-8 -*-
2132+###############################################################################
2133+#
2134+# Module for OpenERP
2135+# Copyright (C) 2014 Akretion (http://www.akretion.com).
2136+# @author Sébastien BEAU <sebastien.beau@akretion.com>
2137+#
2138+# This program is free software: you can redistribute it and/or modify
2139+# it under the terms of the GNU Affero General Public License as
2140+# published by the Free Software Foundation, either version 3 of the
2141+# License, or (at your option) any later version.
2142+#
2143+# This program is distributed in the hope that it will be useful,
2144+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2145+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2146+# GNU Affero General Public License for more details.
2147+#
2148+# You should have received a copy of the GNU Affero General Public License
2149+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2150+#
2151+###############################################################################
2152+
2153+from . import product_template_add_option
2154+
2155
2156=== added file 'product_variant_generator/wizard/product_template_add_option.py'
2157--- product_variant_generator/wizard/product_template_add_option.py 1970-01-01 00:00:00 +0000
2158+++ product_variant_generator/wizard/product_template_add_option.py 2014-06-24 09:37:35 +0000
2159@@ -0,0 +1,69 @@
2160+# -*- coding: utf-8 -*-
2161+###############################################################################
2162+#
2163+# Module for OpenERP
2164+# Copyright (C) 2014 Akretion (http://www.akretion.com).
2165+# @author Sébastien BEAU <sebastien.beau@akretion.com>
2166+#
2167+# This program is free software: you can redistribute it and/or modify
2168+# it under the terms of the GNU Affero General Public License as
2169+# published by the Free Software Foundation, either version 3 of the
2170+# License, or (at your option) any later version.
2171+#
2172+# This program is distributed in the hope that it will be useful,
2173+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2174+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2175+# GNU Affero General Public License for more details.
2176+#
2177+# You should have received a copy of the GNU Affero General Public License
2178+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2179+#
2180+###############################################################################
2181+
2182+from openerp.osv import fields, orm
2183+
2184+
2185+class ProductTemplateAddOption(orm.TransientModel):
2186+ _name = 'product.template.add.option'
2187+ _description = 'Product Template Add Option'
2188+
2189+ def _get_option_domain(self, cr, uid, tmpl, context=None):
2190+ domain = [('attribute_id', 'in', [x.id for x in tmpl.dimension_ids])]
2191+ existing_option_ids = [value.option_id.id for value in tmpl.value_ids]
2192+ if existing_option_ids:
2193+ domain.append(('id', 'not in', existing_option_ids))
2194+ return domain
2195+
2196+ def fields_view_get(self, cr, uid, view_id=None, view_type='form',
2197+ context=None, toolbar=False, submenu=False):
2198+ res = super(ProductTemplateAddOption, self).fields_view_get(
2199+ cr, uid, view_id=view_id, view_type=view_type,
2200+ context=context, toolbar=toolbar, submenu=submenu,
2201+ )
2202+ tmpl_id = context.get('active_id')
2203+ if tmpl_id and view_type == 'form':
2204+ tmpl_obj = self.pool['product.template']
2205+ tmpl = tmpl_obj.browse(cr, uid, tmpl_id, context=context)
2206+ res['fields']['option_ids']['domain'] = self._get_option_domain(
2207+ cr, uid, tmpl, context=context)
2208+ return res
2209+
2210+ _columns = {
2211+ 'option_ids': fields.many2many(
2212+ 'attribute.option',
2213+ string='Option'),
2214+ }
2215+
2216+ def add_option(self, cr, uid, ids, context=None):
2217+ tmpl_obj = self.pool['product.template']
2218+ for wizard in self.browse(cr, uid, ids, context=context):
2219+ values = []
2220+ for option in wizard.option_ids:
2221+ values.append([0, 0, {
2222+ 'dimension_id': option.attribute_id.id,
2223+ 'option_id': option.id,
2224+ }])
2225+ tmpl_obj.write(cr, uid, context['active_id'], {
2226+ 'value_ids': values,
2227+ }, context=context)
2228+ return True
2229
2230=== added file 'product_variant_generator/wizard/product_template_add_option_view.xml'
2231--- product_variant_generator/wizard/product_template_add_option_view.xml 1970-01-01 00:00:00 +0000
2232+++ product_variant_generator/wizard/product_template_add_option_view.xml 2014-06-24 09:37:35 +0000
2233@@ -0,0 +1,29 @@
2234+<?xml version="1.0" encoding="UTF-8"?>
2235+<openerp>
2236+ <data>
2237+
2238+ <record id="action_add_option" model="ir.actions.act_window">
2239+ <field name="name">Add Option</field>
2240+ <field name="res_model">product.template.add.option</field>
2241+ <field name="view_type">form</field>
2242+ <field name="view_mode">form</field>
2243+ <field name="target">new</field>
2244+ </record>
2245+
2246+ <record id="view_product_template_add_option_form" model="ir.ui.view">
2247+ <field name="model">product.template.add.option</field>
2248+ <field name="arch" type="xml">
2249+ <form string="Add Option" version="7.0">
2250+ <field name="option_ids"/>
2251+ <footer>
2252+ <button name="add_option" string="Confirm" type="object" class="oe_highlight"/>
2253+ or
2254+ <button string="Cancel" class="oe_link" special="cancel" />
2255+ </footer>
2256+
2257+ </form>
2258+ </field>
2259+ </record>
2260+
2261+ </data>
2262+</openerp>
2263
2264=== added directory 'product_variant_generator_price'
2265=== added file 'product_variant_generator_price/__init__.py'
2266--- product_variant_generator_price/__init__.py 1970-01-01 00:00:00 +0000
2267+++ product_variant_generator_price/__init__.py 2014-06-24 09:37:35 +0000
2268@@ -0,0 +1,23 @@
2269+# -*- encoding: utf-8 -*-
2270+##############################################################################
2271+#
2272+# OpenERP, Open Source Management Solution
2273+# Copyright (C) 2014 Akretion (http://www.akretion.com)
2274+#
2275+# This program is free software: you can redistribute it and/or modify
2276+# it under the terms of the GNU General Public License as published by
2277+# the Free Software Foundation, either version 3 of the License, or
2278+# (at your option) any later version.
2279+#
2280+# This program is distributed in the hope that it will be useful,
2281+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2282+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2283+# GNU General Public License for more details.
2284+#
2285+# You should have received a copy of the GNU General Public License
2286+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2287+#
2288+##############################################################################
2289+# flake8: noqa
2290+
2291+from . import product_variant
2292
2293=== added file 'product_variant_generator_price/__openerp__.py'
2294--- product_variant_generator_price/__openerp__.py 1970-01-01 00:00:00 +0000
2295+++ product_variant_generator_price/__openerp__.py 2014-06-24 09:37:35 +0000
2296@@ -0,0 +1,40 @@
2297+# -*- encoding: utf-8 -*-
2298+##############################################################################
2299+#
2300+# OpenERP, Open Source Management Solution
2301+# Copyright (C) 2014 Akretion (http://www.akretion.com)
2302+#
2303+# This program is free software: you can redistribute it and/or modify
2304+# it under the terms of the GNU General Public License as published by
2305+# the Free Software Foundation, either version 3 of the License, or
2306+# (at your option) any later version.
2307+#
2308+# This program is distributed in the hope that it will be useful,
2309+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2310+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2311+# GNU General Public License for more details.
2312+#
2313+# You should have received a copy of the GNU General Public License
2314+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2315+#
2316+##############################################################################
2317+{
2318+ "name": "Product Variant Generator Price",
2319+ "version": "1.0",
2320+ "author": "OpenERP SA, Akretion",
2321+ "category": "Sales Management",
2322+ "license": "AGPL-3",
2323+ "summary": "Product Prices with multi-dimension variants",
2324+ "description": """
2325+
2326+ """,
2327+ "depends" : [
2328+ "product_variant_generator",
2329+ ],
2330+ "data" : [
2331+ "product_view.xml",
2332+ ],
2333+ "application": True,
2334+ "active": False,
2335+ "installable": True,
2336+}
2337
2338=== added file 'product_variant_generator_price/product_variant.py'
2339--- product_variant_generator_price/product_variant.py 1970-01-01 00:00:00 +0000
2340+++ product_variant_generator_price/product_variant.py 2014-06-24 09:37:35 +0000
2341@@ -0,0 +1,119 @@
2342+# -*- encoding: utf-8 -*-
2343+##############################################################################
2344+#
2345+# OpenERP, Open Source Management Solution
2346+# Copyright (C) 2014 Akretion (www.akretion.com). All Rights Reserved
2347+# @author Chafique Delli <chafique.delli@akretion.com>
2348+#
2349+# This program is free software: you can redistribute it and/or modify
2350+# it under the terms of the GNU General Public License as published by
2351+# the Free Software Foundation, either version 3 of the License, or
2352+# (at your option) any later version.
2353+#
2354+# This program is distributed in the hope that it will be useful,
2355+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2356+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2357+# GNU General Public License for more details.
2358+#
2359+# You should have received a copy of the GNU General Public License
2360+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2361+#
2362+##############################################################################
2363+
2364+from openerp.osv import fields, orm
2365+
2366+
2367+class ProductTemplate(orm.Model):
2368+ _inherit = "product.template"
2369+
2370+ _columns = {
2371+ 'automatic_price_extra': fields.boolean("Automatic Price Extra"),
2372+ }
2373+
2374+
2375+class ProductProduct(orm.Model):
2376+ _inherit = "product.product"
2377+
2378+ def _get_price_extra(self, cr, uid, ids, field_names=None, arg=False,
2379+ context=None):
2380+ res = {}
2381+ for product in self.browse(cr, uid, ids, context=context):
2382+ price_extra = 0.00
2383+ if product.automatic_price_extra:
2384+ for dimension in product.dimension_ids:
2385+ option = product[dimension.name]
2386+ if option:
2387+ for dimension_value in product.value_ids:
2388+ if dimension_value.option_id.id == option.id:
2389+ price_extra += dimension_value.price_extra
2390+ break
2391+ else:
2392+ price_extra = product.manual_price_extra
2393+ res[product.id] = price_extra
2394+ return res
2395+
2396+ def _set_extra_price(self, cr, uid, ids, field_names=None, value=None,
2397+ arg=False, context=None):
2398+ if isinstance(ids, (int, long)):
2399+ ids = [ids]
2400+ for product in self.browse(cr, uid, ids, context=context):
2401+ if not product.automatic_price_extra:
2402+ self.write(
2403+ cr, uid, product.id, {'manual_price_extra': value},
2404+ context=context)
2405+ else:
2406+ price = self._get_price_extra(
2407+ cr, uid, [product.id], field_names=field_names, arg=arg,
2408+ context=context)[product.id]
2409+ cr.execute("""update product_product set
2410+ price_extra=%s where id=%s""", (price, product.id))
2411+ return True
2412+
2413+ def _get_products_from_product_template(self, cr, uid, ids, context=None):
2414+ return self.get_products_from_product_template(
2415+ cr, uid, ids, context=context)
2416+
2417+ def _get_products_from_dimension_value(self, cr, uid, ids, context=None):
2418+ res = []
2419+ for value in self.browse(cr, uid, ids, context=context):
2420+ res += self.pool['product.product'].search(cr, uid,
2421+ [('product_tmpl_id', '=', value.product_tmpl_id.id),
2422+ (value.dimension_id.name, '=', value.option_id.id)],
2423+ context=context)
2424+ return res
2425+
2426+ _columns = {
2427+ 'price_extra': fields.function(
2428+ _get_price_extra,
2429+ type='float',
2430+ fnct_inv=_set_extra_price,
2431+ string='Price Extra',
2432+ store ={
2433+ 'product.product': (
2434+ lambda self, cr, uid, ids, c={}: ids,
2435+ ['product_tmpl_id', 'manual_price_extra'],
2436+ 10),
2437+ 'product.template': (
2438+ _get_products_from_product_template,
2439+ ['automatic_price_extra'],
2440+ 10),
2441+ 'dimension.value': (
2442+ _get_products_from_dimension_value,
2443+ ['price_extra'],
2444+ 10)
2445+ },
2446+ ),
2447+ 'manual_price_extra': fields.float('Manual Price Extra'),
2448+ }
2449+
2450+
2451+class DimensionValue(orm.Model):
2452+ _inherit = "dimension.value"
2453+
2454+ _columns = {
2455+ 'price_extra': fields.float('Price Extra'),
2456+ }
2457+
2458+ _defaults = {
2459+ 'price_extra': 0,
2460+ }
2461
2462=== added file 'product_variant_generator_price/product_view.xml'
2463--- product_variant_generator_price/product_view.xml 1970-01-01 00:00:00 +0000
2464+++ product_variant_generator_price/product_view.xml 2014-06-24 09:37:35 +0000
2465@@ -0,0 +1,79 @@
2466+<?xml version="1.0" encoding="utf-8"?>
2467+<openerp>
2468+ <data>
2469+
2470+<!--
2471+ "Product variant generator price" module for OpenERP
2472+ The licence is in the file __openerp__.py
2473+ @author Chafique Delli <chafique.delli@akretion.com>
2474+-->
2475+
2476+ <!-- PRODUCT TEMPLATE -->
2477+ <record id="product_variant_price_product_template_form_view" model="ir.ui.view">
2478+ <field name="name">product.variant.price.product.template.form</field>
2479+ <field name="model">product.template</field>
2480+ <field name="inherit_id" ref="product_variant_generator.product_variant_multi_product_template_form_view"/>
2481+ <field name="arch" type="xml">
2482+ <field name="option_id" position="after">
2483+ <field name="price_extra"/>
2484+ </field>
2485+ <xpath expr="//field[@name='variant_ids']/tree[@string='Variants']/field[@name='variants']" position="after">
2486+ <field name="price_extra"/>
2487+ </xpath>
2488+ <field name="attribute_set_id" position="after">
2489+ <field name="automatic_price_extra"
2490+ attrs="{'invisible':[('is_multi_variants', '=', False)]}"/>
2491+ </field>
2492+ </field>
2493+ </record>
2494+
2495+ <!-- PRODUCT PRODUCT -->
2496+ <record id="product_variant_price_product_normal_form_view" model="ir.ui.view">
2497+ <field name="name">product.variant.price.product.normal.form</field>
2498+ <field name="model">product.product</field>
2499+ <field name="inherit_id" ref="product.product_normal_form_view"/>
2500+ <field name="arch" type="xml">
2501+ <field name="list_price" position="replace">
2502+ <field name="lst_price" string="Sale Price"/>
2503+ </field>
2504+ <field name="price_margin" position="replace">
2505+ <field name="list_price" string="Base Price"/>
2506+ </field>
2507+ <div name="options" position="inside">
2508+ <field name="automatic_price_extra" invisible="True"/>
2509+ </div>
2510+ <field name="price_extra" position="replace">
2511+ <field name="price_extra"
2512+ attrs="{'readonly':[('automatic_price_extra', '=', True)]}"/>
2513+ </field>
2514+ </field>
2515+ </record>
2516+
2517+ <!-- PRODUCT VARIANT -->
2518+ <record id="product_variant_form_view" model="ir.ui.view">
2519+ <field name="model">product.product</field>
2520+ <field name="inherit_id" ref="product.product_variant_form_view"/>
2521+ <field name="arch" type="xml">
2522+ <field name="price_extra" position="after">
2523+ <field name="automatic_price_extra" invisible="True"/>
2524+ </field>
2525+ <field name="price_extra" position="replace">
2526+ <field name="price_extra"
2527+ attrs="{'readonly':[('automatic_price_extra', '=', True)]}"/>
2528+ </field>
2529+ </field>
2530+ </record>
2531+
2532+ <!-- DIMENSION VALUES -->
2533+ <record id="product_variant_multi_dimension_value_form" model="ir.ui.view">
2534+ <field name="model">dimension.value</field>
2535+ <field name="inherit_id" ref="product_variant_generator.product_variant_multi_dimension_value_form"/>
2536+ <field name="arch" type="xml">
2537+ <field name="option_id" position="after">
2538+ <field name="price_extra"/>
2539+ </field>
2540+ </field>
2541+ </record>
2542+
2543+ </data>
2544+</openerp>
2545
2546=== removed file 'product_variant_multi/demo_data.xml'
2547--- product_variant_multi/demo_data.xml 2011-08-23 07:46:17 +0000
2548+++ product_variant_multi/demo_data.xml 1970-01-01 00:00:00 +0000
2549@@ -1,84 +0,0 @@
2550-<?xml version="1.0" ?>
2551-<openerp>
2552-
2553- <data noupdate="1">
2554-<!--
2555- <record id="product_template_mezzanine" model="product.template">
2556- <field eval="1.0" name="list_price"/>
2557- <field eval="1.0" name="standard_price"/>
2558- <field eval="&quot;&quot;&quot;fixed&quot;&quot;&quot;" name="mes_type"/>
2559- <field name="uom_id" ref="product.product_uom_unit"/>
2560- <field eval="1.0" name="uos_coeff"/>
2561- <field eval="1" name="sale_ok"/>
2562- <field eval="1" name="purchase_ok"/>
2563- <field name="company_id" ref="base.main_company"/>
2564- <field name="uom_po_id" ref="product.product_uom_unit"/>
2565- <field eval="&quot;&quot;&quot;product&quot;&quot;&quot;" name="type"/>
2566- <field eval="&quot;&quot;&quot;Mezzanine&quot;&quot;&quot;" name="name"/>
2567- <field name="cost_method">standard</field>
2568- <field name="supply_method">produce</field>
2569- <field name="procure_method">make_to_order</field>
2570- <field eval="0" name="rental"/>
2571- <field eval="7.0" name="sale_delay"/>
2572- <field name="categ_id" ref="product.cat1"/>
2573- <field eval="1.0" name="produce_delay"/>
2574- </record>
2575- <record id="product_variant_dimension_type_hauteur0" model="product.variant.dimension.type">
2576- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2577- <field eval="1" name="allow_custom_value"/>
2578- <field eval="&quot;&quot;&quot;Hauteur&quot;&quot;&quot;" name="name"/>
2579- <field eval="0" name="sequence"/>
2580- </record>
2581- <record id="product_variant_dimension_type_largeur0" model="product.variant.dimension.type">
2582- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2583- <field eval="1" name="allow_custom_value"/>
2584- <field eval="&quot;&quot;&quot;Largeur&quot;&quot;&quot;" name="name"/>
2585- <field eval="1" name="sequence"/>
2586- </record>
2587- <record id="product_variant_dimension_value_0" model="product.variant.dimension.value">
2588- <field eval="&quot;&quot;&quot;10&quot;&quot;&quot;" name="name"/>
2589- <field name="dimension_id" ref="product_variant_dimension_type_hauteur0"/>
2590- <field model="product.template" name="product_tmpl_id" search="[('name','=','Mezzanine')]"/>
2591- </record>
2592- <record id="product_variant_dimension_value_1" model="product.variant.dimension.value">
2593- <field eval="&quot;&quot;&quot;20&quot;&quot;&quot;" name="name"/>
2594- <field name="dimension_id" ref="product_variant_dimension_type_hauteur0"/>
2595- <field model="product.template" name="product_tmpl_id" search="[('name','=','Mezzanine')]"/>
2596- </record>
2597-
2598- <record id="product_variant_dimension_value_2" model="product.variant.dimension.value">
2599- <field eval="&quot;&quot;&quot;22&quot;&quot;&quot;" name="name"/>
2600- <field name="dimension_id" ref="product_variant_dimension_type_largeur0"/>
2601- <field eval="1.0" name="dimension_sequence"/>
2602- <field model="product.template" name="product_tmpl_id" search="[('name','=','Mezzanine')]"/>
2603- </record>
2604- <record id="product_variant_dimension_value_3" model="product.variant.dimension.value">
2605- <field eval="&quot;&quot;&quot;33&quot;&quot;&quot;" name="name"/>
2606- <field name="dimension_id" ref="product_variant_dimension_type_largeur0"/>
2607- <field eval="1.0" name="dimension_sequence"/>
2608- <field model="product.template" name="product_tmpl_id" search="[('name','=','Mezzanine')]"/>
2609- </record>
2610-
2611- <record id="product_product_mezzanine0" model="product.product">
2612- <field eval="1" name="active"/>
2613- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2614- <field eval="[(6,0,[ref('product_variant_dimension_value_0'),ref('product_variant_dimension_value_2')])]" name="dimension_value_ids"/>
2615- </record>
2616- <record id="product_product_mezzanine1" model="product.product">
2617- <field eval="1" name="active"/>
2618- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2619- <field eval="[(6,0,[ref('product_variant_dimension_value_0'),ref('product_variant_dimension_value_3')])]" name="dimension_value_ids"/>
2620- </record>
2621- <record id="product_product_mezzanine2" model="product.product">
2622- <field eval="1" name="active"/>
2623- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2624- <field eval="[(6,0,[ref('product_variant_dimension_value_1'),ref('product_variant_dimension_value_2')])]" name="dimension_value_ids"/>
2625- </record>
2626- <record id="product_product_mezzanine3" model="product.product">
2627- <field eval="1" name="active"/>
2628- <field name="product_tmpl_id" ref="product_template_mezzanine"/>
2629- <field eval="[(6,0,[ref('product_variant_dimension_value_1'),ref('product_variant_dimension_value_3')])]" name="dimension_value_ids"/>
2630- </record>
2631--->
2632- </data>
2633-</openerp>
2634
2635=== added directory 'product_variant_simple'
2636=== added file 'product_variant_simple/__init__.py'
2637--- product_variant_simple/__init__.py 1970-01-01 00:00:00 +0000
2638+++ product_variant_simple/__init__.py 2014-06-24 09:37:35 +0000
2639@@ -0,0 +1,3 @@
2640+# -*- coding: utf-8 -*-
2641+
2642+from . import product
2643
2644=== added file 'product_variant_simple/__openerp__.py'
2645--- product_variant_simple/__openerp__.py 1970-01-01 00:00:00 +0000
2646+++ product_variant_simple/__openerp__.py 2014-06-24 09:37:35 +0000
2647@@ -0,0 +1,47 @@
2648+# -*- coding: utf-8 -*-
2649+##############################################################################
2650+#
2651+# Author: Yannick Vaucher
2652+# Copyright 2013 Camptocamp SA
2653+#
2654+# This program is free software: you can redistribute it and/or modify
2655+# it under the terms of the GNU Affero General Public License as
2656+# published by the Free Software Foundation, either version 3 of the
2657+# License, or (at your option) any later version.
2658+#
2659+# This program is distributed in the hope that it will be useful,
2660+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2661+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2662+# GNU Affero General Public License for more details.
2663+#
2664+# You should have received a copy of the GNU Affero General Public License
2665+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2666+#
2667+##############################################################################
2668+{'name' : 'Simple Product Variants',
2669+ 'version' : '1.0',
2670+ 'category': '',
2671+ 'description': """
2672+Simple Product Variants
2673+=======================
2674+
2675+A simple module for handling the product variants.
2676+It provides views for templates and for variants,
2677+so they can be created manually. It doesn't provide
2678+all the bells and whistles of the product_variant_multi
2679+module (generators wizards, ...).
2680+
2681+""",
2682+ 'author' : 'Camptocamp',
2683+ 'maintainer': 'Camptocamp',
2684+ 'website': 'http://www.camptocamp.com/',
2685+ 'depends' : ['product',
2686+ ],
2687+ 'data': ['product_view.xml',
2688+ 'security/security.xml',
2689+ ],
2690+ 'test': [],
2691+ 'installable': True,
2692+ 'auto_install': False,
2693+ 'application': True,
2694+ }
2695
2696=== added file 'product_variant_simple/product.py'
2697--- product_variant_simple/product.py 1970-01-01 00:00:00 +0000
2698+++ product_variant_simple/product.py 2014-06-24 09:37:35 +0000
2699@@ -0,0 +1,32 @@
2700+# -*- coding: utf-8 -*-
2701+##############################################################################
2702+#
2703+# Author: Guewen Baconnier
2704+# Copyright 2013 Camptocamp SA
2705+#
2706+# This program is free software: you can redistribute it and/or modify
2707+# it under the terms of the GNU Affero General Public License as
2708+# published by the Free Software Foundation, either version 3 of the
2709+# License, or (at your option) any later version.
2710+#
2711+# This program is distributed in the hope that it will be useful,
2712+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2713+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2714+# GNU Affero General Public License for more details.
2715+#
2716+# You should have received a copy of the GNU Affero General Public License
2717+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2718+#
2719+##############################################################################
2720+
2721+from openerp.osv import orm, fields
2722+
2723+
2724+class ProductTemplate(orm.Model):
2725+ _inherit = 'product.template'
2726+ _columns = {
2727+ 'variant_ids': fields.one2many(
2728+ 'product.product',
2729+ 'product_tmpl_id',
2730+ string='Variants'),
2731+ }
2732
2733=== added file 'product_variant_simple/product_view.xml'
2734--- product_variant_simple/product_view.xml 1970-01-01 00:00:00 +0000
2735+++ product_variant_simple/product_view.xml 2014-06-24 09:37:35 +0000
2736@@ -0,0 +1,92 @@
2737+<?xml version="1.0" encoding="utf-8"?>
2738+<openerp>
2739+ <data>
2740+
2741+ <!-- Templates -->
2742+
2743+ <record id="product_template_search_view" model="ir.ui.view">
2744+ <field name="name">product.template.search</field>
2745+ <field name="model">product.template</field>
2746+ <field name="arch" type="xml">
2747+ <search string="Product Templates">
2748+ <field name="name" string="Product"/>
2749+ <filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
2750+ <filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
2751+ <field name="categ_id"/>
2752+ <group expand='0' string='Group by...'>
2753+ <filter string='Category'
2754+ icon="terp-stock_symbol-selection"
2755+ domain="[]"
2756+ context="{'group_by': 'categ_id'}"/>
2757+ <filter string='Type'
2758+ icon="terp-stock_symbol-selection"
2759+ domain="[]"
2760+ context="{'group_by': 'type'}"/>
2761+ </group>
2762+ </search>
2763+ </field>
2764+ </record>
2765+
2766+ <record id="product_template" model="ir.actions.act_window">
2767+ <field name="name">Product Templates</field>
2768+ <field name="res_model">product.template</field>
2769+ <field name="view_type">form</field>
2770+ <field name="domain">[]</field>
2771+ <field name="view_mode">tree,form</field>
2772+ <field name="search_view_id" ref="product_template_search_view"/>
2773+ </record>
2774+
2775+ <menuitem action="product_template" id="menu_template"
2776+ sequence="15"
2777+ parent="base.menu_product" />
2778+
2779+ <!-- Variants -->
2780+
2781+ <record id="product_variant_search_view" model="ir.ui.view">
2782+ <field name="name">product.variant.search</field>
2783+ <field name="model">product.product</field>
2784+ <field name="arch" type="xml">
2785+ <search string="Product Variants">
2786+ <field name="product_tmpl_id" string="Product"/>
2787+ <field name="name" string="Variant"
2788+ filter_domain="['|', '|',
2789+ ('variants', 'ilike', self),
2790+ ('default_code', 'ilike', self)]"/>
2791+ <group expand='0' string='Group by...'>
2792+ <filter string='Template'
2793+ icon="terp-stock_symbol-selection"
2794+ domain="[]"
2795+ context="{'group_by': 'product_tmpl_id'}"/>
2796+ </group>
2797+ </search>
2798+ </field>
2799+ </record>
2800+
2801+ <record id="product_variant" model="ir.actions.act_window">
2802+ <field name="name">Product Variants</field>
2803+ <field name="res_model">product.product</field>
2804+ <field name="view_type">form</field>
2805+ <field name="view_mode">tree,form</field>
2806+ <field name="search_view_id" ref="product_variant_search_view"/>
2807+ </record>
2808+
2809+ <record id="action_variant_tree" model="ir.actions.act_window.view">
2810+ <field name="sequence" eval="10" />
2811+ <field name="view_mode">tree</field>
2812+ <field name="act_window_id" ref="product_variant" />
2813+ <field name="view_id" ref="product.product_variant_tree_view" />
2814+ </record>
2815+
2816+ <record id="action_variant_form" model="ir.actions.act_window.view">
2817+ <field name="sequence" eval="20" />
2818+ <field name="view_mode">form</field>
2819+ <field name="act_window_id" ref="product_variant" />
2820+ <field name="view_id" ref="product.product_variant_form_view" />
2821+ </record>
2822+
2823+ <menuitem action="product_variant" id="menu_variant"
2824+ sequence="20"
2825+ parent="base.menu_product" />
2826+
2827+ </data>
2828+</openerp>
2829
2830=== added directory 'product_variant_simple/security'
2831=== added file 'product_variant_simple/security/security.xml'
2832--- product_variant_simple/security/security.xml 1970-01-01 00:00:00 +0000
2833+++ product_variant_simple/security/security.xml 2014-06-24 09:37:35 +0000
2834@@ -0,0 +1,13 @@
2835+<?xml version="1.0" encoding="utf-8"?>
2836+<openerp>
2837+ <data noupdate="1">
2838+
2839+ <!-- Activate the Product Variant group on the Sales Manager -->
2840+ <record model="res.groups" id="base.group_sale_manager">
2841+ <field name="implied_ids" eval="[(4, ref('product.group_product_variant'))]"/>
2842+ </record>
2843+
2844+ </data>
2845+</openerp>
2846+
2847+
2848
2849=== added directory 'sale_product_display'
2850=== added file 'sale_product_display/__init__.py'
2851--- sale_product_display/__init__.py 1970-01-01 00:00:00 +0000
2852+++ sale_product_display/__init__.py 2014-06-24 09:37:35 +0000
2853@@ -0,0 +1,23 @@
2854+# -*- coding: utf-8 -*-
2855+###############################################################################
2856+#
2857+# Module for OpenERP
2858+# Copyright (C) 2013 Akretion (http://www.akretion.com).
2859+# @author Chafique Delli <chafique.delli@akretion.com>
2860+#
2861+# This program is free software: you can redistribute it and/or modify
2862+# it under the terms of the GNU Affero General Public License as
2863+# published by the Free Software Foundation, either version 3 of the
2864+# License, or (at your option) any later version.
2865+#
2866+# This program is distributed in the hope that it will be useful,
2867+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2868+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2869+# GNU Affero General Public License for more details.
2870+#
2871+# You should have received a copy of the GNU Affero General Public License
2872+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2873+#
2874+###############################################################################
2875+
2876+import sale
2877
2878=== added file 'sale_product_display/__openerp__.py'
2879--- sale_product_display/__openerp__.py 1970-01-01 00:00:00 +0000
2880+++ sale_product_display/__openerp__.py 2014-06-24 09:37:35 +0000
2881@@ -0,0 +1,40 @@
2882+# -*- coding: utf-8 -*-
2883+##############################################################################
2884+#
2885+# Author: Chafique Delli
2886+# Copyright 2013 Akretion
2887+#
2888+# This program is free software: you can redistribute it and/or modify
2889+# it under the terms of the GNU Affero General Public License as
2890+# published by the Free Software Foundation, either version 3 of the
2891+# License, or (at your option) any later version.
2892+#
2893+# This program is distributed in the hope that it will be useful,
2894+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2895+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2896+# GNU Affero General Public License for more details.
2897+#
2898+# You should have received a copy of the GNU Affero General Public License
2899+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2900+#
2901+##############################################################################
2902+{'name' : 'Sale Product Display',
2903+ 'version' : '1.0',
2904+ "author": "Akretion",
2905+ 'category': 'Sales Management',
2906+ 'description': """
2907+Sale Product Display
2908+====================
2909+
2910+A simple module for preventing validation of a command that contains a product display.
2911+
2912+""",
2913+ 'author' : 'Akretion',
2914+ 'maintainer': 'Akretion',
2915+ 'website': 'http://www.akretion.com/',
2916+ 'depends' : [
2917+ 'sale',
2918+ 'product_display'
2919+ ],
2920+ 'auto_install': True,
2921+ }
2922
2923=== added file 'sale_product_display/sale.py'
2924--- sale_product_display/sale.py 1970-01-01 00:00:00 +0000
2925+++ sale_product_display/sale.py 2014-06-24 09:37:35 +0000
2926@@ -0,0 +1,39 @@
2927+# -*- coding: utf-8 -*-
2928+###############################################################################
2929+#
2930+# Module for OpenERP
2931+# Copyright (C) 2013 Akretion (http://www.akretion.com).
2932+# @author Chafique Delli <chafique.delli@akretion.com>
2933+#
2934+# This program is free software: you can redistribute it and/or modify
2935+# it under the terms of the GNU Affero General Public License as
2936+# published by the Free Software Foundation, either version 3 of the
2937+# License, or (at your option) any later version.
2938+#
2939+# This program is distributed in the hope that it will be useful,
2940+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2941+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2942+# GNU Affero General Public License for more details.
2943+#
2944+# You should have received a copy of the GNU Affero General Public License
2945+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2946+#
2947+###############################################################################
2948+
2949+from openerp.osv import orm, osv
2950+from openerp.tools.translate import _
2951+
2952+
2953+class SaleOrderLine(orm.Model):
2954+ _inherit = 'sale.order.line'
2955+
2956+
2957+ def _check_product_display(self, cr, uid, ids, context=None):
2958+ for record in self.browse(cr, uid, ids, context=context):
2959+ if record.product_id.is_display:
2960+ raise osv.except_osv(_('Error'), _('You cannot validate the order with product display %s.')% (record.product_id.name))
2961+ return True
2962+
2963+ _constraints = [
2964+ (_check_product_display, 'You cannot validate an order with a product display.',
2965+ [])]

Subscribers

People subscribed via source and target branches