mirror of
https://github.com/Kalenis/kalenislims.git
synced 2023-12-14 07:13:04 +01:00
381 lines
14 KiB
Python
381 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
# This file is part of lims_production module for 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.wizard import Wizard, StateAction
|
|
from trytond.pool import PoolMeta, Pool
|
|
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not
|
|
from trytond.transaction import Transaction
|
|
from trytond.modules.product import price_digits
|
|
|
|
|
|
class Template(metaclass=PoolMeta):
|
|
__name__ = 'product.template'
|
|
|
|
common_name = fields.Char('Common name')
|
|
chemical_name = fields.Char('Chemical name')
|
|
commercial_name = fields.Char('Commercial name')
|
|
cas_number = fields.Char('CAS number')
|
|
commercial_brand = fields.Many2One('lims.brand', 'Commercial Brand')
|
|
purity_degree = fields.Many2One('lims.purity.degree', 'Purity Degree')
|
|
family_equivalent = fields.Many2One('lims.family.equivalent',
|
|
'Family/Equivalent',
|
|
domain=[('uom.category', '=', Eval('default_uom_category'))],
|
|
depends=['default_uom_category'],
|
|
help='The UoM\'s Category of Family/Equivalent which you can '
|
|
'select here will match the UoM\'s Category of this Product.')
|
|
controlled = fields.Boolean('Controlled')
|
|
reference_material = fields.Boolean('Reference Material')
|
|
certified = fields.Boolean('Certified')
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
Product = Pool().get('product.product')
|
|
products = Product.search(['OR',
|
|
[('code',) + tuple(clause[1:])],
|
|
[('barcode',) + tuple(clause[1:])],
|
|
], order=[])
|
|
if products:
|
|
return [('id', 'in', list(map(int, [product.template.id
|
|
for product in products])))]
|
|
return super().search_rec_name(name, clause)
|
|
|
|
|
|
class Product(metaclass=PoolMeta):
|
|
__name__ = 'product.product'
|
|
|
|
catalog = fields.Char('Catalog', depends=['active'],
|
|
states={'readonly': ~Eval('active', True)})
|
|
barcode = fields.Char('Bar Code', depends=['active'],
|
|
states={'readonly': ~Eval('active', True)})
|
|
common_name = fields.Function(fields.Char('Common name'),
|
|
'get_template_field', searcher='search_template_field')
|
|
chemical_name = fields.Function(fields.Char('Chemical name'),
|
|
'get_template_field', searcher='search_template_field')
|
|
commercial_name = fields.Function(fields.Char('Commercial name'),
|
|
'get_template_field', searcher='search_template_field')
|
|
cas_number = fields.Function(fields.Char('CAS number'),
|
|
'get_template_field', searcher='search_template_field')
|
|
commercial_brand = fields.Function(fields.Many2One('lims.brand',
|
|
'Commercial Brand'), 'get_template_field',
|
|
searcher='search_template_field')
|
|
purity_degree = fields.Function(fields.Many2One('lims.purity.degree',
|
|
'Purity Degree'), 'get_template_field',
|
|
searcher='search_template_field')
|
|
family_equivalent = fields.Function(fields.Many2One(
|
|
'lims.family.equivalent', 'Family/Equivalent'), 'get_template_field',
|
|
searcher='search_template_field')
|
|
controlled = fields.Function(fields.Boolean('Controlled'),
|
|
'get_template_field', searcher='search_template_field')
|
|
reference_material = fields.Function(fields.Boolean('Reference Material'),
|
|
'get_template_field', searcher='search_template_field')
|
|
certified = fields.Function(fields.Boolean('Certified'),
|
|
'get_template_field', searcher='search_template_field')
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
res = super().search_rec_name(name, clause)
|
|
return ['OR',
|
|
res,
|
|
[('barcode', ) + tuple(clause[1:])]
|
|
]
|
|
|
|
@classmethod
|
|
def get_template_field(cls, products, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name in ('commercial_brand', 'purity_degree',
|
|
'family_equivalent'):
|
|
for p in products:
|
|
field = getattr(p.template, name, None)
|
|
result[name][p.id] = field.id if field else None
|
|
else:
|
|
for p in products:
|
|
result[name][p.id] = getattr(p.template, name, None)
|
|
return result
|
|
|
|
@classmethod
|
|
def search_template_field(cls, name, clause):
|
|
return [('template.' + name,) + tuple(clause[1:])]
|
|
|
|
|
|
class LotCategory(ModelSQL, ModelView):
|
|
"Lot Category"
|
|
__name__ = "stock.lot.category"
|
|
_rec_name = 'name'
|
|
|
|
name = fields.Char('Name', required=True)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._order.insert(0, ('name', 'ASC'))
|
|
|
|
|
|
class Lot(metaclass=PoolMeta):
|
|
__name__ = 'stock.lot'
|
|
|
|
category = fields.Many2One('stock.lot.category', 'Category')
|
|
special_category = fields.Function(fields.Char('Category'),
|
|
'on_change_with_special_category')
|
|
stability = fields.Char('Stability', depends=['special_category'],
|
|
states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
})
|
|
homogeneity = fields.Char('Homogeneity', depends=['special_category'],
|
|
states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
})
|
|
concentration = fields.Char('Concentration',
|
|
depends=['special_category'], states={
|
|
'invisible': ~Eval('special_category').in_(
|
|
['input_prod', 'domestic_use']),
|
|
})
|
|
reception_date = fields.Date('Reception date',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
})
|
|
preparation_date = fields.Date('Preparation date',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'domestic_use'))),
|
|
})
|
|
common_name = fields.Function(fields.Char('Common name',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_common_name')
|
|
chemical_name = fields.Function(fields.Char('Chemical name',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_chemical_name')
|
|
commercial_name = fields.Function(fields.Char('Commercial name',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_commercial_name')
|
|
cas_number = fields.Function(fields.Char('CAS number',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_cas_number', searcher='search_cas_number')
|
|
commercial_brand = fields.Function(fields.Many2One('lims.brand',
|
|
'Commercial Brand', depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_commercial_brand')
|
|
catalog = fields.Function(fields.Char('Catalog',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_catalog')
|
|
purity_degree = fields.Function(fields.Many2One('lims.purity.degree',
|
|
'Purity Degree', depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_purity_degree')
|
|
solvent = fields.Many2One('product.product', 'Solvent',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'domestic_use'))),
|
|
})
|
|
technician = fields.Many2One('lims.laboratory.professional', 'Technician',
|
|
depends=['special_category'], states={
|
|
'invisible': ~Eval('special_category').in_(
|
|
['domestic_use', 'prod_sale']),
|
|
})
|
|
account_category = fields.Function(fields.Many2One('product.category',
|
|
'Account Category', depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod'))),
|
|
}), 'get_account_category', searcher='search_account_category')
|
|
exclusive_glp = fields.Boolean('Exclusive use GLP',
|
|
depends=['special_category'], states={
|
|
'invisible': Not(Bool(Equal(Eval('special_category'),
|
|
'input_prod')))})
|
|
production = fields.Many2One('production', 'Production origin',
|
|
readonly=True)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls.expiration_date.states['invisible'] = False
|
|
|
|
@staticmethod
|
|
def default_exclusive_glp():
|
|
return False
|
|
|
|
@fields.depends('category', 'product', '_parent_product.purchasable',
|
|
'_parent_product.salable')
|
|
def on_change_with_special_category(self, name=None):
|
|
Config = Pool().get('lims.configuration')
|
|
if self.category:
|
|
config = Config(1)
|
|
if self.category == config.lot_category_input_prod:
|
|
return 'input_prod'
|
|
elif self.category == config.lot_category_prod_sale:
|
|
return 'prod_sale'
|
|
elif self.category == config.lot_category_prod_domestic_use:
|
|
return 'domestic_use'
|
|
elif self.product:
|
|
if (self.product.purchasable and not self.product.salable):
|
|
return 'input_prod'
|
|
elif (not self.product.purchasable and self.product.salable):
|
|
return 'prod_sale'
|
|
elif (not self.product.purchasable and not self.product.salable):
|
|
return 'domestic_use'
|
|
else:
|
|
return ''
|
|
|
|
def get_common_name(self, name=None):
|
|
if self.product:
|
|
return self.product.common_name
|
|
return ''
|
|
|
|
def get_chemical_name(self, name=None):
|
|
if self.product:
|
|
return self.product.chemical_name
|
|
return ''
|
|
|
|
def get_commercial_name(self, name=None):
|
|
if self.product:
|
|
return self.product.commercial_name
|
|
return ''
|
|
|
|
def get_cas_number(self, name=None):
|
|
if self.product:
|
|
return self.product.cas_number
|
|
return ''
|
|
|
|
@classmethod
|
|
def search_cas_number(cls, name, clause):
|
|
return [('product.cas_number',) + tuple(clause[1:])]
|
|
|
|
def get_commercial_brand(self, name=None):
|
|
if self.product and self.product.commercial_brand:
|
|
return self.product.commercial_brand.id
|
|
return None
|
|
|
|
def get_catalog(self, name=None):
|
|
if self.product:
|
|
return self.product.catalog
|
|
return ''
|
|
|
|
def get_purity_degree(self, name=None):
|
|
if self.product and self.product.purity_degree:
|
|
return self.product.purity_degree.id
|
|
return None
|
|
|
|
def get_account_category(self, name=None):
|
|
if self.product:
|
|
return self.product.account_category.id
|
|
return None
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
pool = Pool()
|
|
Product = pool.get('product.product')
|
|
Config = pool.get('lims.configuration')
|
|
|
|
config = Config(1)
|
|
vlist = [x.copy() for x in vlist]
|
|
for values in vlist:
|
|
if not values.get('category'):
|
|
product = Product(values['product'])
|
|
lot_category_id = None
|
|
if (hasattr(product, 'purchasable') and
|
|
product.purchasable and not product.salable):
|
|
lot_category_id = (config.lot_category_input_prod.id
|
|
if config.lot_category_input_prod else None)
|
|
elif (not product.purchasable and product.salable):
|
|
lot_category_id = (config.lot_category_prod_sale.id
|
|
if config.lot_category_prod_sale else None)
|
|
elif (not product.purchasable and not product.salable):
|
|
lot_category_id = (
|
|
config.lot_category_prod_domestic_use.id if
|
|
config.lot_category_prod_domestic_use else None)
|
|
if lot_category_id:
|
|
values['category'] = lot_category_id
|
|
return super().create(vlist)
|
|
|
|
@classmethod
|
|
def search_account_category(cls, name, clause):
|
|
return [('product.' + name,) + tuple(clause[1:])]
|
|
|
|
|
|
class Move(metaclass=PoolMeta):
|
|
__name__ = 'stock.move'
|
|
|
|
label_quantity = fields.Float("Label Quantity",
|
|
digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
|
|
origin_purchase_unit_price = fields.Numeric('Unit Price',
|
|
digits=price_digits)
|
|
origin_purchase_currency = fields.Many2One('currency.currency',
|
|
'Currency')
|
|
|
|
@fields.depends('quantity')
|
|
def on_change_quantity(self):
|
|
if self.quantity:
|
|
self.label_quantity = self.quantity
|
|
|
|
@classmethod
|
|
def _get_origin(cls):
|
|
models = super()._get_origin()
|
|
models.append('production')
|
|
return models
|
|
|
|
|
|
class ShipmentIn(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.in'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls.inventory_moves.states['readonly'] = Eval('state').in_(
|
|
['draft', 'cancel'])
|
|
|
|
def _get_inventory_move(self, incoming_move):
|
|
move = super()._get_inventory_move(incoming_move)
|
|
if not move:
|
|
return None
|
|
|
|
if not incoming_move.origin:
|
|
return None
|
|
|
|
move.label_quantity = move.quantity
|
|
move.origin_purchase_currency = \
|
|
incoming_move.origin.purchase.currency.id
|
|
move.origin_purchase_unit_price = incoming_move.origin.unit_price
|
|
return move
|
|
|
|
|
|
class MoveProductionRelated(Wizard):
|
|
'Related Productions'
|
|
__name__ = 'lims.move.production_related'
|
|
|
|
start = StateAction('lims_production.act_production_related')
|
|
|
|
def do_start(self, action):
|
|
Move = Pool().get('stock.move')
|
|
|
|
move = Move(Transaction().context['active_id'])
|
|
|
|
production_id = None
|
|
if move.production_input:
|
|
production_id = move.production_input.id
|
|
elif move.production_output:
|
|
production_id = move.production_output.id
|
|
|
|
action['pyson_domain'] = PYSONEncoder().encode([
|
|
('id', '=', production_id),
|
|
])
|
|
|
|
return action, {}
|