kalenislims/lims_analysis_sheet_stock/sheet.py

338 lines
11 KiB
Python

# This file is part of lims_analysis_sheet_stock 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, Unique
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.pyson import Eval, Id
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.modules.lims_interface.interface import FUNCTIONS
from .function import custom_functions
FUNCTIONS.update(custom_functions)
class TemplateAnalysisSheet(metaclass=PoolMeta):
__name__ = 'lims.template.analysis_sheet'
materials = fields.One2Many('lims.template.analysis_sheet.material',
'template', 'Materials')
class TemplateAnalysisSheetMaterial(ModelSQL, ModelView):
'Template Material'
__name__ = 'lims.template.analysis_sheet.material'
template = fields.Many2One('lims.template.analysis_sheet', 'Template',
required=True, ondelete='CASCADE', select=True)
product = fields.Many2One('product.product', 'Product',
required=True, domain=[
('type', '!=', 'service'),
])
uom_category = fields.Function(fields.Many2One(
'product.uom.category', 'Uom Category'), 'on_change_with_uom_category')
uom = fields.Many2One('product.uom', 'Uom', required=True,
domain=[
('category', '=', Eval('uom_category')),
], depends=['uom_category'])
unit_digits = fields.Function(fields.Integer('Unit Digits'),
'on_change_with_unit_digits')
quantity = fields.Float('Quantity', required=True,
domain=['OR',
('quantity', '>=', 0),
('quantity', '=', None),
],
digits=(16, Eval('unit_digits', 2)),
depends=['unit_digits'])
quantity_by_sample = fields.Boolean('Quantity by Sample')
interface = fields.Function(fields.Many2One(
'lims.interface', 'Device Interface'), 'get_interface')
@classmethod
def __setup__(cls):
super().__setup__()
t = cls.__table__()
cls._sql_constraints = [
('product_template_uniq', Unique(t, t.product, t.template),
'lims_analysis_sheet_stock.msg_product_template_unique'),
]
@fields.depends('product', 'uom')
def on_change_product(self):
if self.product:
category = self.product.default_uom.category
if not self.uom or self.uom.category != category:
self.uom = self.product.default_uom
self.unit_digits = self.product.default_uom.digits
else:
self.uom = None
@fields.depends('product')
def on_change_with_uom_category(self, name=None):
if self.product:
return self.product.default_uom.category.id
@fields.depends('uom')
def on_change_with_unit_digits(self, name=None):
if self.uom:
return self.uom.digits
return 2
def get_rec_name(self, name):
return self.product.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('product.rec_name',) + tuple(clause[1:])]
def compute_quantity(self, factor):
return self.uom.ceil(self.quantity * factor)
def get_interface(self, name):
return self.template.interface.id
class AnalysisSheet(metaclass=PoolMeta):
__name__ = 'lims.analysis_sheet'
moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True)
@classmethod
@ModelView.button
def validate_(cls, sheets):
super().validate_(sheets)
cls.check_materials(sheets)
@classmethod
def check_materials(cls, sheets):
for s in sheets:
if s.template.materials and not s.moves:
raise UserError(gettext(
'lims_analysis_sheet_stock.msg_sheet_not_materials'))
class AddMaterialStart(ModelView):
'Add Material'
__name__ = 'lims.analysis_sheet.add_material.start'
materials = fields.One2Many(
'lims.analysis_sheet.add_material_detail.start',
'material', 'Materials')
class AddMaterialDetailStart(ModelView):
'Add Material'
__name__ = 'lims.analysis_sheet.add_material_detail.start'
material = fields.Many2One('lims.analysis_sheet.add_material.start',
'Material')
product = fields.Many2One('product.product', 'Product',
required=True, domain=[
('type', '!=', 'service'),
])
lot = fields.Many2One('stock.lot', 'Lot',
domain=[
('product', '=', Eval('product')),
],
depends=['product'])
from_location = fields.Many2One('stock.location', 'From Location',
domain=[('type', '=', 'storage')])
uom_category = fields.Function(fields.Many2One(
'product.uom.category', 'Uom Category'), 'on_change_with_uom_category')
uom = fields.Many2One('product.uom', 'Uom', required=True,
domain=[
('category', '=', Eval('uom_category')),
], depends=['uom_category'])
unit_digits = fields.Function(fields.Integer('Unit Digits'),
'on_change_with_unit_digits')
quantity = fields.Float('Quantity', required=True,
domain=['OR',
('quantity', '>=', 0),
('quantity', '=', None),
],
digits=(16, Eval('unit_digits', 2)),
depends=['unit_digits'])
@fields.depends('product', 'uom')
def on_change_product(self):
if self.product:
category = self.product.default_uom.category
if not self.uom or self.uom.category != category:
self.uom = self.product.default_uom
self.unit_digits = self.product.default_uom.digits
else:
self.uom = None
@fields.depends('product')
def on_change_with_uom_category(self, name=None):
if self.product:
return self.product.default_uom.category.id
@fields.depends('uom')
def on_change_with_unit_digits(self, name=None):
if self.uom:
return self.uom.digits
return 2
class AddMaterialAssignFailed(ModelView):
'Add Material Assign Failed'
__name__ = 'lims.analysis_sheet.add_material.assign.failed'
inventory_moves = fields.Many2Many('stock.move', None, None,
'Inventory Moves', readonly=True)
@staticmethod
def default_inventory_moves():
AnalysisSheet = Pool().get('lims.analysis_sheet')
sheet = None
line_id = Transaction().context.get('active_id', None)
sheet_id = Transaction().context.get('lims_analysis_sheet', None)
if line_id and sheet_id:
sheet = AnalysisSheet(sheet_id)
if not sheet:
return []
return [x.id for x in sheet.moves if x.state == 'draft']
class AddMaterial(Wizard):
'Add Material'
__name__ = 'lims.analysis_sheet.add_material'
start_state = 'check'
check = StateTransition()
start = StateView('lims.analysis_sheet.add_material.start',
'lims_analysis_sheet_stock.add_material_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Save', 'save', 'tryton-save', default=True),
])
save = StateTransition()
failed = StateView('lims.analysis_sheet.add_material.assign.failed',
'lims_analysis_sheet_stock.add_material_assign_failed_view_form', [
Button('Force Assign', 'force', 'tryton-forward',
states={
'invisible': ~Id('stock',
'group_stock_force_assignment').in_(
Eval('context', {}).get('groups', [])),
}),
Button('OK', 'delete_moves', 'tryton-ok', True),
])
force = StateTransition()
delete_moves = StateTransition()
def _get_analysis_sheet_id(self):
return Transaction().context.get('lims_analysis_sheet', None)
def transition_check(self):
AnalysisSheet = Pool().get('lims.analysis_sheet')
line_id = Transaction().context.get('active_id', None)
sheet_id = self._get_analysis_sheet_id()
if line_id and sheet_id:
sheet = AnalysisSheet(sheet_id)
if sheet.state == 'active':
return 'start'
return 'end'
def default_start(self, fields):
pool = Pool()
AnalysisSheet = pool.get('lims.analysis_sheet')
sheet_id = self._get_analysis_sheet_id()
sheet = AnalysisSheet(sheet_id)
moves = []
for material in sheet.template.materials:
move = {
'product': material.product.id,
'uom': material.uom.id,
'quantity': material.quantity,
}
moves.append(move)
defaults = {
'materials': moves,
}
return defaults
def _move(self, origin, from_location, to_location, product, uom, quantity,
lot):
Move = Pool().get('stock.move')
move = Move(
origin=origin,
product=product,
lot=lot,
uom=uom,
quantity=quantity,
from_location=from_location,
to_location=to_location,
state='draft',
)
return move
def get_moves(self):
pool = Pool()
AnalysisSheet = pool.get('lims.analysis_sheet')
Move = pool.get('stock.move')
Config = Pool().get('lims.configuration')
config = Config(1)
sheet_id = self._get_analysis_sheet_id()
sheet = AnalysisSheet(sheet_id)
moves = []
for material in self.start.materials:
move = self._move(sheet, material.from_location,
config.materials_consumption_location, material.product,
material.uom, material.quantity, material.lot)
moves.append(move)
Move.save(moves)
return moves
def transition_save(self):
pool = Pool()
Move = pool.get('stock.move')
moves = self.get_moves()
if not Move.assign_try(moves):
return 'failed'
Move.do(moves)
return 'end'
def transition_force(self):
pool = Pool()
AnalysisSheet = pool.get('lims.analysis_sheet')
Move = pool.get('stock.move')
sheet = None
line_id = Transaction().context.get('active_id', None)
sheet_id = Transaction().context.get('lims_analysis_sheet', None)
if line_id and sheet_id:
sheet = AnalysisSheet(sheet_id)
Move.do([m for m in sheet.moves])
return 'end'
def transition_delete_moves(self):
pool = Pool()
AnalysisSheet = pool.get('lims.analysis_sheet')
Move = pool.get('stock.move')
sheet = None
line_id = Transaction().context.get('active_id', None)
sheet_id = Transaction().context.get('lims_analysis_sheet', None)
if line_id and sheet_id:
sheet = AnalysisSheet(sheet_id)
Move.delete([m for m in sheet.moves])
return 'end'