kalenislims/lims/configuration.py
Sebastián Marró fe8d687c82 Remove 2to3
2019-03-04 11:41:58 -03:00

586 lines
20 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of lims 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 ModelSingleton, ModelView, ModelSQL, fields
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta
from trytond.modules.company.model import (
CompanyMultiValueMixin, CompanyValueMixin)
__all__ = ['NotebookView', 'NotebookViewColumn', 'UserRole', 'UserRoleGroup',
'Printer', 'User', 'UserLaboratory', 'Configuration',
'ConfigurationLaboratory', 'ConfigurationSequence',
'ConfigurationProductCategory', 'LabWorkYear', 'LabWorkYearSequence',
'ModelDoc', 'Model']
sequence_names = [
'entry_sequence', 'sample_sequence', 'service_sequence',
'results_report_sequence']
class NotebookView(ModelSQL, ModelView):
'Laboratory Notebook View'
__name__ = 'lims.notebook.view'
name = fields.Char('Name', required=True)
columns = fields.One2Many('lims.notebook.view.column', 'view', 'Columns',
required=True)
class NotebookViewColumn(ModelSQL, ModelView):
'Laboratory Notebook View Column'
__name__ = 'lims.notebook.view.column'
view = fields.Many2One('lims.notebook.view', 'View', required=True,
ondelete='CASCADE', select=True)
field = fields.Many2One('ir.model.field', 'Field', required=True,
domain=[('model.model', '=', 'lims.notebook.line')])
sequence = fields.Integer('Sequence', required=True, select=True)
@classmethod
def __setup__(cls):
super(NotebookViewColumn, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
class UserRole(ModelSQL, ModelView):
'User Role'
__name__ = 'lims.user.role'
name = fields.Char('Name', required=True)
groups = fields.Many2Many('lims.user.role-res.group',
'role', 'group', 'Groups')
class UserRoleGroup(ModelSQL):
'User Role - Group'
__name__ = 'lims.user.role-res.group'
role = fields.Many2One('lims.user.role', 'Role',
ondelete='CASCADE', select=True, required=True)
group = fields.Many2One('res.group', 'Group',
ondelete='CASCADE', select=True, required=True)
@classmethod
def create(cls, vlist):
role_groups = super(UserRoleGroup, cls).create(vlist)
cls._create_user_groups(role_groups)
return role_groups
@classmethod
def _create_user_groups(cls, role_groups):
pool = Pool()
User = pool.get('res.user')
UserGroup = pool.get('res.user-res.group')
for role_group in role_groups:
users = User.search([
('role', '=', role_group.role),
])
for user in users:
if not UserGroup.search([
('user', '=', user),
('group', '=', role_group.group),
]):
UserGroup.create([{
'user': user,
'group': role_group.group,
}])
@classmethod
def delete(cls, role_groups):
cls._delete_user_groups(role_groups)
super(UserRoleGroup, cls).delete(role_groups)
@classmethod
def _delete_user_groups(cls, role_groups):
pool = Pool()
User = pool.get('res.user')
UserGroup = pool.get('res.user-res.group')
for role_group in role_groups:
users = User.search([
('role', '=', role_group.role),
])
if users:
user_groups = UserGroup.search([
('user', 'in', users),
('group', '=', role_group.group),
])
if user_groups:
UserGroup.delete(user_groups)
class Printer(ModelSQL, ModelView):
'Printer'
__name__ = 'lims.printer'
name = fields.Char('Name', required=True)
class User(metaclass=PoolMeta):
__name__ = 'res.user'
notebook_view = fields.Many2One('lims.notebook.view', 'Notebook view')
role = fields.Many2One('lims.user.role', 'Role')
laboratories = fields.Many2Many('lims.user-laboratory',
'user', 'laboratory', 'Laboratories')
laboratory = fields.Many2One('lims.laboratory', 'Main Laboratory',
domain=[('id', 'in', Eval('laboratories'))], depends=['laboratories'])
printer = fields.Many2One('lims.printer', 'Printer')
departments = fields.One2Many('user.department', 'user', 'Departments')
@classmethod
def __setup__(cls):
super(User, cls).__setup__()
cls._context_fields.insert(0, 'laboratory')
cls._context_fields.insert(0, 'laboratories')
@classmethod
def create(cls, vlist):
users = super(User, cls).create(vlist)
cls._create_user_groups(users)
return users
@classmethod
def _create_user_groups(cls, users):
pool = Pool()
RoleGroup = pool.get('lims.user.role-res.group')
UserGroup = pool.get('res.user-res.group')
for user in users:
if user.role:
role_groups = RoleGroup.search([
('role', '=', user.role),
])
for role_group in role_groups:
if not UserGroup.search([
('user', '=', user),
('group', '=', role_group.group),
]):
UserGroup.create([{
'user': user,
'group': role_group.group,
}])
@classmethod
def write(cls, *args):
actions = iter(args)
for users, vals in zip(actions, actions):
if 'role' in vals:
cls._update_user_groups(users, vals['role'])
super(User, cls).write(*args)
@classmethod
def _update_user_groups(cls, users, new_role=None):
pool = Pool()
RoleGroup = pool.get('lims.user.role-res.group')
UserGroup = pool.get('res.user-res.group')
for user in users:
# delete old groups
if user.role:
role_groups = RoleGroup.search([
('role', '=', user.role),
])
for role_group in role_groups:
user_groups = UserGroup.search([
('user', '=', user),
('group', '=', role_group.group),
])
if user_groups:
UserGroup.delete(user_groups)
# create new groups
if new_role:
role_groups = RoleGroup.search([
('role', '=', new_role),
])
for role_group in role_groups:
if not UserGroup.search([
('user', '=', user),
('group', '=', role_group.group),
]):
UserGroup.create([{
'user': user,
'group': role_group.group,
}])
def get_status_bar(self, name):
status = self.name
if self.company:
status = '%s - %s' % (self.company.rec_name, status)
if self.laboratory:
status += ' [%s]' % self.laboratory.rec_name
return status
class UserLaboratory(ModelSQL):
'User - Laboratory'
__name__ = 'lims.user-laboratory'
user = fields.Many2One('res.user', 'User',
ondelete='CASCADE', select=True, required=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
ondelete='CASCADE', select=True, required=True)
class Configuration(ModelSingleton, ModelSQL, ModelView,
CompanyMultiValueMixin):
'Configuration'
__name__ = 'lims.configuration'
fraction_product = fields.Many2One('product.product', 'Fraction product',
states={'required': True})
mail_ack_subject = fields.Char('Email subject of Acknowledgment of Samples'
' Receipt',
help="In the text will be added suffix with the entry report number")
mail_ack_body = fields.Text('Email body of Acknowledgment of Samples'
' Receipt')
microbiology_laboratories = fields.Many2Many(
'lims.configuration-laboratory', 'configuration',
'laboratory', 'Microbiology Laboratories')
default_notebook_view = fields.Many2One('lims.notebook.view',
'Default Notebook view', required=True)
brix_digits = fields.Integer('Brix digits')
density_digits = fields.Integer('Density digits')
soluble_solids_digits = fields.Integer('Soluble solids digits')
rm_start_uom = fields.Many2One('product.uom', 'RM Start UoM',
domain=[('category.lims_only_available', '=', True)])
email_qa = fields.Char('QA Email')
analysis_product_category = fields.Many2One('product.category',
'Analysis Product Category', states={'required': True})
entry_confirm_background = fields.Boolean(
'Confirm Entries in Background')
planification_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', 'Planification Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'lims.planification'),
]))
mcl_fraction_type = fields.Many2One('lims.fraction.type',
'MCL fraction type')
con_fraction_type = fields.Many2One('lims.fraction.type',
'Control fraction type')
bmz_fraction_type = fields.Many2One('lims.fraction.type',
'BMZ fraction type')
rm_fraction_type = fields.Many2One('lims.fraction.type',
'RM fraction type')
bre_fraction_type = fields.Many2One('lims.fraction.type',
'BRE fraction type')
mrt_fraction_type = fields.Many2One('lims.fraction.type',
'MRT fraction type')
coi_fraction_type = fields.Many2One('lims.fraction.type',
'COI fraction type')
mrc_fraction_type = fields.Many2One('lims.fraction.type',
'MRC fraction type')
sla_fraction_type = fields.Many2One('lims.fraction.type',
'SLA fraction type')
itc_fraction_type = fields.Many2One('lims.fraction.type',
'ITC fraction type')
itl_fraction_type = fields.Many2One('lims.fraction.type',
'ITL fraction type')
reagents = fields.Many2Many('lims.configuration-product.category',
'configuration', 'category', 'Reagents')
planification_process_background = fields.Boolean(
'Process Planifications in Background')
invoice_party_relation_type = fields.Many2One('party.relation.type',
'Invoice Party Relation Type')
@staticmethod
def default_brix_digits():
return 2
@staticmethod
def default_density_digits():
return 2
@staticmethod
def default_soluble_solids_digits():
return 2
@staticmethod
def default_entry_confirm_background():
return False
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field == 'planification_sequence':
return pool.get('lims.configuration.sequence')
return super(Configuration, cls).multivalue_model(field)
@classmethod
def default_planification_sequence(cls, **pattern):
return cls.multivalue_model(
'planification_sequence').default_planification_sequence()
@staticmethod
def default_planification_process_background():
return False
def get_reagents(self):
res = []
if self.reagents:
for r in self.reagents:
res.append(r.id)
res.extend(self.get_reagent_childs(r.id))
return res
def get_reagent_childs(self, reagent_id):
Category = Pool().get('product.category')
res = []
categories = Category.search([
('parent', '=', reagent_id),
])
if categories:
for c in categories:
res.append(c.id)
res.extend(self.get_reagent_childs(c.id))
return res
class ConfigurationLaboratory(ModelSQL):
'Configuration - Laboratory'
__name__ = 'lims.configuration-laboratory'
configuration = fields.Many2One('lims.configuration', 'Configuration',
ondelete='CASCADE', select=True, required=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
ondelete='CASCADE', select=True, required=True)
class ConfigurationSequence(ModelSQL, CompanyValueMixin):
'Configuration Sequence'
__name__ = 'lims.configuration.sequence'
planification_sequence = fields.Many2One('ir.sequence',
'Planification Sequence', depends=['company'], domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'lims.planification'),
])
@classmethod
def default_planification_sequence(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('lims.planification', 'seq_planification')
except KeyError:
return None
class ConfigurationProductCategory(ModelSQL):
'Configuration - Product Category'
__name__ = 'lims.configuration-product.category'
configuration = fields.Many2One('lims.configuration', 'Configuration',
ondelete='CASCADE', select=True, required=True)
category = fields.Many2One('product.category', 'Category',
ondelete='CASCADE', select=True, required=True)
class LabWorkYear(ModelSQL, ModelView, CompanyMultiValueMixin):
'Work Year'
__name__ = 'lims.lab.workyear'
_rec_name = 'code'
code = fields.Char('Code', required=True)
start_date = fields.Date('Start date', required=True)
end_date = fields.Date('End date', required=True)
entry_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', 'Entry Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'lims.entry'),
]))
sample_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', 'Sample Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'lims.sample'),
]))
service_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', 'Service Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'lims.service'),
]))
results_report_sequence = fields.MultiValue(fields.Many2One(
'ir.sequence', 'Results Report Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'lims.results_report'),
]))
sequences = fields.One2Many('lims.lab.workyear.sequence',
'workyear', 'Sequences')
default_entry_control = fields.Many2One('lims.entry',
'Default entry control')
@classmethod
def __setup__(cls):
super(LabWorkYear, cls).__setup__()
cls._order.insert(0, ('start_date', 'ASC'))
cls._error_messages.update({
'workyear_overlaps': ('Work year "%(first)s" and '
'"%(second)s" overlap.'),
'no_workyear_date': 'No work year defined for "%s".',
})
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field in sequence_names:
return pool.get('lims.lab.workyear.sequence')
return super(LabWorkYear, cls).multivalue_model(field)
@classmethod
def default_entry_sequence(cls, **pattern):
return cls.multivalue_model(
'entry_sequence').default_entry_sequence()
@classmethod
def default_sample_sequence(cls, **pattern):
return cls.multivalue_model(
'sample_sequence').default_sample_sequence()
@classmethod
def default_service_sequence(cls, **pattern):
return cls.multivalue_model(
'service_sequence').default_service_sequence()
@classmethod
def validate(cls, years):
super(LabWorkYear, cls).validate(years)
for year in years:
year.check_dates()
def check_dates(self):
cursor = Transaction().connection.cursor()
table = self.__table__()
cursor.execute(*table.select(table.id,
where=(((table.start_date <= self.start_date) &
(table.end_date >= self.start_date)) |
((table.start_date <= self.end_date) &
(table.end_date >= self.end_date)) |
((table.start_date >= self.start_date) &
(table.end_date <= self.end_date))) &
(table.id != self.id)))
second_id = cursor.fetchone()
if second_id:
second = self.__class__(second_id[0])
self.raise_user_error('workyear_overlaps', {
'first': self.rec_name,
'second': second.rec_name,
})
@classmethod
def find(cls, date=None, exception=True):
pool = Pool()
Lang = pool.get('ir.lang')
Date = pool.get('ir.date')
if not date:
date = Date.today()
workyears = cls.search([
('start_date', '<=', date),
('end_date', '>=', date),
], order=[('start_date', 'DESC')], limit=1)
if not workyears:
if exception:
lang = Lang.get()
formatted = lang.strftime(date)
cls.raise_user_error('no_workyear_date', (formatted,))
else:
return None
return workyears[0].id
def get_sequence(self, type):
sequence = getattr(self, type + '_sequence')
if sequence:
return sequence
class LabWorkYearSequence(ModelSQL, CompanyValueMixin):
'Work Year Sequence'
__name__ = 'lims.lab.workyear.sequence'
workyear = fields.Many2One('lims.lab.workyear', 'Work Year',
ondelete='CASCADE', select=True)
entry_sequence = fields.Many2One('ir.sequence',
'Entry Sequence', depends=['company'], domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'lims.entry'),
])
sample_sequence = fields.Many2One('ir.sequence',
'Sample Sequence', depends=['company'], domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'lims.sample'),
])
service_sequence = fields.Many2One('ir.sequence',
'Service Sequence', depends=['company'], domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'lims.service'),
])
results_report_sequence = fields.Many2One('ir.sequence',
'Results Report Sequence', depends=['company'], domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'lims.results_report'),
])
@classmethod
def default_entry_sequence(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('lims.entry', 'seq_entry')
except KeyError:
return None
@classmethod
def default_sample_sequence(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('lims.sample', 'seq_sample')
except KeyError:
return None
@classmethod
def default_service_sequence(cls):
pool = Pool()
ModelData = pool.get('ir.model.data')
try:
return ModelData.get_id('lims.service', 'seq_service')
except KeyError:
return None
class ModelDoc(ModelSQL, ModelView):
'Model Doc'
__name__ = 'ir.model.doc'
model = fields.Many2One('ir.model', 'Model')
doc = fields.Text('Documentation', translate=True)
kind = fields.Selection([
('base', 'Base'),
('extended', 'Extended'),
], 'Kind')
name = fields.Function(fields.Char('Name'), 'get_name')
def get_name(self, name):
return self.model.name
class Model(metaclass=PoolMeta):
__name__ = 'ir.model'
docs = fields.One2Many('ir.model.doc', 'model', 'Docs')