2014-12-02 12:55:39 +01:00
|
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2018-12-29 14:20:29 +01:00
|
|
|
from trytond.i18n import gettext
|
2021-12-11 17:58:43 +01:00
|
|
|
from trytond.model import (
|
|
|
|
|
MatchMixin, ModelSQL, ModelView, fields, sequence_ordered)
|
2018-12-29 14:20:29 +01:00
|
|
|
from trytond.model.exceptions import RecursionError
|
2012-09-24 22:12:50 +02:00
|
|
|
from trytond.pool import PoolMeta
|
2022-11-24 19:16:51 +01:00
|
|
|
from trytond.pyson import Bool, Eval, Get, If, TimeDelta
|
2012-09-24 22:12:50 +02:00
|
|
|
|
2017-01-13 20:20:10 +01:00
|
|
|
|
2018-06-26 19:11:49 +02:00
|
|
|
class Template(metaclass=PoolMeta):
|
2017-01-13 20:20:10 +01:00
|
|
|
__name__ = 'product.template'
|
2021-04-03 23:30:21 +02:00
|
|
|
producible = fields.Boolean("Producible")
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def __setup__(cls):
|
|
|
|
|
super().__setup__()
|
|
|
|
|
cls.producible.states = {
|
|
|
|
|
'invisible': ~Eval('type').in_(cls.get_producible_types()),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_producible_types(cls):
|
|
|
|
|
return ['goods', 'assets']
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2020-04-20 14:00:59 +02:00
|
|
|
@classmethod
|
|
|
|
|
def view_attributes(cls):
|
|
|
|
|
return super().view_attributes() + [
|
|
|
|
|
('//page[@id="production"]', 'states', {
|
|
|
|
|
'invisible': ~Eval('producible'),
|
|
|
|
|
})]
|
|
|
|
|
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2018-06-26 19:11:49 +02:00
|
|
|
class Product(metaclass=PoolMeta):
|
2012-09-24 22:12:50 +02:00
|
|
|
__name__ = 'product.product'
|
2012-05-13 23:00:54 +02:00
|
|
|
|
|
|
|
|
boms = fields.One2Many('product.product-production.bom', 'product',
|
2012-06-13 23:38:52 +02:00
|
|
|
'BOMs', order=[('sequence', 'ASC'), ('id', 'ASC')],
|
|
|
|
|
states={
|
2017-01-13 20:20:10 +01:00
|
|
|
'invisible': ~Eval('producible')
|
2022-04-08 19:07:13 +02:00
|
|
|
})
|
2023-03-13 22:53:44 +01:00
|
|
|
production_lead_times = fields.One2Many('production.lead_time',
|
2016-06-24 09:22:52 +02:00
|
|
|
'product', 'Lead Times', order=[('sequence', 'ASC'), ('id', 'ASC')],
|
|
|
|
|
states={
|
2017-01-13 20:20:10 +01:00
|
|
|
'invisible': ~Eval('producible'),
|
2022-04-08 19:07:13 +02:00
|
|
|
})
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2013-03-07 16:55:39 +01:00
|
|
|
@classmethod
|
|
|
|
|
def validate(cls, products):
|
|
|
|
|
super(Product, cls).validate(products)
|
|
|
|
|
for product in products:
|
|
|
|
|
product.check_bom_recursion()
|
|
|
|
|
|
2012-09-24 22:12:50 +02:00
|
|
|
def check_bom_recursion(self, product=None):
|
2012-05-13 23:00:54 +02:00
|
|
|
'''
|
|
|
|
|
Check BOM recursion
|
|
|
|
|
'''
|
2012-09-24 22:12:50 +02:00
|
|
|
if product is None:
|
|
|
|
|
product = self
|
|
|
|
|
for product_bom in self.boms:
|
|
|
|
|
for input_ in product_bom.bom.inputs:
|
2020-03-01 12:33:52 +01:00
|
|
|
if (input_.product == product
|
|
|
|
|
or input_.product.check_bom_recursion(
|
|
|
|
|
product=product)):
|
2018-12-29 14:20:29 +01:00
|
|
|
raise RecursionError(
|
|
|
|
|
gettext('production.msg_recursive_bom',
|
|
|
|
|
product=product.rec_name))
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2013-03-29 00:01:53 +01:00
|
|
|
@classmethod
|
|
|
|
|
def copy(cls, products, default=None):
|
|
|
|
|
if default is None:
|
|
|
|
|
default = {}
|
2018-09-01 01:02:03 +02:00
|
|
|
else:
|
|
|
|
|
default = default.copy()
|
2013-03-29 00:01:53 +01:00
|
|
|
default.setdefault('boms', None)
|
2023-03-13 22:53:44 +01:00
|
|
|
default.setdefault('production_lead_times', None)
|
2013-03-29 00:01:53 +01:00
|
|
|
return super(Product, cls).copy(products, default=default)
|
|
|
|
|
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2016-09-13 14:07:35 +02:00
|
|
|
class ProductBom(sequence_ordered(), ModelSQL, ModelView):
|
2012-05-13 23:00:54 +02:00
|
|
|
'Product - BOM'
|
2012-09-24 22:12:50 +02:00
|
|
|
__name__ = 'product.product-production.bom'
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2022-10-11 00:44:49 +02:00
|
|
|
product = fields.Many2One(
|
|
|
|
|
'product.product', "Product", ondelete='CASCADE', required=True,
|
2012-06-13 23:39:42 +02:00
|
|
|
domain=[
|
2017-01-13 20:20:10 +01:00
|
|
|
('producible', '=', True),
|
2012-06-13 23:39:42 +02:00
|
|
|
])
|
2022-10-11 00:44:49 +02:00
|
|
|
bom = fields.Many2One(
|
|
|
|
|
'production.bom', "BOM", ondelete='CASCADE', required=True,
|
|
|
|
|
domain=[
|
2012-05-13 23:00:54 +02:00
|
|
|
('output_products', '=', If(Bool(Eval('product')),
|
|
|
|
|
Eval('product', 0),
|
|
|
|
|
Get(Eval('_parent_product', {}), 'id', 0))),
|
2022-04-08 19:07:13 +02:00
|
|
|
])
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2012-09-24 22:12:50 +02:00
|
|
|
def get_rec_name(self, name):
|
|
|
|
|
return self.bom.rec_name
|
2012-05-13 23:00:54 +02:00
|
|
|
|
2012-09-24 22:12:50 +02:00
|
|
|
@classmethod
|
|
|
|
|
def search_rec_name(cls, name, clause):
|
2012-08-29 11:20:35 +02:00
|
|
|
return [('bom.rec_name',) + tuple(clause[1:])]
|
2015-06-14 18:37:04 +02:00
|
|
|
|
2016-06-24 09:22:52 +02:00
|
|
|
|
2016-09-13 14:07:35 +02:00
|
|
|
class ProductionLeadTime(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
|
2016-06-24 09:22:52 +02:00
|
|
|
'Production Lead Time'
|
|
|
|
|
__name__ = 'production.lead_time'
|
|
|
|
|
|
2022-10-11 00:44:49 +02:00
|
|
|
product = fields.Many2One(
|
|
|
|
|
'product.product', "Product", ondelete='CASCADE', required=True,
|
2016-06-24 09:22:52 +02:00
|
|
|
domain=[
|
2021-04-03 23:30:21 +02:00
|
|
|
('producible', '=', True),
|
2016-06-24 09:22:52 +02:00
|
|
|
])
|
|
|
|
|
bom = fields.Many2One('production.bom', 'BOM', ondelete='CASCADE',
|
|
|
|
|
domain=[
|
|
|
|
|
('output_products', '=', If(Bool(Eval('product')),
|
|
|
|
|
Eval('product', -1),
|
|
|
|
|
Get(Eval('_parent_product', {}), 'id', 0))),
|
2022-04-08 19:07:13 +02:00
|
|
|
])
|
2022-11-24 19:16:51 +01:00
|
|
|
lead_time = fields.TimeDelta(
|
|
|
|
|
"Lead Time",
|
|
|
|
|
domain=['OR',
|
|
|
|
|
('lead_time', '=', None),
|
|
|
|
|
('lead_time', '>=', TimeDelta()),
|
|
|
|
|
])
|
2016-06-24 09:22:52 +02:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def __setup__(cls):
|
|
|
|
|
super(ProductionLeadTime, cls).__setup__()
|
|
|
|
|
cls._order.insert(0, ('product', 'ASC'))
|