Commits

grasbauer committed c3dea9c

initial import

Comments (0)

Files changed (6)

+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+from configuration import *
+from product import *
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+{
+    'name' : 'Product Variants',
+    'version' : '2.0.2',
+    'author' : 'grasbauer',
+    'email': 'info@grasbauer.com',
+    'website': 'http://www.grasbauer.com/',
+    'description': 'one click variant generation',
+    'depends' : [
+        'ir',
+        'res',
+        'product',
+            ],
+    'xml' : [
+             'configuration.xml',
+             'product.xml',
+
+            ]
+}
+
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
+from trytond.pyson import Eval
+
+
+class Configuration(ModelSingleton, ModelSQL, ModelView):
+    'Product Configuration'
+    _name = 'product.variant.configuration'
+    _description = __doc__
+
+    identifier_seperator = fields.Property(fields.Char('Identifier seperator'))
+    code_seperator = fields.Property(fields.Char('Code seperator'))
+
+Configuration()

configuration.xml

+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<tryton>
+    <data>
+        <record model="ir.ui.view" id="product_configuration_view_form">
+            <field name="model">variant.configuration</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <![CDATA[
+                <form string="Variant Configuration">
+                    <label name="identifier_seperator"/>
+                    <field name="identifier_seperator"/>
+                    <label name="code_seperator" />
+                    <field name="code_seperator" />
+                </form>
+                ]]>
+            </field>
+        </record>
+        
+         <record model="ir.action.act_window" 
+                 id="act_variant_configuration_form">
+            <field name="name">Variant Configuration</field>
+            <field name="res_model">product.variant.configuration</field>
+        </record>
+       
+        <record model="ir.action.act_window.view"
+            id="act_product_configuration_view1">
+            <field name="sequence" eval="1"/>
+            <field name="view" ref="act_variant_configuration_form"/>
+            <field name="act_window" ref="act_variant_configuration_form"/>
+        </record>
+        
+        <menuitem parent="product.menu_main_product"
+            action="act_variant_configuration_form"
+            id="menu_variant_configuration"
+            sequence="0" icon="tryton-list"
+            groups="product.group_product_admin"/>
+
+        
+    </data>
+</tryton>
+#-*- coding:utf-8 -*-
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+from trytond.model import ModelView, ModelSQL, fields
+from trytond.pyson import Equal, Eval, Greater, Not, In, If, Get, Bool, Or, And, PYSONEncoder
+from trytond.transaction import Transaction
+import logging
+import copy
+import itertools
+
+STATES = {
+    'readonly': Not(Bool(Eval('active'))),
+}
+
+class Product(ModelSQL, ModelView):
+    _name = 'product.product'
+
+    identifier = fields.Char('Identifier', readonly=True)
+
+    def __init__(self):
+        super(Product, self).__init__()
+        fields = self._columns
+        for field in self._inherit_fields.itervalues():
+            if not field in fields:
+                field[2].states['readonly'] = Eval('variant')
+        self.template = copy.copy(self.template)
+        self.template.states = copy.copy(self.template.states)
+        self.template.states['required'] = Greater(Eval('active_id', 0), 0)
+        self.template.states['invisible'] = Not(Greater(Eval('active_id', 0), 0))
+        self._reset_columns()
+
+    def fields_get(self, fields_names=None):
+        res = super(Product, self).fields_get(fields_names)
+        if 'codebase' in res.keys(): del res['codebase']
+        return res
+
+    def create(self, vals):
+        if not vals.get('template'):
+            vals.pop('template')
+        return super(Product, self).create(vals)
+
+    def delete(self, ids):
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+        Transaction().set_context(save_templates=True)
+        res = super(Product, self).delete(ids)
+        return res
+
+Product()
+
+class Template(ModelSQL, ModelView):
+    _name = "product.template"
+
+    codebase = fields.Char('Code')
+    attributes = fields.Many2Many('product.template-product.attribute', 'template',
+            'attribute', 'Attributes', states=STATES)
+
+    variant = fields.Function(fields.Boolean('Variant'),
+            'get_variants')
+
+    description = fields.Text("Description", translate=True)
+
+    def __init__(self):
+        super(Template, self).__init__()
+        self._rpc.update({
+            'generate_contained_products': True,
+                   })
+
+    def delete(self, ids):
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+        templates = self.browse(ids)
+        for template in templates:
+            if template.products or  Transaction().context.get('save_templates'):
+                return
+        res = super(Template, self).delete(ids)
+        return res
+
+    def get_variants(self, ids, name):
+        res = {}
+        for template in self.browse(ids):
+            res[template.id] = False
+            if len(template.products) > 1:
+                res[template.id] = True
+        return res
+
+    def generate_contained_products(self, ids):
+        """
+        generate all variants - if not already done
+        """
+        product_obj = self.pool.get('product.product')
+        config_obj = self.pool.get('product.variant.configuration')
+        config = config_obj.browse(1)
+        if not ids:
+            return {}
+        res = {}
+        for template in self.browse(ids):
+            if not template.attributes:
+                continue
+            already = [p.code for p in template.products]
+            #map?
+            identifier = [(i.sequence, i.values) for i in template.attributes]
+            identifier.sort()
+            identifier = [i[1] for i in identifier]
+            variants = list(itertools.product(*identifier))
+
+            for variant in variants:
+                identifier = config.identifier_seperator.join(
+                                                 [i.name for i in variant])
+                code = '%s%s' % (template.codebase or '', config.code_seperator)
+                code = code + config.code_seperator.join(
+                                                 [i.code for i in variant])
+                if not code in already:
+                    product_obj.create({'template':template.id,
+                                        'identifier':identifier,
+                                        'code':code})
+        return res
+Template()
+
+class ProductAttribute(ModelSQL, ModelView):
+    "Possible Attribute"
+    _name = "product.attribute"
+    _description = __doc__
+
+    sequence = fields.Integer('Sequence')
+    name = fields.Char('Name', size=None, required=True, translate=True,
+            select=1)
+    values = fields.One2Many('product.attribute.value', 'attribute', 'Values')
+
+    def __init__(self):
+        super(ProductAttribute, self).__init__()
+
+ProductAttribute()
+
+
+class ProductAttributeValue(ModelSQL, ModelView):
+    "Values for Attributes"
+    _name = "product.attribute.value"
+    _description = __doc__
+
+    sequence = fields.Integer('Sequence')
+    name = fields.Char('Name', required=True, select=1)
+    code = fields.Char('Code', required=True)
+    attribute = fields.Many2One('product.attribute', 'Product Attribute',
+            required=True, ondelete='CASCADE', select=1)
+    def __init__(self):
+        super(ProductAttributeValue, self).__init__()
+        self._order.insert(0, ('sequence', 'ASC'))
+
+ProductAttributeValue()
+
+
+class ProductTemplateAttribute(ModelSQL, ModelView):
+    "Product Template Product Attribute"
+    _name = "product.template-product.attribute"
+    _description = __doc__
+
+
+    attribute = fields.Many2One('product.attribute', 'Product Attribute',
+            ondelete='CASCADE', select=1, required=True)
+    template = fields.Many2One('product.template', 'Product template',
+            ondelete='CASCADE', select=1, required=True)
+
+ProductTemplateAttribute()
+
+
+
+<?xml version="1.0"?>
+	<!--
+		This file is part of Tryton. The COPYRIGHT file at the top level of
+		this repository contains the full copyright notices and license terms.
+	-->
+<tryton>
+	<data>
+		<!-- productview anpassen -->
+		<record model="ir.ui.view" id="product_view_form">
+			<field name="model">product.product</field>
+			<field name="inherit" ref="product.product_view_form" />
+			<field name="arch" type="xml">
+				<![CDATA[
+				<data>
+					<xpath expr="/form/notebook/page[@id=&quot;products&quot;]"
+						position="replace">
+					</xpath>
+					<xpath expr="/form/notebook/page[@id=&quot;description&quot;]"
+						position="replace">
+					</xpath>
+					<xpath
+						expr="/form/notebook/page[@id=&quot;general&quot;]/label[@name=&quot;code&quot;]"
+						position="before">
+						<label name="template" />
+						<field name="template" />
+						<label name="variant" />
+						<field name="variant" />
+
+					</xpath>
+					<xpath
+						expr="/form/notebook/page[@id=&quot;general&quot;]/field[@name=&quot;list_price&quot;]"
+						position="after">
+						<label name="identifier" />
+						<field name="identifier" />
+					</xpath>
+				</data>
+
+				]]>
+			</field>
+		</record>
+
+		<record model="ir.ui.view" id="product_view_tree2">
+			<field name="model">product.product</field>
+			<field name="inherit" ref="product.product_view_tree" />
+			<field name="arch" type="xml">
+                <![CDATA[
+                <data>
+                    <xpath
+                        expr="/tree/field[@name=&quot;code&quot;]"
+                        position="after">
+                         <field name="identifier"/>
+                    </xpath>
+                    
+                    
+                </data>
+                ]]>
+			</field>
+		</record>
+
+		
+		<!--  access to product templates -->
+
+		<record model="ir.action.act_window" id="act_product_template_form">
+			<field name="name">Product Template</field>
+			<field name="res_model">product.template</field>
+
+
+		</record>
+
+		<menuitem name="Product Template" parent="product.menu_main_product"
+			sequence="0" id="menu_product_template" action="act_product_template_form"
+			groups="product.group_product_admin" />
+		
+
+		<record model="ir.ui.view" id="template_view_form">
+			<field name="model">product.template</field>
+			<field name="inherit" ref="product.template_view_form" />
+			<field name="arch" type="xml">
+				<![CDATA[
+				<data>
+					<xpath expr="/form/field[@name=&quot;active&quot;]" position="after">
+						<label name="codebase" />
+						<field name="codebase" />
+					</xpath>
+
+					<xpath expr="/form/notebook/page[@id=&quot;general&quot;]"
+						position="after">
+						<page string="Description" col="4" id="description">
+							<field name="description" colspan="4" />
+
+						</page>
+					</xpath>
+					<xpath expr="/form/notebook/page[@id=&quot;general&quot;]"
+						position="after">
+						<page string="Products" col="4" id="products">
+							<field name="products" colspan="4" />
+							<field name="attributes" colspan="4" />
+							<button name="generate_contained_products" 
+							        string="Generate Variants"
+								    type="object" 
+								    icon="tryton-ok" />
+						</page>
+					</xpath>
+				</data>
+				]]>
+			</field>
+		</record>
+
+		<record model="ir.ui.view" id="template_view_tree">
+			<field name="model">product.template</field>
+			<field name="inherit" ref="product.template_view_tree" />
+			<field name="arch" type="xml">
+                <![CDATA[
+                <data>
+                    <xpath
+                        expr="/tree/field[@name=&quot;name&quot;]"
+                        position="after">
+                        <field name="variants"/>   
+                    </xpath>
+                    
+                </data>
+                ]]>
+			</field>
+		</record>
+
+
+
+		<!-- Attribute -->
+		<record model="ir.action.act_window" id="act_product_attribute_form">
+			<field name="name">Product Attributes</field>
+			<field name="res_model">product.attribute</field>
+		</record>
+		<record model="ir.ui.view" id="product_attribute_view_form">
+			<field name="model">product.attribute</field>
+			<field name="type">form</field>
+			<field name="arch" type="xml">
+                <![CDATA[
+                <form string="Product Attributes">
+                <label name="name"/>
+                <field name="name"/>
+                <label name="sequence"/>
+                <field name="sequence"/>
+                <newline/>
+                <field name="values" colspan="4"/>
+                </form>
+                
+                ]]>
+			</field>
+		</record>
+
+		<record model="ir.ui.view" id="product_attribute_view_tree">
+			<field name="model">product.attribute</field>
+			<field name="type">tree</field>
+			<field name="arch" type="xml">
+                <![CDATA[
+                <tree string="Product Attributes">
+                <field name="sequence"/>
+                <field name="name"/>
+                <field name="values"/>
+                </tree>
+                ]]>
+			</field>
+		</record>
+
+		<menuitem name="Product Attributes" parent="product.menu_main_product"
+			sequence="2" id="menu_product_attribute" action="act_product_attribute_form"
+			groups="product.group_product_admin" />
+
+		<record model="ir.ui.view" id="product_attribute_value_view_form">
+			<field name="model">product.attribute.value</field>
+			<field name="type">form</field>
+			<field name="arch" type="xml">
+                <![CDATA[
+                <form string="Product Attribute Values">
+                <label name="name"/>
+                <field name="name"/>
+                <label name="code"/>
+                <field name="code"/>
+                </form>
+                ]]>
+			</field>
+		</record>
+		<record model="ir.ui.view" id="product_attribute_value_view_tree">
+			<field name="model">product.attribute.value</field>
+			<field name="type">tree</field>
+			<field name="arch" type="xml">
+                <![CDATA[
+                <tree string="Product Attribute Values" sequence="sequence">
+                <field name="sequence" tree_invisible="1"/>
+                <field name="name"/>
+                <field name="code"/>
+                </tree>
+                ]]>
+			</field>
+		</record>
+
+
+	</data>
+</tryton>