trytond-product_variant-old/product.py

241 lines
8.5 KiB
Python

#This file is part product_variant module for Tryton.
#The COPYRIGHT file at the top level of this repository contains
#the full copyright notices and license terms.
from trytond.pool import Pool, PoolMeta
from trytond.model import ModelView, ModelSQL, fields
from trytond.pyson import Eval, Not, Bool
from trytond.transaction import Transaction
import itertools
__all__ = ['Product', 'Template', 'ProductAttribute', 'AttributeValue',
'ProductTemplateAttribute', 'ProductAttributeValue']
class Product:
__metaclass__ = PoolMeta
__name__ = 'product.product'
attribute_values = fields.Many2Many('product.product-attribute.value',
'product', 'value', 'Values', readonly=True,
order=[('value', 'DESC')])
@classmethod
def create(cls, vlist):
for vals in vlist:
if vals.get('template') and not vals['template']:
vals = vals.copy()
vals.pop('template')
return super(Product, cls).create(vlist)
class Template:
__metaclass__ = PoolMeta
__name__ = 'product.template'
basecode = fields.Char('Basecode',
states={
'invisible': Not(Bool(Eval('attributes')))
}, depends=['attributes'])
attributes = fields.Many2Many('product.template-product.attribute',
'template', 'attribute', 'Attributes')
variants = fields.Function(fields.Integer('Variants', select=1,
help='Number variants from this template'),
'get_variants', searcher='search_variants')
@classmethod
def __setup__(cls):
super(Template, cls).__setup__()
cls._buttons.update({
'generate_variants': {
'invisible': Eval('template'),
}
})
@classmethod
def delete(cls, templates):
#don't know - but this prevent always the deleation of the template
#so the user has to delete empty templates manually
templates = list(set(templates))
if Transaction().delete:
return templates
return super(Template, cls).delete(templates)
def get_variants(self, name=None):
variants = len(self.products)
if variants <= 1:
variants = None
return variants
@classmethod
def search_variants(cls, name, clause):
res = []
for template in cls.search([]):
if len(template.products) >= clause[2]:
res.append(template.id)
return [('id', 'in', res)]
@classmethod
def create_variant_code(cls, basecode, variant):
Config = Pool().get('product.configuration')
config = Config(1)
sep = config.code_separator or ''
code = '%s%s' % (basecode or '', ['', sep][bool(basecode)])
code = code + sep.join(i.code for i in variant)
return code
def create_variant_product(self, variant):
"Create the product from variant"
pool = Pool()
Product = pool.get('product.product')
code = self.create_variant_code(self.basecode, variant)
product, = Product.create([{
'template': self.id,
'code': code,
'attribute_values': [('add', [v.id for v in variant])],
}])
return product
def update_variant_product(self, products, variant):
"""Updates the code of supplied products with the code returned by
create_code()"""
pool = Pool()
Product = pool.get('product.product')
code = self.create_variant_code(self.basecode, variant)
to_update = [p for p in products if p.code != code]
if to_update:
Product.write(to_update, {
'code': code,
})
def deactivate_variant_product(self, products):
"""Deactivates supplied products"""
pool = Pool()
Product = pool.get('product.product')
to_update = [p for p in products if p.active]
if to_update:
Product.write(to_update, {
'active': False,
})
@classmethod
@ModelView.button
def generate_variants(cls, templates):
"""Generate variants"""
Product = Pool().get('product.product')
for template in templates:
if not template.attributes:
continue
all_template_products = Product.search([
('template', '=', template.id),
('active', 'in', (True, False)),
])
products_by_attr_values = {}
to_deactivate = []
for product in all_template_products:
if (product.attribute_values
and all(v.active for v in product.attribute_values)):
key = tuple(
sorted(v.id for v in product.attribute_values))
products_by_attr_values.setdefault(key, []).append(
product)
continue
to_deactivate.append(product)
values = [a.values for a in template.attributes]
for variant in itertools.product(*values):
key = tuple(sorted(v.id for v in variant))
if key in products_by_attr_values:
template.update_variant_product(
products_by_attr_values[key], variant)
else:
template.create_variant_product(variant)
template.deactivate_variant_product(to_deactivate)
class ProductAttribute(ModelSQL, ModelView):
"Product Attribute"
__name__ = "product.attribute"
sequence = fields.Integer('Sequence')
name = fields.Char('Name', required=True, translate=True, select=1)
values = fields.One2Many('product.attribute.value', 'attribute', 'Values')
@classmethod
def __setup__(cls):
super(ProductAttribute, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [table.sequence == None, table.sequence]
class AttributeValue(ModelSQL, ModelView):
"Values for Attributes"
__name__ = "product.attribute.value"
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')
active = fields.Boolean('Active', select=True)
@classmethod
def __setup__(cls):
super(AttributeValue, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
@staticmethod
def default_sequence():
return 0
@staticmethod
def default_active():
return True
def deactivate(self, values):
"""Deactivates products attribute values"""
pool = Pool()
Product = pool.get('product.attribute.value')
to_update = [p for p in values if p.active]
if to_update:
Product.write(to_update, {
'active': False,
})
def activate(self, values):
"""Deactivates products attribute values"""
pool = Pool()
Product = pool.get('product.attribute.value')
to_update = [p for p in values if not p.active]
if to_update:
Product.write(to_update, {
'active': True,
})
class ProductTemplateAttribute(ModelSQL, ModelView):
"Product Template - Product Attribute"
__name__ = "product.template-product.attribute"
attribute = fields.Many2One('product.attribute', 'Product Attribute',
ondelete='RESTRICT', required=True)
template = fields.Many2One('product.template', 'Product template',
ondelete='CASCADE', required=True)
class ProductAttributeValue(ModelSQL, ModelView):
"Product - Product Attribute Value"
__name__ = "product.product-attribute.value"
product = fields.Many2One('product.product', 'Product',
ondelete='CASCADE', required=True)
value = fields.Many2One('product.attribute.value', 'Attribute Value',
ondelete='CASCADE', required=True)
@classmethod
def search(cls, args, offset=0, limit=None, order=None, count=False,
query=False):
'''Order attributes value by sequence'''
res = super(ProductAttributeValue, cls).search(args, offset, limit,
order, count, query)
obs = [(ob.value.attribute.sequence, ob.id) for ob in res]
obs.sort()
res = [cls(i[1]) for i in obs]
return res