6651 lines
266 KiB
Python
6651 lines
266 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.
|
|
import logging
|
|
from datetime import datetime, date
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from trytond.model import Workflow, ModelView, ModelSQL, fields, Unique
|
|
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
|
|
Button
|
|
from trytond.report import Report
|
|
from trytond.pool import Pool
|
|
from trytond.transaction import Transaction
|
|
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, Or
|
|
from trytond.exceptions import UserError
|
|
from trytond.i18n import gettext
|
|
from .results_report import get_print_date
|
|
|
|
__all__ = ['Planification', 'PlanificationTechnician',
|
|
'PlanificationTechnicianDetail', 'PlanificationDetail',
|
|
'PlanificationServiceDetail', 'NotebookLineFraction',
|
|
'PlanificationServiceDetailLaboratoryProfessional',
|
|
'PlanificationAnalysis', 'PlanificationFraction', 'FractionReagent',
|
|
'LabProfessionalMethod', 'LabProfessionalMethodRequalification',
|
|
'LabProfessionalMethodRequalificationSupervisor',
|
|
'LabProfessionalMethodRequalificationControl', 'BlindSample',
|
|
'RelateTechniciansStart', 'RelateTechniciansResult',
|
|
'RelateTechniciansDetail1', 'RelateTechniciansDetail2',
|
|
'RelateTechniciansDetail3', 'RelateTechnicians', 'UnlinkTechniciansStart',
|
|
'UnlinkTechniciansDetail1', 'UnlinkTechnicians', 'AddFractionControlStart',
|
|
'AddFractionControl', 'AddFractionRMBMZStart', 'AddFractionRMBMZ',
|
|
'AddFractionBREStart', 'AddFractionBRE', 'AddFractionMRTStart',
|
|
'AddFractionMRT', 'RemoveControlStart', 'RemoveControl',
|
|
'AddAnalysisStart', 'AddAnalysis', 'SearchFractionsNext',
|
|
'SearchFractionsDetail', 'SearchFractions', 'SearchPlannedFractionsStart',
|
|
'SearchPlannedFractionsNext', 'SearchPlannedFractions',
|
|
'CreateFractionControlStart', 'CreateFractionControl',
|
|
'ReleaseFractionStart', 'ReleaseFractionEmpty', 'ReleaseFractionResult',
|
|
'ReleaseFraction', 'QualificationSituations', 'QualificationSituation',
|
|
'QualificationAction', 'QualificationSituation2',
|
|
'QualificationSituation3', 'QualificationSituation4',
|
|
'TechniciansQualification', 'ReplaceTechnicianStart', 'ReplaceTechnician',
|
|
'LoadServices', 'PlanificationSequenceReport',
|
|
'PlanificationWorksheetAnalysisReport',
|
|
'PlanificationWorksheetMethodReport', 'PlanificationWorksheetReport',
|
|
'PendingServicesUnplannedReport', 'PendingServicesUnplannedSpreadsheet',
|
|
'PrintBlindSampleReportStart', 'PrintBlindSampleReport',
|
|
'BlindSampleReport', 'PrintPendingServicesUnplannedReportStart',
|
|
'PrintPendingServicesUnplannedReport',
|
|
'PlanificationSequenceAnalysisReport']
|
|
|
|
|
|
class Planification(Workflow, ModelSQL, ModelView):
|
|
'Planification'
|
|
__name__ = 'lims.planification'
|
|
_rec_name = 'code'
|
|
|
|
code = fields.Char('Code', select=True, readonly=True)
|
|
date = fields.Date('Date', readonly=True)
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
|
|
required=True)
|
|
analysis = fields.Many2Many('lims.planification-analysis',
|
|
'planification', 'analysis', 'Analysis/Sets/Groups',
|
|
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))},
|
|
context={'date_from': Eval('date_from'), 'date_to': Eval('date_to'),
|
|
'calculate': Bool(Equal(Eval('state'), 'draft'))},
|
|
domain=['OR', ('id', 'in', Eval('analysis')), [
|
|
('id', 'in', Eval('analysis_domain'))]],
|
|
depends=['state', 'date_from', 'date_to', 'analysis_domain'])
|
|
analysis_domain = fields.Function(fields.Many2Many('lims.analysis',
|
|
None, None, 'Analysis domain'),
|
|
'on_change_with_analysis_domain')
|
|
technicians = fields.One2Many('lims.planification.technician',
|
|
'planification', 'Technicians', depends=['method_domain',
|
|
'technicians_domain'])
|
|
date_from = fields.Date('Date from', depends=['state'], required=True,
|
|
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))})
|
|
date_to = fields.Date('Date to', depends=['state'], required=True,
|
|
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))})
|
|
start_date = fields.Date('Start date', depends=['state'],
|
|
states={'readonly': Bool(Equal(Eval('state'), 'confirmed'))})
|
|
details = fields.One2Many('lims.planification.detail',
|
|
'planification', 'Fractions to plan',
|
|
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))},
|
|
depends=['state'])
|
|
controls = fields.Many2Many('lims.planification-fraction',
|
|
'planification', 'fraction', 'Controls', readonly=True)
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('preplanned', 'Pre-Planned'),
|
|
('confirmed', 'Confirmed'),
|
|
('not_executed', 'Not executed'),
|
|
], 'State', required=True, readonly=True)
|
|
waiting_process = fields.Boolean('Waiting process')
|
|
method_domain = fields.Function(fields.One2Many('lims.lab.method',
|
|
None, 'Method domain'),
|
|
'on_change_with_method_domain', setter='set_method_domain')
|
|
technicians_domain = fields.Function(fields.One2Many(
|
|
'lims.laboratory.professional', None, 'Technicians domain'),
|
|
'on_change_with_technicians_domain', setter='set_technicians_domain')
|
|
comments = fields.Text('Comments')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Planification, cls).__setup__()
|
|
cls._order.insert(0, ('code', 'DESC'))
|
|
cls._transitions |= set((
|
|
('draft', 'preplanned'),
|
|
('preplanned', 'confirmed'),
|
|
('confirmed', 'not_executed'),
|
|
))
|
|
cls._buttons.update({
|
|
'add_analysis': {
|
|
'readonly': (Eval('state') != 'draft'),
|
|
},
|
|
'search_fractions': {
|
|
'readonly': (Eval('state') != 'draft'),
|
|
},
|
|
'search_planned_fractions': {
|
|
'readonly': (Eval('state') != 'draft'),
|
|
},
|
|
'preplan': {
|
|
'invisible': (Eval('state') != 'draft'),
|
|
},
|
|
'confirm': {
|
|
'invisible': (Eval('state') != 'preplanned'),
|
|
},
|
|
'release_controls': {
|
|
'invisible': (Eval('state') != 'confirmed'),
|
|
},
|
|
'relate_technicians': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'unlink_technicians': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'replace_technician': {
|
|
'readonly': (Eval('state') != 'confirmed'),
|
|
},
|
|
'add_fraction_con': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'add_fraction_rm_bmz': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'add_fraction_bre': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'add_fraction_mrt': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
'remove_control': {
|
|
'readonly': (Eval('state') != 'preplanned'),
|
|
},
|
|
})
|
|
|
|
@staticmethod
|
|
def default_state():
|
|
return 'draft'
|
|
|
|
@staticmethod
|
|
def default_waiting_process():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_date():
|
|
Date = Pool().get('ir.date')
|
|
return Date.today()
|
|
|
|
@staticmethod
|
|
def default_laboratory():
|
|
return Transaction().context.get('laboratory', None)
|
|
|
|
@fields.depends('laboratory', 'state',
|
|
methods=['_get_analysis_domain'])
|
|
def on_change_with_analysis_domain(self, name=None):
|
|
if not self.laboratory or self.state != 'draft':
|
|
return []
|
|
return self._get_analysis_domain(self.laboratory)
|
|
|
|
@staticmethod
|
|
def _get_analysis_domain(laboratory):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
if not laboratory:
|
|
return []
|
|
|
|
cursor.execute('SELECT al.analysis '
|
|
'FROM "' + AnalysisLaboratory._table + '" al '
|
|
'INNER JOIN "' + Analysis._table + '" a '
|
|
'ON a.id = al.analysis '
|
|
'WHERE al.laboratory = %s '
|
|
'AND a.behavior != \'internal_relation\'',
|
|
(laboratory.id,))
|
|
analysis_sets_list = [a[0] for a in cursor.fetchall()]
|
|
|
|
groups_list = []
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + Analysis._table + '" '
|
|
'WHERE type = \'group\'')
|
|
groups_list_ids = [g[0] for g in cursor.fetchall()]
|
|
for group_id in groups_list_ids:
|
|
if Planification._get_group_available(group_id,
|
|
analysis_sets_list):
|
|
groups_list.append(group_id)
|
|
|
|
return analysis_sets_list + groups_list
|
|
|
|
@staticmethod
|
|
def _get_group_available(group_id, analysis_sets_list):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
AnalysisIncluded = pool.get('lims.analysis.included')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
cursor.execute('SELECT ia.included_analysis, a.type '
|
|
'FROM "' + AnalysisIncluded._table + '" ia '
|
|
'INNER JOIN "' + Analysis._table + '" a '
|
|
'ON a.id = ia.included_analysis '
|
|
'WHERE ia.analysis = %s '
|
|
'AND a.behavior != \'internal_relation\'',
|
|
(group_id,))
|
|
included_analysis = cursor.fetchall()
|
|
if not included_analysis:
|
|
return False
|
|
for analysis in included_analysis:
|
|
if (analysis[1] != 'group' and analysis[0] not in
|
|
analysis_sets_list):
|
|
return False
|
|
if (analysis[1] == 'group' and not
|
|
Planification._get_group_available(analysis[0],
|
|
analysis_sets_list)):
|
|
return False
|
|
return True
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
Sequence = pool.get('ir.sequence')
|
|
|
|
vlist = [x.copy() for x in vlist]
|
|
config = Config(1)
|
|
for values in vlist:
|
|
values['code'] = Sequence.get_id(
|
|
config.planification_sequence.id)
|
|
return super(Planification, cls).create(vlist)
|
|
|
|
@classmethod
|
|
def check_delete(cls, planifications):
|
|
for planification in planifications:
|
|
if planification.state not in ['draft', 'preplanned']:
|
|
raise UserError(gettext('lims.msg_delete_planification',
|
|
planification=planification.rec_name))
|
|
|
|
@classmethod
|
|
def delete(cls, planifications):
|
|
cls.check_delete(planifications)
|
|
super(Planification, cls).delete(planifications)
|
|
|
|
@classmethod
|
|
def copy(cls, planifications, default=None):
|
|
raise UserError(gettext('lims.msg_copy_planification'))
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_add_analysis')
|
|
def add_analysis(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_search_fractions')
|
|
def search_fractions(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_search_planned_fractions')
|
|
def search_planned_fractions(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('preplanned')
|
|
def preplan(cls, planifications):
|
|
for planification in planifications:
|
|
planification.check_start_date()
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_technicians_qualification')
|
|
def confirm(cls, planifications):
|
|
for planification in planifications:
|
|
planification.check_start_date()
|
|
planification.check_technicians()
|
|
|
|
def check_start_date(self):
|
|
if not self.start_date:
|
|
raise UserError(gettext('lims.msg_not_start_date'))
|
|
for detail in self.details:
|
|
if detail.fraction.sample.date2 > self.start_date:
|
|
raise UserError(gettext('lims.msg_invalid_start_date',
|
|
date=detail.fraction.sample.date2))
|
|
|
|
def check_technicians(self):
|
|
fractions = {}
|
|
for detail in self.details:
|
|
for service_detail in detail.details:
|
|
if not service_detail.staff_responsible:
|
|
key = (detail.fraction.id,
|
|
service_detail.notebook_line.method.id)
|
|
if key not in fractions:
|
|
fractions[key] = '%s (%s)' % (detail.fraction.rec_name,
|
|
service_detail.notebook_line.method.code)
|
|
if fractions:
|
|
sorted_fractions = sorted(
|
|
list(fractions.values()), key=lambda x: x)
|
|
raise UserError(gettext('lims.msg_no_technician',
|
|
fractions='\n' + '\n'.join(sorted_fractions) + '\n'))
|
|
|
|
@classmethod
|
|
def process_waiting_planifications(cls):
|
|
'''
|
|
Cron - Process Waiting Planifications
|
|
'''
|
|
logger = logging.getLogger('lims_planification')
|
|
|
|
planifications = cls.search([
|
|
('waiting_process', '=', True),
|
|
], order=[('id', 'ASC')])
|
|
if planifications:
|
|
logger.info('Cron - Processing planifications:INIT')
|
|
for planification in planifications:
|
|
if planification.state == 'confirmed':
|
|
cls.do_confirm([planification])
|
|
elif planification.state == 'not_executed':
|
|
cls.do_release_controls([planification])
|
|
logger.info('Cron - Processing planifications:END')
|
|
|
|
@classmethod
|
|
def do_confirm(cls, planifications):
|
|
for planification in planifications:
|
|
planification.update_laboratory_notebook()
|
|
planification.update_analysis_detail()
|
|
if planification.waiting_process:
|
|
planification.waiting_process = False
|
|
planification.save()
|
|
|
|
def update_laboratory_notebook(self):
|
|
pool = Pool()
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
notebook_lines = []
|
|
service_details = PlanificationServiceDetail.search([
|
|
('detail.planification', '=', self.id),
|
|
('notebook_line', '!=', None),
|
|
])
|
|
for service_detail in service_details:
|
|
notebook_line = NotebookLine(
|
|
service_detail.notebook_line.id)
|
|
notebook_line.start_date = self.start_date
|
|
notebook_line.laboratory_professionals = [p.id
|
|
for p in service_detail.staff_responsible]
|
|
notebook_line.planification = self.id
|
|
if not service_detail.is_control:
|
|
notebook_line.controls = [f.id
|
|
for f in self.controls]
|
|
notebook_lines.append(notebook_line)
|
|
if notebook_lines:
|
|
NotebookLine.save(notebook_lines)
|
|
|
|
def update_analysis_detail(self):
|
|
pool = Pool()
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
analysis_detail_ids = []
|
|
service_details = PlanificationServiceDetail.search([
|
|
('detail.planification', '=', self.id),
|
|
('notebook_line.analysis_detail', '!=', None),
|
|
])
|
|
for service_detail in service_details:
|
|
analysis_detail_ids.append(
|
|
service_detail.notebook_line.analysis_detail.id)
|
|
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', analysis_detail_ids),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'planned',
|
|
})
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('not_executed')
|
|
def release_controls(cls, planifications):
|
|
Config = Pool().get('lims.configuration')
|
|
process_background = Config(1).planification_process_background
|
|
|
|
for planification in planifications:
|
|
# Check if is still waiting for confirmation
|
|
if planification.waiting_process:
|
|
raise UserError(gettext('lims.msg_waiting_process',
|
|
planification=planification.code))
|
|
if process_background:
|
|
planification.waiting_process = True
|
|
planification.save()
|
|
if not process_background:
|
|
cls.do_release_controls(planifications)
|
|
|
|
@classmethod
|
|
def do_release_controls(cls, planifications):
|
|
for planification in planifications:
|
|
planification.re_update_laboratory_notebook()
|
|
planification.re_update_analysis_detail()
|
|
planification.unlink_controls()
|
|
if planification.waiting_process:
|
|
planification.waiting_process = False
|
|
planification.save()
|
|
|
|
def re_update_laboratory_notebook(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
for detail in self.details:
|
|
for service_detail in detail.details:
|
|
if service_detail.is_control and service_detail.notebook_line:
|
|
notebook_line = NotebookLine(
|
|
service_detail.notebook_line.id)
|
|
notebook_line.start_date = None
|
|
notebook_line.laboratory_professionals = []
|
|
notebook_line.planification = None
|
|
notebook_line.controls = []
|
|
notebook_line.save()
|
|
|
|
def re_update_analysis_detail(self):
|
|
EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
|
|
analysis_detail_ids = []
|
|
for detail in self.details:
|
|
for service_detail in detail.details:
|
|
if (service_detail.is_control and
|
|
service_detail.notebook_line and
|
|
service_detail.notebook_line.analysis_detail):
|
|
analysis_detail_ids.append(
|
|
service_detail.notebook_line.analysis_detail.id)
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', analysis_detail_ids),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'unplanned',
|
|
})
|
|
|
|
def unlink_controls(self):
|
|
pool = Pool()
|
|
PlanificationFraction = pool.get('lims.planification-fraction')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
controls = PlanificationFraction.search([
|
|
('planification', '=', self.id),
|
|
])
|
|
if controls:
|
|
PlanificationFraction.delete(controls)
|
|
|
|
controls_details = PlanificationDetail.search([
|
|
('planification', '=', self.id),
|
|
('details.is_control', '=', True),
|
|
])
|
|
if controls_details:
|
|
PlanificationDetail.delete(controls_details)
|
|
|
|
@fields.depends('analysis')
|
|
def on_change_with_method_domain(self, name=None):
|
|
methods = []
|
|
if self.analysis:
|
|
for a in self.analysis:
|
|
if a.methods:
|
|
methods.extend([m.id for m in a.methods])
|
|
return methods
|
|
|
|
@classmethod
|
|
def set_method_domain(cls, records, name, value):
|
|
return
|
|
|
|
@fields.depends('laboratory')
|
|
def on_change_with_technicians_domain(self, name=None):
|
|
pool = Pool()
|
|
UserLaboratory = pool.get('lims.user-laboratory')
|
|
LaboratoryProfessional = pool.get('lims.laboratory.professional')
|
|
|
|
if not self.laboratory:
|
|
return []
|
|
users = UserLaboratory.search([
|
|
('laboratory', '=', self.laboratory.id),
|
|
])
|
|
if not users:
|
|
return []
|
|
professionals = LaboratoryProfessional.search([
|
|
('party.lims_user', 'in', [u.user.id for u in users]),
|
|
])
|
|
if not professionals:
|
|
return []
|
|
return [p.id for p in professionals]
|
|
|
|
@classmethod
|
|
def set_technicians_domain(cls, records, name, value):
|
|
return
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_relate_technicians')
|
|
def relate_technicians(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_unlink_technicians')
|
|
def unlink_technicians(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_replace_technician')
|
|
def replace_technician(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_add_fraction_con')
|
|
def add_fraction_con(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_add_fraction_rm_bmz')
|
|
def add_fraction_rm_bmz(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_add_fraction_bre')
|
|
def add_fraction_bre(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_add_fraction_mrt')
|
|
def add_fraction_mrt(cls, planifications):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button_action('lims.wiz_lims_remove_control')
|
|
def remove_control(cls, planifications):
|
|
pass
|
|
|
|
|
|
class PlanificationTechnician(ModelSQL, ModelView):
|
|
'Technician'
|
|
__name__ = 'lims.planification.technician'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
laboratory_professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', required=True, domain=[
|
|
('id', 'in', Eval('_parent_planification',
|
|
{}).get('technicians_domain')),
|
|
])
|
|
details = fields.Function(fields.One2Many(
|
|
'lims.planification.technician.detail', 'technician',
|
|
'Fractions to plan', readonly=True),
|
|
'get_details', setter='set_details')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(PlanificationTechnician, cls).__setup__()
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('planification_professional_uniq',
|
|
Unique(t, t.planification, t.laboratory_professional),
|
|
'lims.msg_planification_professional_unique_id'),
|
|
]
|
|
|
|
def get_details(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Fraction = pool.get('lims.fraction')
|
|
LabMethod = pool.get('lims.lab.method')
|
|
|
|
cursor.execute('SELECT DISTINCT(f.number, nl.analysis_origin, '
|
|
'm.code||\' - \'||m.name) '
|
|
'FROM "' + PlanificationDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
|
|
'ON sd.id = sdp.detail '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d '
|
|
'ON d.id = sd.detail '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON nl.id = sd.notebook_line '
|
|
'INNER JOIN "' + LabMethod._table + '" m '
|
|
'ON m.id = nl.method '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = d.fraction '
|
|
'WHERE sdp.professional = %s '
|
|
'AND d.planification = %s',
|
|
(self.laboratory_professional.id, self.planification.id))
|
|
|
|
fractions = []
|
|
for d in cursor.fetchall():
|
|
r = d[0].split(',')
|
|
fractions.append({
|
|
'fraction': str(r[0][1:]),
|
|
'analysis_origin': str(r[1]).replace('"', ''),
|
|
'method': str(','.join(r[2:])[:-1].replace('"', '')),
|
|
})
|
|
return fractions
|
|
|
|
@classmethod
|
|
def set_details(cls, records, name, value):
|
|
return
|
|
|
|
|
|
class PlanificationTechnicianDetail(ModelView):
|
|
'Technician Detail'
|
|
__name__ = 'lims.planification.technician.detail'
|
|
|
|
fraction = fields.Char('Fraction')
|
|
analysis_origin = fields.Char('Analysis origin')
|
|
method = fields.Char('Method')
|
|
|
|
|
|
class PlanificationDetail(ModelSQL, ModelView):
|
|
'Fraction to Plan'
|
|
__name__ = 'lims.planification.detail'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', required=True)
|
|
service_analysis = fields.Many2One('lims.analysis', 'Service',
|
|
required=True)
|
|
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
product_type = fields.Function(fields.Many2One('lims.product.type',
|
|
'Product type'), 'get_fraction_field')
|
|
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
|
|
'get_fraction_field')
|
|
details = fields.One2Many('lims.planification.service_detail',
|
|
'detail', 'Planification detail', states={'readonly': True})
|
|
urgent = fields.Function(fields.Boolean('Urgent'), 'get_service_field',
|
|
setter='set_urgent')
|
|
priority = fields.Function(fields.Integer('Priority'), 'get_service_field')
|
|
report_date = fields.Function(fields.Date('Date agreed for result'),
|
|
'get_service_field')
|
|
comments = fields.Function(fields.Text('Comments'), 'get_fraction_field')
|
|
icon = fields.Function(fields.Char("Icon"), 'get_icon')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(PlanificationDetail, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('service_analysis', 'ASC'))
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if (name == 'label' or name == 'comments'):
|
|
for d in details:
|
|
result[name][d.id] = getattr(d.fraction, name, None)
|
|
elif name == 'fraction_type':
|
|
for d in details:
|
|
field = getattr(d.fraction, 'type', None)
|
|
result[name][d.id] = field.id if field else None
|
|
else:
|
|
for d in details:
|
|
field = getattr(d.fraction, name, None)
|
|
result[name][d.id] = field.id if field else None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_fraction_field(cls, name, clause):
|
|
if name == 'fraction_type':
|
|
name = 'type'
|
|
return [('fraction.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_service_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'urgent':
|
|
for d in details:
|
|
result[name][d.id] = False
|
|
elif name == 'priority':
|
|
for d in details:
|
|
result[name][d.id] = 0
|
|
else:
|
|
for d in details:
|
|
result[name][d.id] = None
|
|
for d in details:
|
|
if d.fraction and d.service_analysis:
|
|
for service in d.fraction.services:
|
|
if service.analysis == d.service_analysis:
|
|
for name in names:
|
|
result[name][d.id] = getattr(service, name)
|
|
return result
|
|
|
|
@classmethod
|
|
def set_urgent(cls, details, name, value):
|
|
Service = Pool().get('lims.service')
|
|
for d in details:
|
|
if d.fraction and d.service_analysis:
|
|
for service in d.fraction.services:
|
|
if service.analysis == d.service_analysis:
|
|
Service.write([service], {'urgent': value})
|
|
|
|
def get_icon(self, name):
|
|
if self.comments:
|
|
return 'lims-blue'
|
|
return 'lims-white'
|
|
|
|
|
|
class PlanificationServiceDetail(ModelSQL, ModelView):
|
|
'Planification Detail'
|
|
__name__ = 'lims.planification.service_detail'
|
|
|
|
detail = fields.Many2One('lims.planification.detail', 'Planification',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
planification = fields.Function(fields.Many2One('lims.planification',
|
|
'Planification'), 'get_planification', searcher='search_planification')
|
|
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook line',
|
|
required=True, select=True)
|
|
staff_responsible = fields.Many2Many(
|
|
'lims.planification.service_detail-laboratory.professional', 'detail',
|
|
'professional', 'Laboratory professionals')
|
|
is_control = fields.Boolean('Is Control')
|
|
is_replanned = fields.Boolean('Is Replanned')
|
|
planned_service = fields.Many2One('lims.analysis', 'Planned service')
|
|
repetition = fields.Function(fields.Integer('Repetition'),
|
|
'get_repetition')
|
|
|
|
@staticmethod
|
|
def default_is_control():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_is_replanned():
|
|
return False
|
|
|
|
def get_planification(self, name=None):
|
|
if self.detail:
|
|
if self.detail.planification:
|
|
return self.detail.planification.id
|
|
return None
|
|
|
|
@classmethod
|
|
def search_planification(cls, name, clause):
|
|
return [('detail.' + name,) + tuple(clause[1:])]
|
|
|
|
def get_repetition(self, name=None):
|
|
if self.notebook_line:
|
|
return self.notebook_line.repetition
|
|
return None
|
|
|
|
|
|
class PlanificationServiceDetailLaboratoryProfessional(ModelSQL):
|
|
'Planification Detail - Laboratory Professional'
|
|
__name__ = 'lims.planification.service_detail-laboratory.professional'
|
|
_table = 'lims_plan_service_d-laboratory_professional'
|
|
|
|
detail = fields.Many2One('lims.planification.service_detail',
|
|
'Planification detail', ondelete='CASCADE', select=True,
|
|
required=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', ondelete='CASCADE', select=True,
|
|
required=True)
|
|
|
|
|
|
class PlanificationAnalysis(ModelSQL):
|
|
'Planification - Analysis'
|
|
__name__ = 'lims.planification-analysis'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
|
|
|
|
class PlanificationFraction(ModelSQL):
|
|
'Planification - Fraction'
|
|
__name__ = 'lims.planification-fraction'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
|
|
|
|
class NotebookLineFraction(ModelSQL):
|
|
'Laboratory Notebook Line - Fraction'
|
|
__name__ = 'lims.notebook.line-fraction'
|
|
|
|
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
|
|
|
|
class FractionReagent(ModelSQL, ModelView):
|
|
'Fraction Reagent'
|
|
__name__ = 'lims.fraction.reagent'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', required=True,
|
|
ondelete='CASCADE', select=True)
|
|
product = fields.Many2One('product.product', 'Reagent', required=True,
|
|
domain=[('account_category', 'in', Eval('reagent_domain'))],
|
|
depends=['reagent_domain'])
|
|
reagent_domain = fields.Function(fields.Many2Many('product.category',
|
|
None, None, 'Reagent domain'), 'get_reagent_domain')
|
|
lot = fields.Many2One('stock.lot', 'Lot',
|
|
domain=[('product', '=', Eval('product'))],
|
|
depends=['product'])
|
|
quantity = fields.Function(fields.Float('Current quantity'),
|
|
'on_change_with_quantity')
|
|
default_uom = fields.Function(fields.Many2One('product.uom',
|
|
'Default UOM'),
|
|
'on_change_with_default_uom')
|
|
|
|
@staticmethod
|
|
def default_reagent_domain():
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
return config.get_reagents()
|
|
|
|
def get_reagent_domain(self, name=None):
|
|
return self.default_reagent_domain()
|
|
|
|
@fields.depends('product', '_parent_product.quantity')
|
|
def on_change_with_quantity(self, name=None):
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Product = pool.get('product.product')
|
|
Date = pool.get('ir.date')
|
|
|
|
if not self.product:
|
|
return 0
|
|
locations = Location.search([('type', '=', 'storage')])
|
|
with Transaction().set_context(locations=[l.id for l in locations],
|
|
stock_date_end=Date.today()):
|
|
product = Product(self.product.id)
|
|
return product.quantity or 0
|
|
|
|
@fields.depends('product', '_parent_product.default_uom')
|
|
def on_change_with_default_uom(self, name=None):
|
|
if self.product:
|
|
return self.product.default_uom.id
|
|
|
|
|
|
class LabProfessionalMethod(ModelSQL, ModelView):
|
|
'Laboratory Professional Method'
|
|
__name__ = 'lims.lab.professional.method'
|
|
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional', required=True)
|
|
method = fields.Many2One('lims.lab.method', 'Method', required=True)
|
|
state = fields.Selection([
|
|
('training', 'Training'),
|
|
('qualified', 'Qualified'),
|
|
('requalified', 'Requalified'),
|
|
], 'State', sort=False)
|
|
type = fields.Selection([
|
|
('preparation', 'Preparation'),
|
|
('analytical', 'Analytical'),
|
|
], 'Type', sort=False)
|
|
requalification_history = fields.One2Many(
|
|
'lims.lab.professional.method.requalification', 'professional_method',
|
|
'Trainings/Qualifications/Requalifications')
|
|
determination = fields.Function(fields.Char('Determination'),
|
|
'get_determination', searcher='search_determination')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(LabProfessionalMethod, cls).__setup__()
|
|
cls._order.insert(0, ('professional', 'ASC'))
|
|
cls._order.insert(1, ('method', 'ASC'))
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('professional_method_type_uniq',
|
|
Unique(t, t.professional, t.method, t.type),
|
|
'lims.msg_professional_method_unique_id'),
|
|
]
|
|
|
|
def get_determination(self, name=None):
|
|
if self.method:
|
|
return self.method.determination
|
|
return None
|
|
|
|
@classmethod
|
|
def search_determination(cls, name, clause):
|
|
return [('method.' + name,) + tuple(clause[1:])]
|
|
|
|
|
|
class LabProfessionalMethodRequalification(ModelSQL, ModelView):
|
|
'Laboratory Professional Method Requalification'
|
|
__name__ = 'lims.lab.professional.method.requalification'
|
|
_table = 'lims_lab_pro_method_req'
|
|
|
|
professional_method = fields.Many2One('lims.lab.professional.method',
|
|
'Professional Method', ondelete='CASCADE', select=True, required=True)
|
|
type = fields.Selection([
|
|
('training', 'Training'),
|
|
('qualification', 'Qualification'),
|
|
('requalification', 'Requalification'),
|
|
], 'Type', sort=False)
|
|
date = fields.Date('Date', required=True)
|
|
last_execution_date = fields.Date('Last execution Date')
|
|
supervisors = fields.One2Many(
|
|
'lims.lab.professional.method.requalification.supervisor',
|
|
'method_requalification', 'Supervisors')
|
|
controls = fields.One2Many(
|
|
'lims.lab.professional.method.requalification.control',
|
|
'method_requalification', 'Controls')
|
|
|
|
|
|
class LabProfessionalMethodRequalificationSupervisor(ModelSQL, ModelView):
|
|
'Laboratory Professional Method Requalification Supervisor'
|
|
__name__ = 'lims.lab.professional.method.requalification.supervisor'
|
|
_table = 'lims_lab_pro_method_req_supervisor'
|
|
|
|
method_requalification = fields.Many2One(
|
|
'lims.lab.professional.method.requalification',
|
|
'Professional method requalification', ondelete='CASCADE',
|
|
select=True, required=True)
|
|
supervisor = fields.Many2One('lims.laboratory.professional', 'Supervisor',
|
|
required=True)
|
|
|
|
|
|
class LabProfessionalMethodRequalificationControl(ModelSQL, ModelView):
|
|
'Laboratory Professional Method Requalification Control'
|
|
__name__ = 'lims.lab.professional.method.requalification.control'
|
|
_table = 'lims_lab_pro_method_req_control'
|
|
|
|
method_requalification = fields.Many2One(
|
|
'lims.lab.professional.method.requalification',
|
|
'Professional method requalification', ondelete='CASCADE',
|
|
select=True, required=True)
|
|
control = fields.Many2One('lims.fraction', 'Control',
|
|
required=True)
|
|
|
|
|
|
class BlindSample(ModelSQL, ModelView):
|
|
'Blind Sample'
|
|
__name__ = 'lims.blind_sample'
|
|
|
|
line = fields.Many2One('lims.notebook.line', 'Line', required=True,
|
|
readonly=True, ondelete='CASCADE', select=True)
|
|
entry = fields.Many2One('lims.entry', 'Entry', readonly=True)
|
|
sample = fields.Many2One('lims.sample', 'Sample', readonly=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
|
|
service = fields.Many2One('lims.service', 'Service', readonly=True)
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
date = fields.Date('Date', readonly=True)
|
|
original_line = fields.Many2One('lims.notebook.line', 'Original line')
|
|
original_sample = fields.Many2One('lims.sample', 'Original sample',
|
|
readonly=True)
|
|
original_fraction = fields.Many2One('lims.fraction', 'Original fraction',
|
|
readonly=True)
|
|
original_repetition = fields.Integer('Repetition', readonly=True)
|
|
min_value = fields.Char('Minimum value', readonly=True)
|
|
max_value = fields.Char('Maximum value', readonly=True)
|
|
|
|
|
|
class RelateTechniciansStart(ModelView):
|
|
'Relate Technicians'
|
|
__name__ = 'lims.planification.relate_technicians.start'
|
|
|
|
exclude_relateds = fields.Boolean('Exclude fractions already related')
|
|
grouping = fields.Selection([
|
|
('none', 'None'),
|
|
('origin_method', 'Analysis origin and Method'),
|
|
('origin', 'Analysis origin'),
|
|
], 'Grouping', sort=False, required=True)
|
|
|
|
|
|
class RelateTechniciansResult(ModelView):
|
|
'Relate Technicians'
|
|
__name__ = 'lims.planification.relate_technicians.result'
|
|
|
|
technicians = fields.Many2Many('lims.laboratory.professional',
|
|
None, None, 'Technicians', required=True,
|
|
domain=[('id', 'in', Eval('technicians_domain'))],
|
|
depends=['technicians_domain'])
|
|
technicians_domain = fields.One2Many('lims.laboratory.professional',
|
|
None, 'Technicians domain')
|
|
grouping = fields.Selection([
|
|
('none', 'None'),
|
|
('origin_method', 'Analysis origin and Method'),
|
|
('origin', 'Analysis origin'),
|
|
], 'Grouping', sort=False, readonly=True)
|
|
details1 = fields.Many2Many(
|
|
'lims.planification.relate_technicians.detail1', None, None,
|
|
'Fractions to plan', domain=[('id', 'in', Eval('details1_domain'))],
|
|
states={'invisible': Not(Bool(Equal(Eval('grouping'), 'none')))},
|
|
depends=['details1_domain', 'grouping'])
|
|
details1_domain = fields.One2Many(
|
|
'lims.planification.relate_technicians.detail1', None,
|
|
'Fractions domain')
|
|
details2 = fields.Many2Many(
|
|
'lims.planification.relate_technicians.detail2', None, None,
|
|
'Fractions to plan', domain=[('id', 'in', Eval('details2_domain'))],
|
|
states={
|
|
'invisible': Not(Bool(Equal(Eval('grouping'), 'origin_method'))),
|
|
}, depends=['details2_domain', 'grouping'])
|
|
details2_domain = fields.One2Many(
|
|
'lims.planification.relate_technicians.detail2', None,
|
|
'Fractions domain')
|
|
details3 = fields.Many2Many(
|
|
'lims.planification.relate_technicians.detail3', None, None,
|
|
'Fractions to plan', domain=[('id', 'in', Eval('details3_domain'))],
|
|
states={'invisible': Not(Bool(Equal(Eval('grouping'), 'origin')))},
|
|
depends=['details3_domain', 'grouping'])
|
|
details3_domain = fields.One2Many(
|
|
'lims.planification.relate_technicians.detail3', None,
|
|
'Fractions domain')
|
|
|
|
@fields.depends('grouping')
|
|
def on_change_grouping(self):
|
|
self.details1 = []
|
|
self.details2 = []
|
|
self.details3 = []
|
|
|
|
|
|
class RelateTechniciansDetail1(ModelSQL, ModelView):
|
|
'Fraction Detail'
|
|
__name__ = 'lims.planification.relate_technicians.detail1'
|
|
_table = 'lims_planification_relate_technicians_d1'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction')
|
|
service_analysis = fields.Many2One('lims.analysis', 'Service')
|
|
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_fraction_field')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(RelateTechniciansDetail1,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(RelateTechniciansDetail1, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('service_analysis', 'ASC'))
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'label':
|
|
for d in details:
|
|
result[name][d.id] = getattr(d.fraction, name, None)
|
|
elif name == 'fraction_type':
|
|
for d in details:
|
|
field = getattr(d.fraction, 'type', None)
|
|
result[name][d.id] = field.id if field else None
|
|
else:
|
|
for d in details:
|
|
field = getattr(d.fraction, name, None)
|
|
result[name][d.id] = field.id if field else None
|
|
return result
|
|
|
|
|
|
class RelateTechniciansDetail2(ModelSQL, ModelView):
|
|
'Fraction Detail'
|
|
__name__ = 'lims.planification.relate_technicians.detail2'
|
|
_table = 'lims_planification_relate_technicians_d2'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction')
|
|
analysis_origin = fields.Char('Analysis origin')
|
|
method = fields.Many2One('lims.lab.method', 'Method')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(RelateTechniciansDetail2,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(RelateTechniciansDetail2, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('analysis_origin', 'ASC'))
|
|
cls._order.insert(2, ('method', 'ASC'))
|
|
|
|
|
|
class RelateTechniciansDetail3(ModelSQL, ModelView):
|
|
'Fraction Detail'
|
|
__name__ = 'lims.planification.relate_technicians.detail3'
|
|
_table = 'lims_planification_relate_technicians_d3'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction')
|
|
analysis_origin = fields.Char('Analysis origin')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(RelateTechniciansDetail3,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(RelateTechniciansDetail3, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('analysis_origin', 'ASC'))
|
|
|
|
|
|
class RelateTechnicians(Wizard):
|
|
'Relate Technicians'
|
|
__name__ = 'lims.planification.relate_technicians'
|
|
|
|
start = StateView('lims.planification.relate_technicians.start',
|
|
'lims.lims_relate_technicians_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-ok', default=True),
|
|
])
|
|
search = StateTransition()
|
|
result = StateView('lims.planification.relate_technicians.result',
|
|
'lims.lims_relate_technicians_result_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Relate', 'relate', 'tryton-ok', default=True),
|
|
])
|
|
relate = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
return {
|
|
'exclude_relateds': True,
|
|
'grouping': 'none',
|
|
}
|
|
|
|
def transition_search(self):
|
|
planification_id = Transaction().context['active_id']
|
|
|
|
self.result.grouping = self.start.grouping
|
|
self.result.details1_domain = []
|
|
self.result.details2_domain = []
|
|
self.result.details3_domain = []
|
|
|
|
if self.start.grouping == 'none':
|
|
self.result.details1_domain = self._view_details1(planification_id,
|
|
self.start.exclude_relateds)
|
|
elif self.start.grouping == 'origin_method':
|
|
self.result.details2_domain = self._view_details2(planification_id,
|
|
self.start.exclude_relateds)
|
|
elif self.start.grouping == 'origin':
|
|
self.result.details3_domain = self._view_details3(planification_id,
|
|
self.start.exclude_relateds)
|
|
return 'result'
|
|
|
|
def default_result(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
details1_domain = []
|
|
if self.result.details1_domain:
|
|
details1_domain = [d.id for d in self.result.details1_domain]
|
|
details2_domain = []
|
|
if self.result.details2_domain:
|
|
details2_domain = [d.id for d in self.result.details2_domain]
|
|
details3_domain = []
|
|
if self.result.details3_domain:
|
|
details3_domain = [d.id for d in self.result.details3_domain]
|
|
|
|
return {
|
|
'technicians_domain': [t.laboratory_professional.id
|
|
for t in planification.technicians],
|
|
'details1_domain': details1_domain,
|
|
'details2_domain': details2_domain,
|
|
'details3_domain': details3_domain,
|
|
'grouping': self.start.grouping,
|
|
}
|
|
|
|
def _view_details1(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
RelateTechniciansDetail1 = pool.get(
|
|
'lims.planification.relate_technicians.detail1')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details1 = {}
|
|
cursor.execute('SELECT d.fraction, d.service_analysis, sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
|
|
'ON sd.detail = d.id '
|
|
'WHERE d.planification = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id,))
|
|
for x in cursor.fetchall():
|
|
f, s = x[0], x[1]
|
|
if (f, s) not in details1:
|
|
details1[(f, s)] = {
|
|
'fraction': f,
|
|
'service_analysis': s,
|
|
}
|
|
|
|
to_create = []
|
|
for d in details1.values():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': d['fraction'],
|
|
'service_analysis': d['service_analysis'],
|
|
})
|
|
return RelateTechniciansDetail1.create(to_create)
|
|
|
|
def _view_details2(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
NotebookLine = pool.get(
|
|
'lims.notebook.line')
|
|
RelateTechniciansDetail2 = pool.get(
|
|
'lims.planification.relate_technicians.detail2')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details2 = {}
|
|
cursor.execute('SELECT d.fraction, nl.analysis_origin, nl.method, '
|
|
'sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
|
|
'ON sd.detail = d.id '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON sd.notebook_line = nl.id '
|
|
'WHERE d.planification = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id,))
|
|
for x in cursor.fetchall():
|
|
f, a, m = x[0], x[1], x[2]
|
|
if (f, a, m) not in details2:
|
|
details2[(f, a, m)] = {
|
|
'fraction': f,
|
|
'analysis_origin': a,
|
|
'method': m,
|
|
}
|
|
|
|
to_create = []
|
|
for d in details2.values():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': d['fraction'],
|
|
'analysis_origin': d['analysis_origin'],
|
|
'method': d['method'],
|
|
})
|
|
return RelateTechniciansDetail2.create(to_create)
|
|
|
|
def _view_details3(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
NotebookLine = pool.get(
|
|
'lims.notebook.line')
|
|
RelateTechniciansDetail3 = pool.get(
|
|
'lims.planification.relate_technicians.detail3')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details3 = {}
|
|
cursor.execute('SELECT d.fraction, nl.analysis_origin, sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
|
|
'ON sd.detail = d.id '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON sd.notebook_line = nl.id '
|
|
'WHERE d.planification = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id,))
|
|
for x in cursor.fetchall():
|
|
f, a = x[0], x[1]
|
|
if (f, a) not in details3:
|
|
details3[(f, a)] = {
|
|
'fraction': f,
|
|
'analysis_origin': a,
|
|
}
|
|
|
|
to_create = []
|
|
for d in details3.values():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': d['fraction'],
|
|
'analysis_origin': d['analysis_origin'],
|
|
})
|
|
return RelateTechniciansDetail3.create(to_create)
|
|
|
|
def transition_relate(self):
|
|
PlanificationServiceDetail = Pool().get(
|
|
'lims.planification.service_detail')
|
|
|
|
planification_id = Transaction().context['active_id']
|
|
details = self._get_details(planification_id)
|
|
if not details:
|
|
return 'end'
|
|
|
|
PlanificationServiceDetail.write(details, {
|
|
'staff_responsible': [('remove',
|
|
[t.id for t in self.result.technicians])],
|
|
})
|
|
PlanificationServiceDetail.write(details, {
|
|
'staff_responsible': [('add',
|
|
[t.id for t in self.result.technicians])],
|
|
})
|
|
return 'end'
|
|
|
|
def _get_details(self, planification_id):
|
|
details = []
|
|
if self.start.grouping == 'none':
|
|
details = self._get_details1(planification_id,
|
|
self.start.exclude_relateds)
|
|
elif self.start.grouping == 'origin_method':
|
|
details = self._get_details2(planification_id,
|
|
self.start.exclude_relateds)
|
|
elif self.start.grouping == 'origin':
|
|
details = self._get_details3(planification_id,
|
|
self.start.exclude_relateds)
|
|
return details
|
|
|
|
def _get_details1(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details = []
|
|
for detail in self.result.details1:
|
|
cursor.execute('SELECT sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s '
|
|
'AND d.fraction = %s '
|
|
'AND d.service_analysis = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id, detail.fraction.id,
|
|
detail.service_analysis.id))
|
|
for x in cursor.fetchall():
|
|
details.append(x[0])
|
|
|
|
return PlanificationServiceDetail.browse(details)
|
|
|
|
def _get_details2(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
NotebookLine = pool.get(
|
|
'lims.notebook.line')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details = []
|
|
for detail in self.result.details2:
|
|
cursor.execute('SELECT sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sd.detail = d.id '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON sd.notebook_line = nl.id '
|
|
'WHERE d.planification = %s '
|
|
'AND d.fraction = %s '
|
|
'AND nl.analysis_origin = %s '
|
|
'AND nl.method = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id, detail.fraction.id,
|
|
detail.analysis_origin, detail.method.id))
|
|
for x in cursor.fetchall():
|
|
details.append(x[0])
|
|
|
|
return PlanificationServiceDetail.browse(details)
|
|
|
|
def _get_details3(self, planification_id, exclude_relateds):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
ServiceDetailProfessional = pool.get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
NotebookLine = pool.get(
|
|
'lims.notebook.line')
|
|
|
|
exclude_relateds_clause = ''
|
|
if exclude_relateds:
|
|
exclude_relateds_clause = (' AND sd.id NOT IN ('
|
|
'SELECT sdp.detail '
|
|
'FROM "' + ServiceDetailProfessional._table + '" sdp '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sdp.detail = sd.id '
|
|
'INNER JOIN "' + PlanificationDetail._table + '" d'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s'
|
|
')' % planification_id)
|
|
|
|
details = []
|
|
for detail in self.result.details3:
|
|
cursor.execute('SELECT sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sd.detail = d.id '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON sd.notebook_line = nl.id '
|
|
'WHERE d.planification = %s '
|
|
'AND d.fraction = %s '
|
|
'AND nl.analysis_origin = %s' +
|
|
exclude_relateds_clause,
|
|
(planification_id, detail.fraction.id,
|
|
detail.analysis_origin))
|
|
for x in cursor.fetchall():
|
|
details.append(x[0])
|
|
|
|
return PlanificationServiceDetail.browse(details)
|
|
|
|
|
|
class UnlinkTechniciansStart(ModelView):
|
|
'Unlink Technicians Start'
|
|
__name__ = 'lims.planification.unlink_technicians.start'
|
|
|
|
technicians = fields.Many2Many('lims.laboratory.professional',
|
|
None, None, 'Technicians', required=True,
|
|
domain=[('id', 'in', Eval('technicians_domain'))],
|
|
depends=['technicians_domain'])
|
|
technicians_domain = fields.One2Many('lims.laboratory.professional',
|
|
None, 'Technicians domain')
|
|
details1 = fields.Many2Many(
|
|
'lims.planification.unlink_technicians.detail1', None, None,
|
|
'Assigned fractions', domain=[('id', 'in', Eval('details1_domain'))],
|
|
depends=['details1_domain'])
|
|
details1_domain = fields.Many2Many(
|
|
'lims.planification.unlink_technicians.detail1', None, None,
|
|
'Fractions domain')
|
|
|
|
|
|
class UnlinkTechniciansDetail1(ModelSQL, ModelView):
|
|
'Fraction Detail Unlink'
|
|
__name__ = 'lims.planification.unlink_technicians.detail1'
|
|
_table = 'lims_planification_unlink_technicians_d1'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction')
|
|
service_analysis = fields.Many2One('lims.analysis', 'Service')
|
|
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_fraction_field')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(UnlinkTechniciansDetail1,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(UnlinkTechniciansDetail1, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('service_analysis', 'ASC'))
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'label':
|
|
for d in details:
|
|
result[name][d.id] = getattr(d.fraction, name, None)
|
|
elif name == 'fraction_type':
|
|
for d in details:
|
|
field = getattr(d.fraction, 'type', None)
|
|
result[name][d.id] = field.id if field else None
|
|
else:
|
|
for d in details:
|
|
field = getattr(d.fraction, name, None)
|
|
result[name][d.id] = field.id if field else None
|
|
return result
|
|
|
|
|
|
class UnlinkTechnicians(Wizard):
|
|
'Unlink Technicians'
|
|
__name__ = 'lims.planification.unlink_technicians'
|
|
|
|
start = StateView('lims.planification.unlink_technicians.start',
|
|
'lims.lims_unlink_technicians_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Unlink', 'unlink', 'tryton-ok', default=True),
|
|
])
|
|
unlink = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
details_domain = self._view_details(planification.id)
|
|
|
|
return {
|
|
'technicians_domain': [t.laboratory_professional.id
|
|
for t in planification.technicians],
|
|
'details1_domain': [d.id for d in details_domain],
|
|
}
|
|
|
|
def _view_details(self, planification_id):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
UnlinkTechniciansDetail1 = pool.get(
|
|
'lims.planification.unlink_technicians.detail1')
|
|
|
|
details1 = {}
|
|
cursor.execute('SELECT d.fraction, d.service_analysis, sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
|
|
'ON sd.detail = d.id '
|
|
'WHERE d.planification = %s',
|
|
(planification_id,))
|
|
for x in cursor.fetchall():
|
|
f, s = x[0], x[1]
|
|
if (f, s) not in details1:
|
|
details1[(f, s)] = {
|
|
'fraction': f,
|
|
'service_analysis': s,
|
|
}
|
|
|
|
to_create = []
|
|
for d in details1.values():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': d['fraction'],
|
|
'service_analysis': d['service_analysis'],
|
|
})
|
|
return UnlinkTechniciansDetail1.create(to_create)
|
|
|
|
def transition_unlink(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
details = self._get_details(planification.id)
|
|
|
|
PlanificationServiceDetail.write(details, {
|
|
'staff_responsible': [('remove',
|
|
[t.id for t in self.start.technicians])],
|
|
})
|
|
return 'end'
|
|
|
|
def _get_details(self, planification_id):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationDetail = pool.get(
|
|
'lims.planification.detail')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
|
|
details = []
|
|
for detail in self.start.details1:
|
|
cursor.execute('SELECT sd.id '
|
|
'FROM "' + PlanificationDetail._table + '" d '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
|
|
' ON sd.detail = d.id '
|
|
'WHERE d.planification = %s '
|
|
'AND d.fraction = %s '
|
|
'AND d.service_analysis = %s',
|
|
(planification_id, detail.fraction.id,
|
|
detail.service_analysis.id))
|
|
for x in cursor.fetchall():
|
|
details.append(x[0])
|
|
|
|
return PlanificationServiceDetail.browse(details)
|
|
|
|
|
|
class AddFractionControlStart(ModelView):
|
|
'Add Fraction Control'
|
|
__name__ = 'lims.planification.add_fraction_con.start'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification')
|
|
type = fields.Selection([
|
|
('exist', 'Existing CON'),
|
|
('coi', 'COI'),
|
|
('mrc', 'MRC'),
|
|
('sla', 'SLA'),
|
|
('itc', 'ITC'),
|
|
('itl', 'ITL'),
|
|
], 'Control type', sort=False, required=True)
|
|
original_fraction = fields.Many2One('lims.fraction', 'Original fraction',
|
|
required=True, domain=[('id', 'in', Eval('fraction_domain'))],
|
|
depends=['fraction_domain'])
|
|
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
|
|
None, 'Fraction domain'), 'on_change_with_fraction_domain')
|
|
label = fields.Char('Label', depends=['type'],
|
|
states={'readonly': Eval('type') == 'exist'})
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', states={
|
|
'invisible': Bool(Eval('concentration_level_invisible')),
|
|
}, depends=['concentration_level_invisible'])
|
|
concentration_level_invisible = fields.Boolean(
|
|
'Concentration level invisible')
|
|
generate_repetition = fields.Boolean('Generate repetition',
|
|
states={'readonly': Eval('type') == 'exist'}, depends=['type'])
|
|
|
|
@fields.depends('planification', 'type', '_parent_planification.analysis')
|
|
def on_change_with_fraction_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Fraction = pool.get('lims.fraction')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
if not self.type:
|
|
return []
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
stored_fractions_ids = Fraction.get_stored_fractions()
|
|
|
|
special_type = 'con' if self.type == 'exist' else self.type
|
|
clause = [
|
|
('notebook.fraction.special_type', '=', special_type),
|
|
('notebook.fraction.id', 'in', stored_fractions_ids),
|
|
('analysis', 'in', p_analysis_ids),
|
|
]
|
|
if self.type == 'exist':
|
|
deadline = datetime.now() - relativedelta(days=5)
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
('notebook.fraction.sample.date2', '>=', deadline),
|
|
])
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if not notebook_lines:
|
|
return []
|
|
|
|
notebook_lines_ids = ', '.join(str(nl.id) for nl in notebook_lines)
|
|
cursor.execute('SELECT DISTINCT(n.fraction) '
|
|
'FROM "' + Notebook._table + '" n '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON nl.notebook = n.id '
|
|
'WHERE nl.id IN (' + notebook_lines_ids + ')')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@fields.depends('type', 'original_fraction', 'concentration_level',
|
|
'_parent_original_fraction.label',
|
|
'_parent_concentration_level.description')
|
|
def on_change_with_label(self, name=None):
|
|
Date = Pool().get('ir.date')
|
|
if self.type == 'exist':
|
|
return ''
|
|
label = ''
|
|
if self.original_fraction:
|
|
label += self.original_fraction.label
|
|
if self.concentration_level:
|
|
label += (' (' +
|
|
self.concentration_level.description + ')')
|
|
label += ' ' + str(Date.today())
|
|
return label
|
|
|
|
|
|
class AddFractionControl(Wizard):
|
|
'Add Fraction Control'
|
|
__name__ = 'lims.planification.add_fraction_con'
|
|
|
|
start = StateView('lims.planification.add_fraction_con.start',
|
|
'lims.lims_add_fraction_con_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
defaults = {
|
|
'planification': Transaction().context['active_id'],
|
|
'concentration_level_invisible': True,
|
|
}
|
|
if (config.con_fraction_type and
|
|
config.con_fraction_type.control_charts):
|
|
defaults['concentration_level_invisible'] = False
|
|
return defaults
|
|
|
|
def transition_add(self):
|
|
fraction = self.start.original_fraction
|
|
if self.start.type != 'exist':
|
|
fraction = self.create_control()
|
|
self.add_control(fraction)
|
|
self.add_planification_detail(fraction)
|
|
return 'end'
|
|
|
|
def create_control(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
config = Config(1)
|
|
fraction_type = config.con_fraction_type
|
|
if not fraction_type:
|
|
raise UserError(gettext('lims.msg_no_con_fraction_type'))
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
original_fraction = self.start.original_fraction
|
|
original_sample = Sample(original_fraction.sample.id)
|
|
obj_description = self._get_obj_description(original_sample)
|
|
|
|
# new sample
|
|
new_sample, = Sample.copy([original_sample], default={
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'fractions': [],
|
|
})
|
|
|
|
# new fraction
|
|
new_fraction, = Fraction.copy([original_fraction], default={
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'services': [],
|
|
'con_type': self.start.type,
|
|
'con_original_fraction': original_fraction.id,
|
|
})
|
|
|
|
# new services
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
services = Service.search([
|
|
('fraction', '=', original_fraction),
|
|
])
|
|
for service in services:
|
|
if not Analysis.is_typified(service.analysis,
|
|
new_sample.product_type, new_sample.matrix):
|
|
continue
|
|
|
|
method_id = service.method and service.method.id or None
|
|
device_id = service.device and service.device.id or None
|
|
if service.analysis.type == 'analysis':
|
|
original_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', original_fraction.id),
|
|
('analysis', '=', service.analysis.id),
|
|
('repetition', '=', 0),
|
|
], limit=1)
|
|
original_line = original_lines[0] if original_lines else None
|
|
if original_line:
|
|
method_id = original_line.method.id
|
|
if original_line.device:
|
|
device_id = original_line.device.id
|
|
|
|
new_service, = Service.copy([service], default={
|
|
'fraction': new_fraction.id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
|
|
# delete services/details not related to planification
|
|
to_delete = EntryDetailAnalysis.search([
|
|
('service', '=', new_service.id),
|
|
('analysis', 'not in', p_analysis_ids),
|
|
])
|
|
if to_delete:
|
|
with Transaction().set_user(0, set_context=True):
|
|
EntryDetailAnalysis.delete(to_delete)
|
|
if EntryDetailAnalysis.search_count([
|
|
('service', '=', new_service.id),
|
|
]) == 0:
|
|
with Transaction().set_user(0, set_context=True):
|
|
Service.delete([new_service])
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
# Generate repetition
|
|
if self.start.generate_repetition:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
self.generate_repetition(notebook_lines)
|
|
|
|
return new_fraction
|
|
|
|
def _get_obj_description(self, sample):
|
|
cursor = Transaction().connection.cursor()
|
|
ObjectiveDescription = Pool().get('lims.objective_description')
|
|
|
|
if not sample.product_type or not sample.matrix:
|
|
return None
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + ObjectiveDescription._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND matrix = %s',
|
|
(sample.product_type.id, sample.matrix.id))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
def generate_repetition(self, notebook_lines):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
analysis_to_repeat = {}
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.analysis.id not in p_analysis_ids:
|
|
continue
|
|
if notebook_line.analysis.id not in analysis_to_repeat:
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
elif (notebook_line.repetition >
|
|
analysis_to_repeat[notebook_line.analysis.id].repetition):
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
|
|
notebook = Notebook(notebook_lines[0].notebook.id)
|
|
|
|
to_create = []
|
|
for analysis_id, nline in analysis_to_repeat.items():
|
|
to_create.append({
|
|
'analysis_detail': nline.analysis_detail.id,
|
|
'service': nline.service.id,
|
|
'analysis': analysis_id,
|
|
'analysis_origin': nline.analysis_origin,
|
|
'repetition': nline.repetition + 1,
|
|
'laboratory': nline.laboratory.id,
|
|
'method': nline.method.id,
|
|
'device': nline.device.id if nline.device else None,
|
|
'initial_concentration': nline.initial_concentration,
|
|
'final_concentration': nline.final_concentration,
|
|
'initial_unit': (nline.initial_unit.id if
|
|
nline.initial_unit else None),
|
|
'final_unit': (nline.final_unit.id if
|
|
nline.final_unit else None),
|
|
'detection_limit': nline.detection_limit,
|
|
'quantification_limit': nline.quantification_limit,
|
|
'decimals': nline.decimals,
|
|
'report': nline.report,
|
|
'concentration_level': (nline.concentration_level.id if
|
|
nline.concentration_level else None),
|
|
'results_estimated_waiting': nline.results_estimated_waiting,
|
|
'department': (nline.department.id if
|
|
nline.department else None),
|
|
})
|
|
Notebook.write([notebook], {
|
|
'lines': [('create', to_create)],
|
|
})
|
|
|
|
def add_control(self, fraction):
|
|
Planification = Pool().get('lims.planification')
|
|
Planification.write([self.start.planification], {
|
|
'controls': [('add', [fraction.id])],
|
|
})
|
|
|
|
def add_planification_detail(self, fraction):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
clause = [
|
|
('notebook.fraction', '=', fraction.id),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
]
|
|
if self.start.type == 'exist':
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
])
|
|
else:
|
|
clause.append(('planification', '=', None))
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if notebook_lines:
|
|
details_to_create = {}
|
|
for nl in notebook_lines:
|
|
f = nl.notebook.fraction.id
|
|
s = nl.service.analysis.id
|
|
if (f, s) not in details_to_create:
|
|
details_to_create[(f, s)] = []
|
|
details_to_create[(f, s)].append({
|
|
'notebook_line': nl.id,
|
|
'planned_service': s,
|
|
'is_control': True,
|
|
})
|
|
if details_to_create:
|
|
for k, v in details_to_create.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', self.start.planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
PlanificationDetail.create([{
|
|
'planification': self.start.planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
}])
|
|
|
|
|
|
class AddFractionRMBMZStart(ModelView):
|
|
'Add Fraction RM/BMZ'
|
|
__name__ = 'lims.planification.add_fraction_rm_bmz.start'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification')
|
|
type = fields.Selection([
|
|
('rm', 'RM'),
|
|
('bmz', 'BMZ'),
|
|
], 'Control type', sort=False, required=True)
|
|
rm_bmz_type = fields.Selection([
|
|
('sla', 'SLA'),
|
|
('noref', 'No Reference'),
|
|
('exist', 'Existing RM/BMZ'),
|
|
], 'RM/BMZ type', sort=False, required=True)
|
|
reference_fraction = fields.Many2One('lims.fraction',
|
|
'Reference fraction', depends=['fraction_domain', 'rm_bmz_type'],
|
|
states={
|
|
'readonly': Bool(Equal(Eval('rm_bmz_type'), 'noref')),
|
|
'required': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
|
|
}, domain=[('id', 'in', Eval('fraction_domain'))])
|
|
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
|
|
None, 'Fraction domain'), 'on_change_with_fraction_domain')
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
states={
|
|
'readonly': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
|
|
'required': Bool(Equal(Eval('rm_bmz_type'), 'noref'))},
|
|
domain=[('id', 'in', Eval('product_type_domain'))],
|
|
depends=['rm_bmz_type', 'product_type_domain'])
|
|
product_type_domain = fields.Function(fields.Many2Many(
|
|
'lims.product.type', None, None, 'Product type domain'),
|
|
'on_change_with_product_type_domain')
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
|
|
states={
|
|
'readonly': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
|
|
'required': Bool(Equal(Eval('rm_bmz_type'), 'noref'))},
|
|
domain=[('id', 'in', Eval('matrix_domain'))],
|
|
depends=['rm_bmz_type', 'matrix_domain'])
|
|
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
|
|
None, None, 'Matrix domain'),
|
|
'on_change_with_matrix_domain')
|
|
repetitions = fields.Integer('Repetitions',
|
|
states={'readonly': Or(Eval('type') == 'bmz',
|
|
Eval('rm_bmz_type') == 'exist')},
|
|
depends=['type', 'rm_bmz_type'])
|
|
label = fields.Char('Label', depends=['rm_bmz_type'], states={
|
|
'readonly': Eval('rm_bmz_type') == 'exist'})
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', states={
|
|
'invisible': Bool(Eval('concentration_level_invisible')),
|
|
}, depends=['concentration_level_invisible'])
|
|
concentration_level_invisible = fields.Boolean(
|
|
'Concentration level invisible')
|
|
|
|
@fields.depends('type')
|
|
def on_change_with_concentration_level_invisible(self, name=None):
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
if self.type == 'rm':
|
|
if (config.rm_fraction_type and
|
|
config.rm_fraction_type.control_charts):
|
|
return False
|
|
elif self.type == 'bmz':
|
|
if (config.bmz_fraction_type and
|
|
config.bmz_fraction_type.control_charts):
|
|
return False
|
|
return True
|
|
|
|
@fields.depends('planification', 'type', 'rm_bmz_type',
|
|
'_parent_planification.analysis')
|
|
def on_change_with_fraction_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Fraction = pool.get('lims.fraction')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
if not self.type or not self.rm_bmz_type:
|
|
return []
|
|
if self.rm_bmz_type == 'noref':
|
|
return []
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
stored_fractions_ids = Fraction.get_stored_fractions()
|
|
|
|
special_type = 'sla' if self.rm_bmz_type == 'sla' else self.type
|
|
clause = [
|
|
('notebook.fraction.special_type', '=', special_type),
|
|
('notebook.fraction.id', 'in', stored_fractions_ids),
|
|
('analysis', 'in', p_analysis_ids),
|
|
]
|
|
if self.rm_bmz_type == 'exist':
|
|
deadline = datetime.now() - relativedelta(days=5)
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
('notebook.fraction.sample.date2', '>=', deadline),
|
|
])
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if not notebook_lines:
|
|
return []
|
|
|
|
notebook_lines_ids = ', '.join(str(nl.id) for nl in notebook_lines)
|
|
cursor.execute('SELECT DISTINCT(n.fraction) '
|
|
'FROM "' + Notebook._table + '" n '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON nl.notebook = n.id '
|
|
'WHERE nl.id IN (' + notebook_lines_ids + ')')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@staticmethod
|
|
def default_product_type_domain():
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
cursor.execute('SELECT DISTINCT(product_type) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE valid')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
def on_change_with_product_type_domain(self, name=None):
|
|
return self.default_product_type_domain()
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_product_type(self):
|
|
matrix = None
|
|
if self.product_type:
|
|
matrixs = self.on_change_with_matrix_domain()
|
|
if len(matrixs) == 1:
|
|
matrix = matrixs[0]
|
|
self.matrix = matrix
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_with_matrix_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
if not self.product_type:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(matrix) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND valid',
|
|
(self.product_type.id,))
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@fields.depends('type', 'rm_bmz_type', 'reference_fraction',
|
|
'product_type', 'matrix', 'concentration_level',
|
|
'_parent_reference_fraction.label',
|
|
'_parent_concentration_level.description')
|
|
def on_change_with_label(self, name=None):
|
|
Date = Pool().get('ir.date')
|
|
if self.rm_bmz_type == 'exist':
|
|
return ''
|
|
label = ''
|
|
if self.type == 'rm':
|
|
label = 'RM'
|
|
if self.concentration_level:
|
|
label += (' (' +
|
|
self.concentration_level.description + ')')
|
|
elif self.type == 'bmz':
|
|
label = 'BMZ'
|
|
if self.rm_bmz_type == 'sla':
|
|
if self.reference_fraction:
|
|
label += (' ' +
|
|
self.reference_fraction.label)
|
|
label += ' ' + str(Date.today())
|
|
elif self.rm_bmz_type == 'noref':
|
|
label += ' ' + str(Date.today())
|
|
return label
|
|
|
|
|
|
class AddFractionRMBMZ(Wizard):
|
|
'Add Fraction RM/BMZ'
|
|
__name__ = 'lims.planification.add_fraction_rm_bmz'
|
|
|
|
start = StateView('lims.planification.add_fraction_rm_bmz.start',
|
|
'lims.lims_add_fraction_rm_bmz_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
defaults = {
|
|
'planification': Transaction().context['active_id'],
|
|
'concentration_level_invisible': True,
|
|
}
|
|
return defaults
|
|
|
|
def transition_add(self):
|
|
fraction = self.start.reference_fraction
|
|
if self.start.rm_bmz_type != 'exist':
|
|
fraction = self.create_control()
|
|
self.add_control(fraction)
|
|
self.add_planification_detail(fraction)
|
|
return 'end'
|
|
|
|
def create_control(self):
|
|
if self.start.rm_bmz_type == 'sla':
|
|
return self._create_control_sla()
|
|
if self.start.rm_bmz_type == 'noref':
|
|
return self._create_control_noref()
|
|
|
|
def _create_control_sla(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
config = Config(1)
|
|
if self.start.type == 'rm':
|
|
if not config.rm_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_rm_fraction_type'))
|
|
fraction_type = config.rm_fraction_type
|
|
elif self.start.type == 'bmz':
|
|
if not config.bmz_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_bmz_fraction_type'))
|
|
fraction_type = config.bmz_fraction_type
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
original_fraction = self.start.reference_fraction
|
|
original_sample = Sample(original_fraction.sample.id)
|
|
obj_description = self._get_obj_description(original_sample)
|
|
|
|
# new sample
|
|
new_sample, = Sample.copy([original_sample], default={
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'fractions': [],
|
|
})
|
|
|
|
# new fraction
|
|
fraction_default = {
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'con_type': '',
|
|
'services': [],
|
|
}
|
|
if self.start.type == 'rm':
|
|
fraction_default['rm_type'] = 'sla'
|
|
fraction_default['rm_product_type'] = new_sample.product_type.id
|
|
fraction_default['rm_matrix'] = new_sample.matrix.id
|
|
fraction_default['rm_original_fraction'] = original_fraction.id
|
|
if self.start.type == 'bmz':
|
|
fraction_default['bmz_type'] = 'sla'
|
|
fraction_default['bmz_product_type'] = new_sample.product_type.id
|
|
fraction_default['bmz_matrix'] = new_sample.matrix.id
|
|
fraction_default['bmz_original_fraction'] = original_fraction.id
|
|
new_fraction, = Fraction.copy([original_fraction],
|
|
default=fraction_default)
|
|
|
|
# new services
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
services = Service.search([
|
|
('fraction', '=', original_fraction),
|
|
])
|
|
for service in services:
|
|
if not Analysis.is_typified(service.analysis,
|
|
new_sample.product_type, new_sample.matrix):
|
|
continue
|
|
|
|
method_id = service.method and service.method.id or None
|
|
device_id = service.device and service.device.id or None
|
|
if service.analysis.type == 'analysis':
|
|
original_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', original_fraction.id),
|
|
('analysis', '=', service.analysis.id),
|
|
('repetition', '=', 0),
|
|
], limit=1)
|
|
original_line = original_lines[0] if original_lines else None
|
|
if original_line:
|
|
method_id = original_line.method.id
|
|
if original_line.device:
|
|
device_id = original_line.device.id
|
|
|
|
new_service, = Service.copy([service], default={
|
|
'fraction': new_fraction.id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
|
|
# delete services/details not related to planification
|
|
to_delete = EntryDetailAnalysis.search([
|
|
('service', '=', new_service.id),
|
|
('analysis', 'not in', p_analysis_ids),
|
|
])
|
|
if to_delete:
|
|
with Transaction().set_user(0, set_context=True):
|
|
EntryDetailAnalysis.delete(to_delete)
|
|
if EntryDetailAnalysis.search_count([
|
|
('service', '=', new_service.id),
|
|
]) == 0:
|
|
with Transaction().set_user(0, set_context=True):
|
|
Service.delete([new_service])
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
if self.start.type == 'rm':
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'final_concentration': None,
|
|
'final_unit': None,
|
|
'detection_limit': None,
|
|
'quantification_limit': None,
|
|
}
|
|
if config.rm_start_uom:
|
|
defaults['initial_unit'] = config.rm_start_uom.id
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
# Generate repetition
|
|
if self.start.repetitions and self.start.repetitions > 0:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
self.generate_repetition(notebook_lines,
|
|
self.start.repetitions)
|
|
|
|
return new_fraction
|
|
|
|
def _get_obj_description(self, sample):
|
|
cursor = Transaction().connection.cursor()
|
|
ObjectiveDescription = Pool().get('lims.objective_description')
|
|
|
|
if not sample.product_type or not sample.matrix:
|
|
return None
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + ObjectiveDescription._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND matrix = %s',
|
|
(sample.product_type.id, sample.matrix.id))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
def _create_control_noref(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
config = Config(1)
|
|
if self.start.type == 'rm':
|
|
if not config.rm_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_rm_fraction_type'))
|
|
fraction_type = config.rm_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext(
|
|
'lims.msg_no_rm_default_configuration'))
|
|
elif self.start.type == 'bmz':
|
|
if not config.bmz_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_bmz_fraction_type'))
|
|
fraction_type = config.bmz_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext(
|
|
'lims.msg_no_bmz_default_configuration'))
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
laboratory = self.start.planification.laboratory
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
obj_description = self._get_obj_description(self.start)
|
|
|
|
# new sample
|
|
new_sample, = Sample.create([{
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'product_type': self.start.product_type.id,
|
|
'matrix': self.start.matrix.id,
|
|
'zone': entry.party.entry_zone.id,
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'packages_quantity': 1,
|
|
'fractions': [],
|
|
}])
|
|
|
|
# new fraction
|
|
fraction_default = {
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'storage_location': laboratory.related_location.id,
|
|
'packages_quantity': 1,
|
|
'package_type': fraction_type.default_package_type.id,
|
|
'fraction_state': fraction_type.default_fraction_state.id,
|
|
'services': [],
|
|
}
|
|
if fraction_type.max_storage_time:
|
|
fraction_default['storage_time'] = fraction_type.max_storage_time
|
|
elif laboratory.related_location.storage_time:
|
|
fraction_default['storage_time'] = (
|
|
laboratory.related_location.storage_time)
|
|
else:
|
|
fraction_default['storage_time'] = 3
|
|
if self.start.type == 'rm':
|
|
fraction_default['rm_type'] = 'noinitialrm'
|
|
fraction_default['rm_product_type'] = new_sample.product_type.id
|
|
fraction_default['rm_matrix'] = new_sample.matrix.id
|
|
if self.start.type == 'bmz':
|
|
fraction_default['bmz_type'] = 'noinitialbmz'
|
|
fraction_default['bmz_product_type'] = new_sample.product_type.id
|
|
fraction_default['bmz_matrix'] = new_sample.matrix.id
|
|
new_fraction, = Fraction.create([fraction_default])
|
|
|
|
# new services
|
|
services_default = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if not Analysis.is_typified(p_analysis,
|
|
new_sample.product_type, new_sample.matrix):
|
|
raise UserError(gettext('lims.msg_not_typified',
|
|
analysis=p_analysis.rec_name,
|
|
product_type=new_sample.product_type.rec_name,
|
|
matrix=new_sample.matrix.rec_name,
|
|
))
|
|
laboratory_id = (laboratory.id if p_analysis.type != 'group'
|
|
else None)
|
|
method_id = None
|
|
if new_sample.typification_domain:
|
|
for t in new_sample.typification_domain:
|
|
if (t.analysis.id == p_analysis.id and
|
|
t.by_default is True):
|
|
method_id = t.method.id
|
|
device_id = None
|
|
if p_analysis.devices:
|
|
for d in p_analysis.devices:
|
|
if (d.laboratory.id == laboratory.id and
|
|
d.by_default is True):
|
|
device_id = d.device.id
|
|
services_default.append({
|
|
'fraction': new_fraction.id,
|
|
'analysis': p_analysis.id,
|
|
'laboratory': laboratory_id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
for service in services_default:
|
|
new_service, = Service.create([service])
|
|
|
|
# new analysis details (on service create)
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
if self.start.type == 'rm':
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'final_concentration': None,
|
|
'final_unit': None,
|
|
'detection_limit': None,
|
|
'quantification_limit': None,
|
|
}
|
|
if config.rm_start_uom:
|
|
defaults['initial_unit'] = config.rm_start_uom.id
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
# Generate repetition
|
|
if self.start.repetitions and self.start.repetitions > 0:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
self.generate_repetition(notebook_lines,
|
|
self.start.repetitions)
|
|
|
|
return new_fraction
|
|
|
|
def generate_repetition(self, notebook_lines, repetitions):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
analysis_to_repeat = {}
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.analysis.id not in p_analysis_ids:
|
|
continue
|
|
if notebook_line.analysis.id not in analysis_to_repeat:
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
elif (notebook_line.repetition >
|
|
analysis_to_repeat[notebook_line.analysis.id].repetition):
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
|
|
notebook = Notebook(notebook_lines[0].notebook.id)
|
|
|
|
to_create = []
|
|
for nline in analysis_to_repeat.values():
|
|
for i in range(1, repetitions + 1):
|
|
to_create.append({
|
|
'analysis_detail': nline.analysis_detail.id,
|
|
'service': nline.service.id,
|
|
'analysis': nline.analysis.id,
|
|
'analysis_origin': nline.analysis_origin,
|
|
'repetition': nline.repetition + i,
|
|
'laboratory': nline.laboratory.id,
|
|
'method': nline.method.id,
|
|
'device': nline.device.id if nline.device else None,
|
|
'initial_concentration': nline.initial_concentration,
|
|
'final_concentration': nline.final_concentration,
|
|
'initial_unit': (nline.initial_unit.id if
|
|
nline.initial_unit else None),
|
|
'final_unit': (nline.final_unit.id if
|
|
nline.final_unit else None),
|
|
'detection_limit': nline.detection_limit,
|
|
'quantification_limit': nline.quantification_limit,
|
|
'decimals': nline.decimals,
|
|
'report': nline.report,
|
|
'concentration_level': (nline.concentration_level.id if
|
|
nline.concentration_level else None),
|
|
'results_estimated_waiting': (
|
|
nline.results_estimated_waiting),
|
|
'department': (nline.department.id if
|
|
nline.department else None),
|
|
})
|
|
Notebook.write([notebook], {
|
|
'lines': [('create', to_create)],
|
|
})
|
|
|
|
def add_control(self, fraction):
|
|
Planification = Pool().get('lims.planification')
|
|
Planification.write([self.start.planification], {
|
|
'controls': [('add', [fraction.id])],
|
|
})
|
|
|
|
def add_planification_detail(self, fraction):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
clause = [
|
|
('notebook.fraction', '=', fraction.id),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
]
|
|
if self.start.rm_bmz_type == 'exist':
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
])
|
|
else:
|
|
clause.append(('planification', '=', None))
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if notebook_lines:
|
|
details_to_create = {}
|
|
for nl in notebook_lines:
|
|
f = nl.notebook.fraction.id
|
|
s = nl.service.analysis.id
|
|
if (f, s) not in details_to_create:
|
|
details_to_create[(f, s)] = []
|
|
details_to_create[(f, s)].append({
|
|
'notebook_line': nl.id,
|
|
'planned_service': s,
|
|
'is_control': True,
|
|
})
|
|
if details_to_create:
|
|
for k, v in details_to_create.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', self.start.planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
PlanificationDetail.create([{
|
|
'planification': self.start.planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
}])
|
|
|
|
|
|
class AddFractionBREStart(ModelView):
|
|
'Add Fraction BRE'
|
|
__name__ = 'lims.planification.add_fraction_bre.start'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification')
|
|
type = fields.Selection([
|
|
('new', 'New BRE'),
|
|
('exist', 'Existing BRE'),
|
|
], 'BRE Type', sort=False, required=True)
|
|
bre_fraction = fields.Many2One('lims.fraction',
|
|
'BRE fraction', depends=['fraction_domain', 'type'],
|
|
states={
|
|
'readonly': Bool(Equal(Eval('type'), 'new')),
|
|
'required': Bool(Equal(Eval('type'), 'exist')),
|
|
}, domain=[('id', 'in', Eval('fraction_domain'))])
|
|
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
|
|
None, 'Fraction domain'), 'on_change_with_fraction_domain')
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
states={'required': Bool(Equal(Eval('type'), 'new'))},
|
|
domain=[('id', 'in', Eval('product_type_domain'))],
|
|
depends=['type', 'product_type_domain'])
|
|
product_type_domain = fields.Function(fields.Many2Many(
|
|
'lims.product.type', None, None, 'Product type domain'),
|
|
'on_change_with_product_type_domain')
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
|
|
states={'required': Bool(Equal(Eval('type'), 'new'))},
|
|
domain=[('id', 'in', Eval('matrix_domain'))],
|
|
depends=['type', 'matrix_domain'])
|
|
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
|
|
None, None, 'Matrix domain'),
|
|
'on_change_with_matrix_domain')
|
|
reagents = fields.One2Many('lims.fraction.reagent', None,
|
|
'Reagents', states={
|
|
'readonly': Bool(Equal(Eval('type'), 'exist')),
|
|
}, depends=['type'], required=True)
|
|
label = fields.Char('Label', depends=['type'], states={
|
|
'readonly': Eval('type') == 'exist'})
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', states={
|
|
'invisible': Bool(Eval('concentration_level_invisible')),
|
|
}, depends=['concentration_level_invisible'])
|
|
concentration_level_invisible = fields.Boolean(
|
|
'Concentration level invisible')
|
|
|
|
@fields.depends('planification', 'type', '_parent_planification.analysis')
|
|
def on_change_with_fraction_domain(self, name=None):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Fraction = pool.get('lims.fraction')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
if self.type != 'exist':
|
|
return []
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
stored_fractions_ids = Fraction.get_stored_fractions()
|
|
|
|
deadline = datetime.now() - relativedelta(days=5)
|
|
clause = [
|
|
('notebook.fraction.special_type', '=', 'bre'),
|
|
('notebook.fraction.id', 'in', stored_fractions_ids),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
('notebook.fraction.sample.date2', '>=', deadline),
|
|
]
|
|
notebook_lines = NotebookLine.search(clause)
|
|
|
|
fractions = [nl.notebook.fraction.id for nl in notebook_lines]
|
|
return list(set(fractions))
|
|
|
|
@staticmethod
|
|
def default_product_type_domain():
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
cursor.execute('SELECT DISTINCT(product_type) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE valid')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
def on_change_with_product_type_domain(self, name=None):
|
|
return self.default_product_type_domain()
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_product_type(self):
|
|
matrix = None
|
|
if self.product_type:
|
|
matrixs = self.on_change_with_matrix_domain()
|
|
if len(matrixs) == 1:
|
|
matrix = matrixs[0]
|
|
self.matrix = matrix
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_with_matrix_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
if not self.product_type:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(matrix) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND valid',
|
|
(self.product_type.id,))
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@fields.depends('type', 'product_type', 'matrix')
|
|
def on_change_with_label(self, name=None):
|
|
Date = Pool().get('ir.date')
|
|
if self.type == 'exist':
|
|
return ''
|
|
label = 'BRE'
|
|
label += ' ' + str(Date.today())
|
|
return label
|
|
|
|
|
|
class AddFractionBRE(Wizard):
|
|
'Add Fraction BRE'
|
|
__name__ = 'lims.planification.add_fraction_bre'
|
|
|
|
start = StateView('lims.planification.add_fraction_bre.start',
|
|
'lims.lims_add_fraction_bre_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
defaults = {
|
|
'planification': Transaction().context['active_id'],
|
|
'concentration_level_invisible': True,
|
|
}
|
|
if (config.bre_fraction_type and
|
|
config.bre_fraction_type.control_charts):
|
|
defaults['concentration_level_invisible'] = False
|
|
return defaults
|
|
|
|
def transition_add(self):
|
|
fraction = self.start.bre_fraction
|
|
if self.start.type == 'new':
|
|
fraction = self.create_control()
|
|
self.add_control(fraction)
|
|
self.add_planification_detail(fraction)
|
|
return 'end'
|
|
|
|
def create_control(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
config = Config(1)
|
|
if not config.bre_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_bre_fraction_type'))
|
|
fraction_type = config.bre_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext('lims.msg_no_bre_default_configuration'))
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
laboratory = self.start.planification.laboratory
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
obj_description = self._get_obj_description(self.start)
|
|
|
|
# new sample
|
|
new_sample, = Sample.create([{
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'product_type': self.start.product_type.id,
|
|
'matrix': self.start.matrix.id,
|
|
'zone': entry.party.entry_zone.id,
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'packages_quantity': 1,
|
|
'fractions': [],
|
|
}])
|
|
|
|
# new fraction
|
|
bre_reagents = [{
|
|
'product': r.product.id,
|
|
'lot': r.lot.id if r.lot else None,
|
|
} for r in self.start.reagents]
|
|
fraction_default = {
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'storage_location': laboratory.related_location.id,
|
|
'packages_quantity': 1,
|
|
'package_type': fraction_type.default_package_type.id,
|
|
'fraction_state': fraction_type.default_fraction_state.id,
|
|
'services': [],
|
|
'bre_product_type': new_sample.product_type.id,
|
|
'bre_matrix': new_sample.matrix.id,
|
|
'bre_reagents': [('create', bre_reagents)],
|
|
}
|
|
if fraction_type.max_storage_time:
|
|
fraction_default['storage_time'] = fraction_type.max_storage_time
|
|
elif laboratory.related_location.storage_time:
|
|
fraction_default['storage_time'] = (
|
|
laboratory.related_location.storage_time)
|
|
else:
|
|
fraction_default['storage_time'] = 3
|
|
new_fraction, = Fraction.create([fraction_default])
|
|
|
|
# new services
|
|
services_default = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if not Analysis.is_typified(p_analysis,
|
|
new_sample.product_type, new_sample.matrix):
|
|
raise UserError(gettext('lims.msg_not_typified',
|
|
analysis=p_analysis.rec_name,
|
|
product_type=new_sample.product_type.rec_name,
|
|
matrix=new_sample.matrix.rec_name,
|
|
))
|
|
laboratory_id = (laboratory.id if p_analysis.type != 'group'
|
|
else None)
|
|
method_id = None
|
|
if new_sample.typification_domain:
|
|
for t in new_sample.typification_domain:
|
|
if (t.analysis.id == p_analysis.id and
|
|
t.by_default is True):
|
|
method_id = t.method.id
|
|
device_id = None
|
|
if p_analysis.devices:
|
|
for d in p_analysis.devices:
|
|
if (d.laboratory.id == laboratory.id and
|
|
d.by_default is True):
|
|
device_id = d.device.id
|
|
services_default.append({
|
|
'fraction': new_fraction.id,
|
|
'analysis': p_analysis.id,
|
|
'laboratory': laboratory_id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
for service in services_default:
|
|
new_service, = Service.create([service])
|
|
|
|
# new analysis details (on service create)
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
return new_fraction
|
|
|
|
def _get_obj_description(self, sample):
|
|
cursor = Transaction().connection.cursor()
|
|
ObjectiveDescription = Pool().get('lims.objective_description')
|
|
|
|
if not sample.product_type or not sample.matrix:
|
|
return None
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + ObjectiveDescription._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND matrix = %s',
|
|
(sample.product_type.id, sample.matrix.id))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
def add_control(self, fraction):
|
|
Planification = Pool().get('lims.planification')
|
|
Planification.write([self.start.planification], {
|
|
'controls': [('add', [fraction.id])],
|
|
})
|
|
|
|
def add_planification_detail(self, fraction):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
clause = [
|
|
('notebook.fraction', '=', fraction.id),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
]
|
|
if self.start.type == 'exist':
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
])
|
|
else:
|
|
clause.append(('planification', '=', None))
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if notebook_lines:
|
|
details_to_create = {}
|
|
for nl in notebook_lines:
|
|
f = nl.notebook.fraction.id
|
|
s = nl.service.analysis.id
|
|
if (f, s) not in details_to_create:
|
|
details_to_create[(f, s)] = []
|
|
details_to_create[(f, s)].append({
|
|
'notebook_line': nl.id,
|
|
'planned_service': s,
|
|
'is_control': True,
|
|
})
|
|
if details_to_create:
|
|
for k, v in details_to_create.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', self.start.planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
PlanificationDetail.create([{
|
|
'planification': self.start.planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
}])
|
|
|
|
|
|
class AddFractionMRTStart(ModelView):
|
|
'Add Fraction MRT'
|
|
__name__ = 'lims.planification.add_fraction_mrt.start'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification')
|
|
type = fields.Selection([
|
|
('new', 'New MRT'),
|
|
('exist', 'Existing MRT'),
|
|
], 'MRT Type', sort=False, required=True)
|
|
mrt_fraction = fields.Many2One('lims.fraction',
|
|
'MRT fraction', depends=['fraction_domain', 'type'],
|
|
states={
|
|
'readonly': Bool(Equal(Eval('type'), 'new')),
|
|
'required': Bool(Equal(Eval('type'), 'exist')),
|
|
}, domain=[('id', 'in', Eval('fraction_domain'))])
|
|
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
|
|
None, 'Fraction domain'), 'on_change_with_fraction_domain')
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
states={'required': Bool(Equal(Eval('type'), 'new'))},
|
|
domain=[('id', 'in', Eval('product_type_domain'))],
|
|
depends=['type', 'product_type_domain'])
|
|
product_type_domain = fields.Function(fields.Many2Many(
|
|
'lims.product.type', None, None, 'Product type domain'),
|
|
'on_change_with_product_type_domain')
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
|
|
states={'required': Bool(Equal(Eval('type'), 'new'))},
|
|
domain=[('id', 'in', Eval('matrix_domain'))],
|
|
depends=['type', 'matrix_domain'])
|
|
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
|
|
None, None, 'Matrix domain'),
|
|
'on_change_with_matrix_domain')
|
|
repetitions = fields.Integer('Repetitions')
|
|
label = fields.Char('Label', depends=['type'], states={
|
|
'readonly': Eval('type') == 'exist'})
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', states={
|
|
'invisible': Bool(Eval('concentration_level_invisible')),
|
|
}, depends=['concentration_level_invisible'])
|
|
concentration_level_invisible = fields.Boolean(
|
|
'Concentration level invisible')
|
|
|
|
@fields.depends('planification', 'type', '_parent_planification.analysis')
|
|
def on_change_with_fraction_domain(self, name=None):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Fraction = pool.get('lims.fraction')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
if self.type != 'exist':
|
|
return []
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
stored_fractions_ids = Fraction.get_stored_fractions()
|
|
|
|
deadline = datetime.now() - relativedelta(days=5)
|
|
clause = [
|
|
('notebook.fraction.special_type', '=', 'mrt'),
|
|
('notebook.fraction.id', 'in', stored_fractions_ids),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
('notebook.fraction.sample.date2', '>=', deadline),
|
|
]
|
|
notebook_lines = NotebookLine.search(clause)
|
|
|
|
fractions = [nl.notebook.fraction.id for nl in notebook_lines]
|
|
return list(set(fractions))
|
|
|
|
@staticmethod
|
|
def default_product_type_domain():
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
cursor.execute('SELECT DISTINCT(product_type) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE valid')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
def on_change_with_product_type_domain(self, name=None):
|
|
return self.default_product_type_domain()
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_product_type(self):
|
|
matrix = None
|
|
if self.product_type:
|
|
matrixs = self.on_change_with_matrix_domain()
|
|
if len(matrixs) == 1:
|
|
matrix = matrixs[0]
|
|
self.matrix = matrix
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_with_matrix_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
if not self.product_type:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(matrix) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND valid',
|
|
(self.product_type.id,))
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@fields.depends('type', 'product_type', 'matrix')
|
|
def on_change_with_label(self, name=None):
|
|
Date = Pool().get('ir.date')
|
|
if self.type == 'exist':
|
|
return ''
|
|
label = 'MRT'
|
|
label += ' ' + str(Date.today())
|
|
return label
|
|
|
|
|
|
class AddFractionMRT(Wizard):
|
|
'Add Fraction MRT'
|
|
__name__ = 'lims.planification.add_fraction_mrt'
|
|
|
|
start = StateView('lims.planification.add_fraction_mrt.start',
|
|
'lims.lims_add_fraction_mrt_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
defaults = {
|
|
'planification': Transaction().context['active_id'],
|
|
'concentration_level_invisible': True,
|
|
}
|
|
if (config.mrt_fraction_type and
|
|
config.mrt_fraction_type.control_charts):
|
|
defaults['concentration_level_invisible'] = False
|
|
return defaults
|
|
|
|
def transition_add(self):
|
|
fraction = self.start.mrt_fraction
|
|
if self.start.type == 'new':
|
|
fraction = self.create_control()
|
|
self.add_control(fraction)
|
|
self.add_planification_detail(fraction)
|
|
return 'end'
|
|
|
|
def create_control(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
config = Config(1)
|
|
if not config.mrt_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_mrt_fraction_type'))
|
|
fraction_type = config.mrt_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext('lims.msg_no_mrt_default_configuration'))
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
laboratory = self.start.planification.laboratory
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
obj_description = self._get_obj_description(self.start)
|
|
|
|
# new sample
|
|
new_sample, = Sample.create([{
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'product_type': self.start.product_type.id,
|
|
'matrix': self.start.matrix.id,
|
|
'zone': entry.party.entry_zone.id,
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'packages_quantity': 1,
|
|
'fractions': [],
|
|
}])
|
|
|
|
# new fraction
|
|
fraction_default = {
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'storage_location': laboratory.related_location.id,
|
|
'packages_quantity': 1,
|
|
'package_type': fraction_type.default_package_type.id,
|
|
'fraction_state': fraction_type.default_fraction_state.id,
|
|
'services': [],
|
|
'mrt_product_type': new_sample.product_type.id,
|
|
'mrt_matrix': new_sample.matrix.id,
|
|
}
|
|
if fraction_type.max_storage_time:
|
|
fraction_default['storage_time'] = fraction_type.max_storage_time
|
|
elif laboratory.related_location.storage_time:
|
|
fraction_default['storage_time'] = (
|
|
laboratory.related_location.storage_time)
|
|
else:
|
|
fraction_default['storage_time'] = 3
|
|
new_fraction, = Fraction.create([fraction_default])
|
|
|
|
# new services
|
|
services_default = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if not Analysis.is_typified(p_analysis,
|
|
new_sample.product_type, new_sample.matrix):
|
|
raise UserError(gettext('lims.msg_not_typified',
|
|
analysis=p_analysis.rec_name,
|
|
product_type=new_sample.product_type.rec_name,
|
|
matrix=new_sample.matrix.rec_name,
|
|
))
|
|
laboratory_id = (laboratory.id if p_analysis.type != 'group'
|
|
else None)
|
|
method_id = None
|
|
if new_sample.typification_domain:
|
|
for t in new_sample.typification_domain:
|
|
if (t.analysis.id == p_analysis.id and
|
|
t.by_default is True):
|
|
method_id = t.method.id
|
|
device_id = None
|
|
if p_analysis.devices:
|
|
for d in p_analysis.devices:
|
|
if (d.laboratory.id == laboratory.id and
|
|
d.by_default is True):
|
|
device_id = d.device.id
|
|
services_default.append({
|
|
'fraction': new_fraction.id,
|
|
'analysis': p_analysis.id,
|
|
'laboratory': laboratory_id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
for service in services_default:
|
|
new_service, = Service.create([service])
|
|
|
|
# new analysis details (on service create)
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
# Generate repetition
|
|
if self.start.repetitions and self.start.repetitions > 0:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
self.generate_repetition(notebook_lines,
|
|
self.start.repetitions)
|
|
|
|
return new_fraction
|
|
|
|
def _get_obj_description(self, sample):
|
|
cursor = Transaction().connection.cursor()
|
|
ObjectiveDescription = Pool().get('lims.objective_description')
|
|
|
|
if not sample.product_type or not sample.matrix:
|
|
return None
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + ObjectiveDescription._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND matrix = %s',
|
|
(sample.product_type.id, sample.matrix.id))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
def generate_repetition(self, notebook_lines, repetitions):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
|
|
analysis_to_repeat = {}
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.analysis.id not in p_analysis_ids:
|
|
continue
|
|
if notebook_line.analysis.id not in analysis_to_repeat:
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
elif (notebook_line.repetition >
|
|
analysis_to_repeat[notebook_line.analysis.id].repetition):
|
|
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
|
|
|
|
notebook = Notebook(notebook_lines[0].notebook.id)
|
|
|
|
to_create = []
|
|
for nline in analysis_to_repeat.values():
|
|
for i in range(1, repetitions + 1):
|
|
to_create.append({
|
|
'analysis_detail': nline.analysis_detail.id,
|
|
'service': nline.service.id,
|
|
'analysis': nline.analysis.id,
|
|
'analysis_origin': nline.analysis_origin,
|
|
'repetition': nline.repetition + i,
|
|
'laboratory': nline.laboratory.id,
|
|
'method': nline.method.id,
|
|
'device': nline.device.id if nline.device else None,
|
|
'initial_concentration': nline.initial_concentration,
|
|
'final_concentration': nline.final_concentration,
|
|
'initial_unit': (nline.initial_unit.id if
|
|
nline.initial_unit else None),
|
|
'final_unit': (nline.final_unit.id if
|
|
nline.final_unit else None),
|
|
'detection_limit': nline.detection_limit,
|
|
'quantification_limit': nline.quantification_limit,
|
|
'decimals': nline.decimals,
|
|
'report': nline.report,
|
|
'concentration_level': (nline.concentration_level.id if
|
|
nline.concentration_level else None),
|
|
'results_estimated_waiting': (
|
|
nline.results_estimated_waiting),
|
|
'department': (nline.department.id if
|
|
nline.department else None),
|
|
})
|
|
Notebook.write([notebook], {
|
|
'lines': [('create', to_create)],
|
|
})
|
|
|
|
def add_control(self, fraction):
|
|
Planification = Pool().get('lims.planification')
|
|
Planification.write([self.start.planification], {
|
|
'controls': [('add', [fraction.id])],
|
|
})
|
|
|
|
def add_planification_detail(self, fraction):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
p_analysis_ids = []
|
|
for p_analysis in self.start.planification.analysis:
|
|
if p_analysis.type == 'analysis':
|
|
p_analysis_ids.append(p_analysis.id)
|
|
else:
|
|
p_analysis_ids.extend(
|
|
Analysis.get_included_analysis_analysis(p_analysis.id))
|
|
clause = [
|
|
('notebook.fraction', '=', fraction.id),
|
|
('analysis', 'in', p_analysis_ids),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
]
|
|
if self.start.type == 'exist':
|
|
clause.extend([
|
|
('result', 'in', (None, '')),
|
|
('end_date', '=', None),
|
|
('annulment_date', '=', None),
|
|
])
|
|
else:
|
|
clause.append(('planification', '=', None))
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if notebook_lines:
|
|
details_to_create = {}
|
|
for nl in notebook_lines:
|
|
f = nl.notebook.fraction.id
|
|
s = nl.service.analysis.id
|
|
if (f, s) not in details_to_create:
|
|
details_to_create[(f, s)] = []
|
|
details_to_create[(f, s)].append({
|
|
'notebook_line': nl.id,
|
|
'planned_service': s,
|
|
'is_control': True,
|
|
})
|
|
if details_to_create:
|
|
for k, v in details_to_create.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', self.start.planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
PlanificationDetail.create([{
|
|
'planification': self.start.planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
}])
|
|
|
|
|
|
class RemoveControlStart(ModelView):
|
|
'Remove Control'
|
|
__name__ = 'lims.planification.remove_control.start'
|
|
|
|
controls = fields.Many2Many('lims.fraction', None, None, 'Controls',
|
|
required=True, domain=[('id', 'in', Eval('controls_domain'))],
|
|
depends=['controls_domain'])
|
|
controls_domain = fields.Many2Many('lims.fraction', None, None,
|
|
'Controls domain')
|
|
|
|
|
|
class RemoveControl(Wizard):
|
|
'Remove Control'
|
|
__name__ = 'lims.planification.remove_control'
|
|
|
|
start = StateView('lims.planification.remove_control.start',
|
|
'lims.lims_remove_control_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Remove', 'remove', 'tryton-ok', default=True),
|
|
])
|
|
remove = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
planification = Planification(Transaction().context['active_id'])
|
|
controls_domain = []
|
|
for c in planification.controls:
|
|
controls_domain.append(c.id)
|
|
return {
|
|
'controls_domain': controls_domain,
|
|
}
|
|
|
|
def transition_remove(self):
|
|
planification_id = Transaction().context['active_id']
|
|
control_ids = [c.id for c in self.start.controls]
|
|
self._unlink_controls(planification_id, control_ids)
|
|
return 'end'
|
|
|
|
def _unlink_controls(self, planification_id, control_ids):
|
|
pool = Pool()
|
|
PlanificationFraction = pool.get('lims.planification-fraction')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
controls = PlanificationFraction.search([
|
|
('planification', '=', planification_id),
|
|
('fraction', 'in', control_ids),
|
|
])
|
|
if controls:
|
|
PlanificationFraction.delete(controls)
|
|
|
|
controls_details = PlanificationDetail.search([
|
|
('planification', '=', planification_id),
|
|
('fraction', 'in', control_ids),
|
|
])
|
|
if controls_details:
|
|
PlanificationDetail.delete(controls_details)
|
|
|
|
|
|
class AddAnalysisStart(ModelView):
|
|
'Add Analysis'
|
|
__name__ = 'lims.planification.add_analysis.start'
|
|
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory')
|
|
date_from = fields.Date('Date from', required=True, readonly=True)
|
|
date_to = fields.Date('Date to', required=True, readonly=True)
|
|
analysis = fields.Many2Many('lims.analysis', None, None,
|
|
'Analysis/Sets/Groups', required=True,
|
|
domain=[('id', 'in', Eval('analysis_domain'))],
|
|
context={'date_from': Eval('date_from'), 'date_to': Eval('date_to')},
|
|
depends=['analysis_domain', 'date_from', 'date_to'])
|
|
analysis_domain = fields.Many2Many('lims.analysis', None, None,
|
|
'Analysis domain')
|
|
|
|
|
|
class AddAnalysis(Wizard):
|
|
'Add Analysis'
|
|
__name__ = 'lims.planification.add_analysis'
|
|
|
|
start = StateView('lims.planification.add_analysis.start',
|
|
'lims.lims_add_analysis_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
planification = Planification(Transaction().context['active_id'])
|
|
analysis_domain = AddAnalysis._get_analysis_domain(
|
|
planification.laboratory, planification.date_from,
|
|
planification.date_to)
|
|
|
|
return {
|
|
'laboratory': planification.laboratory.id,
|
|
'analysis_domain': analysis_domain,
|
|
'date_from': planification.date_from,
|
|
'date_to': planification.date_to,
|
|
}
|
|
|
|
@staticmethod
|
|
def _get_analysis_domain(laboratory, date_from, date_to):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
if not laboratory:
|
|
return []
|
|
|
|
asg_list = Planification._get_analysis_domain(laboratory)
|
|
|
|
new_context = {}
|
|
new_context['date_from'] = date_from
|
|
new_context['date_to'] = date_to
|
|
with Transaction().set_context(new_context):
|
|
pending_fractions = Analysis.analysis_pending_fractions(asg_list)
|
|
res = []
|
|
for analysis, pending in iter(pending_fractions.items()):
|
|
if pending > 0:
|
|
res.append(analysis)
|
|
return res
|
|
|
|
def transition_add(self):
|
|
Planification = Pool().get('lims.planification')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
Planification.write([planification], {
|
|
'analysis': [('remove', self.start.analysis)],
|
|
})
|
|
Planification.write([planification], {
|
|
'analysis': [('add', self.start.analysis)],
|
|
})
|
|
return 'end'
|
|
|
|
|
|
class SearchFractionsNext(ModelView):
|
|
'Search Fractions'
|
|
__name__ = 'lims.planification.search_fractions.next'
|
|
|
|
details = fields.Many2Many(
|
|
'lims.planification.search_fractions.detail',
|
|
None, None, 'Fractions to plan', depends=['details_domain'],
|
|
domain=[('id', 'in', Eval('details_domain'))], required=True)
|
|
details_domain = fields.One2Many(
|
|
'lims.planification.search_fractions.detail',
|
|
None, 'Fractions domain')
|
|
|
|
|
|
class SearchFractionsDetail(ModelSQL, ModelView):
|
|
'Fraction to Plan'
|
|
__name__ = 'lims.planification.search_fractions.detail'
|
|
_table = 'lims_plan_search_fractions_detail'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
|
|
service_analysis = fields.Many2One('lims.analysis', 'Service',
|
|
readonly=True)
|
|
type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
product_type = fields.Function(fields.Many2One('lims.product.type',
|
|
'Product type'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
|
|
'get_fraction_field', searcher='search_fraction_field')
|
|
create_date2 = fields.Function(fields.DateTime('Create Date'),
|
|
'get_fraction_field', searcher='search_fraction_field')
|
|
urgent = fields.Function(fields.Boolean('Urgent'), 'get_service_field')
|
|
priority = fields.Function(fields.Integer('Priority'), 'get_service_field')
|
|
repetition = fields.Boolean('Repetition', readonly=True)
|
|
report_date = fields.Function(fields.Date('Date agreed for result'),
|
|
'get_service_field')
|
|
completion_percentage = fields.Function(fields.Float('Complete',
|
|
digits=(1, 4)), 'get_completion_percentage')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(SearchFractionsDetail,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(SearchFractionsDetail, cls).__setup__()
|
|
cls._order.insert(0, ('fraction', 'ASC'))
|
|
cls._order.insert(1, ('service_analysis', 'ASC'))
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name in ('label', 'create_date2'):
|
|
for d in details:
|
|
result[name][d.id] = getattr(d.fraction, name, None)
|
|
else:
|
|
for d in details:
|
|
field = getattr(d.fraction, name, None)
|
|
result[name][d.id] = field.id if field else None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_fraction_field(cls, name, clause):
|
|
return [('fraction.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_service_field(cls, details, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'urgent':
|
|
for d in details:
|
|
result[name][d.id] = False
|
|
elif name == 'priority':
|
|
for d in details:
|
|
result[name][d.id] = 0
|
|
else:
|
|
for d in details:
|
|
result[name][d.id] = None
|
|
for d in details:
|
|
if d.fraction and d.service_analysis:
|
|
for service in d.fraction.services:
|
|
if service.analysis == d.service_analysis:
|
|
for name in names:
|
|
result[name][d.id] = getattr(service, name)
|
|
return result
|
|
|
|
@classmethod
|
|
def get_completion_percentage(cls, details, name):
|
|
result = {}
|
|
for d in details:
|
|
result[d.id] = getattr(d.fraction.sample, name, None)
|
|
return result
|
|
|
|
|
|
class SearchFractions(Wizard):
|
|
'Search Fractions'
|
|
__name__ = 'lims.planification.search_fractions'
|
|
|
|
start_state = 'search'
|
|
search = StateTransition()
|
|
next = StateView('lims.planification.search_fractions.next',
|
|
'lims.lims_search_fractions_next_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def transition_search(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
SearchFractionsDetail = pool.get(
|
|
'lims.planification.search_fractions.detail')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
service_detail = PlanificationServiceDetail.search([
|
|
('planification', '=', planification.id),
|
|
('is_control', '=', False),
|
|
('is_replanned', '=', False),
|
|
])
|
|
if service_detail:
|
|
PlanificationServiceDetail.delete(service_detail)
|
|
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', planification.id),
|
|
('details', '=', None),
|
|
])
|
|
if details:
|
|
PlanificationDetail.delete(details)
|
|
|
|
fractions_added = []
|
|
if not planification.analysis:
|
|
self.next.details = fractions_added
|
|
return 'next'
|
|
|
|
data = self._get_service_details(planification)
|
|
|
|
to_create = []
|
|
for k, v in data.items():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'repetition': v['repetition'],
|
|
})
|
|
fractions_added = SearchFractionsDetail.create(to_create)
|
|
|
|
self.next.details = fractions_added
|
|
return 'next'
|
|
|
|
def default_next(self, fields):
|
|
details = [d.id for d in self.next.details]
|
|
self.next.details = None
|
|
return {
|
|
'details': [],
|
|
'details_domain': details,
|
|
}
|
|
|
|
def transition_add(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
records_added = ['(%s,%s)' % (d.fraction.id, d.service_analysis.id)
|
|
for d in self.next.details]
|
|
records_ids_added = ', '.join(str(x)
|
|
for x in ['(0,0)'] + records_added)
|
|
extra_where = (
|
|
'AND (nb.fraction, srv.analysis) IN (' + records_ids_added + ') ')
|
|
|
|
data = self._get_service_details(planification, extra_where)
|
|
|
|
to_create = []
|
|
for k, v in data.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
to_create.append({
|
|
'planification': planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
})
|
|
if to_create:
|
|
PlanificationDetail.create(to_create)
|
|
|
|
return 'end'
|
|
|
|
def _get_service_details(self, planification, extra_where=''):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
planification_details = PlanificationServiceDetail.search([
|
|
('planification.state', '=', 'preplanned'),
|
|
])
|
|
planned_lines = [pd.notebook_line.id for pd in planification_details
|
|
if pd.notebook_line]
|
|
planned_lines_ids = ', '.join(str(x) for x in [0] + planned_lines)
|
|
|
|
result = {}
|
|
nlines_added = []
|
|
for a in planification.analysis:
|
|
analysis_id = a.id
|
|
|
|
all_included_analysis = [analysis_id]
|
|
all_included_analysis.extend(
|
|
Analysis.get_included_analysis_analysis(analysis_id))
|
|
all_included_analysis_ids = ', '.join(str(x)
|
|
for x in all_included_analysis)
|
|
service_where = ('AND ad.analysis IN (' +
|
|
all_included_analysis_ids + ') ')
|
|
|
|
sql_select = (
|
|
'SELECT nl.id, nb.fraction, srv.analysis, nl.repetition != 0 ')
|
|
|
|
sql_from = (
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Analysis._table + '" nla '
|
|
'ON nla.id = nl.analysis '
|
|
'INNER JOIN "' + Notebook._table + '" nb '
|
|
'ON nb.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" frc '
|
|
'ON frc.id = nb.fraction '
|
|
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
|
|
'ON ad.id = nl.analysis_detail '
|
|
'INNER JOIN "' + Service._table + '" srv '
|
|
'ON srv.id = nl.service ')
|
|
|
|
sql_where = (
|
|
'WHERE ad.plannable = TRUE '
|
|
'AND nl.start_date IS NULL '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.laboratory = %s '
|
|
'AND nl.id NOT IN (' + planned_lines_ids + ') '
|
|
'AND nla.behavior != \'internal_relation\' '
|
|
'AND ad.confirmation_date::date >= %s::date '
|
|
'AND ad.confirmation_date::date <= %s::date ' +
|
|
service_where + extra_where)
|
|
|
|
sql_order = (
|
|
'ORDER BY nb.fraction ASC, srv.analysis ASC')
|
|
|
|
with Transaction().set_user(0):
|
|
cursor.execute(sql_select + sql_from + sql_where + sql_order,
|
|
(planification.laboratory.id, planification.date_from,
|
|
planification.date_to,))
|
|
notebook_lines = cursor.fetchall()
|
|
if not notebook_lines:
|
|
continue
|
|
if extra_where:
|
|
for nl in notebook_lines:
|
|
f_ = nl[1]
|
|
s_ = nl[2]
|
|
if (f_, s_) not in result:
|
|
result[(f_, s_)] = []
|
|
if nl[0] not in nlines_added:
|
|
nlines_added.append(nl[0])
|
|
result[(f_, s_)].append({
|
|
'notebook_line': nl[0],
|
|
'planned_service': a.id,
|
|
})
|
|
else:
|
|
for nl in notebook_lines:
|
|
f_ = nl[1]
|
|
s_ = nl[2]
|
|
result[(f_, s_)] = {
|
|
'repetition': nl[3],
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
class SearchPlannedFractionsStart(ModelView):
|
|
'Search Planned Fractions'
|
|
__name__ = 'lims.planification.search_planned_fractions.start'
|
|
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory')
|
|
date_from = fields.Date('Date from', required=True)
|
|
date_to = fields.Date('Date to', required=True)
|
|
analysis = fields.Many2Many('lims.planification-analysis',
|
|
'planification', 'analysis', 'Analysis/Sets/Groups',
|
|
domain=[('id', 'in', Eval('analysis_domain'))],
|
|
depends=['analysis_domain'], required=True)
|
|
analysis_domain = fields.One2Many('lims.analysis', None,
|
|
'Analysis domain')
|
|
|
|
|
|
class SearchPlannedFractionsNext(ModelView):
|
|
'Search Planned Fractions'
|
|
__name__ = 'lims.planification.search_planned_fractions.next'
|
|
|
|
details = fields.Many2Many(
|
|
'lims.planification.search_fractions.detail',
|
|
None, None, 'Fractions to replan', depends=['details_domain'],
|
|
domain=[('id', 'in', Eval('details_domain'))], required=True)
|
|
details_domain = fields.One2Many(
|
|
'lims.planification.search_fractions.detail',
|
|
None, 'Fractions domain')
|
|
|
|
|
|
class SearchPlannedFractions(Wizard):
|
|
'Search Planned Fractions'
|
|
__name__ = 'lims.planification.search_planned_fractions'
|
|
|
|
start = StateView('lims.planification.search_planned_fractions.start',
|
|
'lims.lims_search_planned_fractions_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
next = StateView('lims.planification.search_planned_fractions.next',
|
|
'lims.lims_search_planned_fractions_next_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
planification = Planification(Transaction().context['active_id'])
|
|
analysis = [a.id for a in planification.analysis]
|
|
return {
|
|
'laboratory': planification.laboratory.id,
|
|
'analysis': analysis,
|
|
'analysis_domain': analysis,
|
|
'date_from': planification.date_from,
|
|
'date_to': planification.date_to,
|
|
}
|
|
|
|
def transition_search(self):
|
|
pool = Pool()
|
|
SearchFractionsDetail = pool.get(
|
|
'lims.planification.search_fractions.detail')
|
|
|
|
data = self._get_service_details()
|
|
|
|
to_create = []
|
|
for k, v in data.items():
|
|
to_create.append({
|
|
'session_id': self._session_id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'repetition': v['repetition'],
|
|
})
|
|
fractions_added = SearchFractionsDetail.create(to_create)
|
|
|
|
self.next.details = fractions_added
|
|
return 'next'
|
|
|
|
def default_next(self, fields):
|
|
details = [d.id for d in self.next.details]
|
|
self.next.details = None
|
|
return {
|
|
'details': [],
|
|
'details_domain': details,
|
|
}
|
|
|
|
def transition_add(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
records_added = ['(%s,%s)' % (d.fraction.id, d.service_analysis.id)
|
|
for d in self.next.details]
|
|
records_ids_added = ', '.join(str(x)
|
|
for x in ['(0,0)'] + records_added)
|
|
extra_where = (
|
|
'AND (nb.fraction, srv.analysis) IN (' + records_ids_added + ') ')
|
|
|
|
data = self._get_service_details(extra_where)
|
|
|
|
to_create = []
|
|
for k, v in data.items():
|
|
details = PlanificationDetail.search([
|
|
('planification', '=', planification.id),
|
|
('fraction', '=', k[0]),
|
|
('service_analysis', '=', k[1]),
|
|
])
|
|
if details:
|
|
PlanificationDetail.write([details[0]], {
|
|
'details': [('create', v)],
|
|
})
|
|
else:
|
|
to_create.append({
|
|
'planification': planification.id,
|
|
'fraction': k[0],
|
|
'service_analysis': k[1],
|
|
'details': [('create', v)],
|
|
})
|
|
if to_create:
|
|
PlanificationDetail.create(to_create)
|
|
|
|
return 'end'
|
|
|
|
def _get_service_details(self, extra_where=''):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
result = {}
|
|
nlines_added = []
|
|
for a in self.start.analysis:
|
|
analysis_id = a.id
|
|
|
|
all_included_analysis = [analysis_id]
|
|
all_included_analysis.extend(
|
|
Analysis.get_included_analysis_analysis(analysis_id))
|
|
all_included_analysis_ids = ', '.join(str(x)
|
|
for x in all_included_analysis)
|
|
service_where = ('AND ad.analysis IN (' +
|
|
all_included_analysis_ids + ') ')
|
|
|
|
excluded_fractions = self.get_control_fractions_excluded(
|
|
self.start.laboratory.id, service_where)
|
|
excluded_fractions_ids = ', '.join(str(x)
|
|
for x in [0] + excluded_fractions)
|
|
|
|
sql_select = (
|
|
'SELECT nl.id, nb.fraction, srv.analysis, nl.repetition != 0 ')
|
|
|
|
sql_from = (
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Analysis._table + '" nla '
|
|
'ON nla.id = nl.analysis '
|
|
'INNER JOIN "' + Notebook._table + '" nb '
|
|
'ON nb.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" frc '
|
|
'ON frc.id = nb.fraction '
|
|
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
|
|
'ON ad.id = nl.analysis_detail '
|
|
'INNER JOIN "' + Service._table + '" srv '
|
|
'ON srv.id = nl.service ')
|
|
|
|
sql_where = (
|
|
'WHERE ad.plannable = TRUE '
|
|
'AND nl.start_date IS NOT NULL '
|
|
'AND nl.end_date IS NULL '
|
|
'AND nl.laboratory = %s '
|
|
'AND nla.behavior != \'internal_relation\' '
|
|
'AND nb.fraction NOT IN (' + excluded_fractions_ids + ') '
|
|
'AND ad.confirmation_date::date >= %s::date '
|
|
'AND ad.confirmation_date::date <= %s::date ' +
|
|
service_where + extra_where)
|
|
|
|
sql_order = (
|
|
'ORDER BY nb.fraction ASC, srv.analysis ASC')
|
|
|
|
with Transaction().set_user(0):
|
|
cursor.execute(sql_select + sql_from + sql_where + sql_order,
|
|
(self.start.laboratory.id, self.start.date_from,
|
|
self.start.date_to,))
|
|
notebook_lines = cursor.fetchall()
|
|
if notebook_lines:
|
|
if extra_where:
|
|
for nl in notebook_lines:
|
|
f_ = nl[1]
|
|
s_ = nl[2]
|
|
if (f_, s_) not in result:
|
|
result[(f_, s_)] = []
|
|
if nl[0] not in nlines_added:
|
|
nlines_added.append(nl[0])
|
|
result[(f_, s_)].append({
|
|
'notebook_line': nl[0],
|
|
'planned_service': a.id,
|
|
'is_replanned': True,
|
|
})
|
|
else:
|
|
for nl in notebook_lines:
|
|
f_ = nl[1]
|
|
s_ = nl[2]
|
|
result[(f_, s_)] = {
|
|
'repetition': nl[3],
|
|
}
|
|
|
|
return result
|
|
|
|
def get_control_fractions_excluded(self, laboratory, search_clause):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
Config = pool.get('lims.configuration')
|
|
|
|
config = Config(1)
|
|
special_types = []
|
|
if config.rm_fraction_type:
|
|
special_types.append(config.rm_fraction_type.id)
|
|
if config.bmz_fraction_type:
|
|
special_types.append(config.bmz_fraction_type.id)
|
|
if config.bre_fraction_type:
|
|
special_types.append(config.bre_fraction_type.id)
|
|
if config.mrt_fraction_type:
|
|
special_types.append(config.mrt_fraction_type.id)
|
|
special_types_ids = ', '.join(str(x) for x in [0] + special_types)
|
|
|
|
cursor.execute('SELECT nl.analysis, nl.notebook '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" nb '
|
|
'ON nb.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" frc '
|
|
'ON frc.id = nb.fraction '
|
|
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
|
|
'ON ad.id = nl.analysis_detail '
|
|
'WHERE frc.type IN (' + special_types_ids + ') '
|
|
'AND nl.start_date IS NOT NULL '
|
|
'AND nl.end_date IS NULL '
|
|
'AND nl.laboratory = %s ' +
|
|
search_clause, (laboratory,))
|
|
notebook_lines = cursor.fetchall()
|
|
if not notebook_lines:
|
|
return []
|
|
|
|
excluded_fractions = []
|
|
analysis = []
|
|
notebooks_id = []
|
|
for nbl in notebook_lines:
|
|
analysis.append(nbl[0])
|
|
notebooks_id.append(nbl[1])
|
|
analysis = set(analysis)
|
|
|
|
notebooks = Notebook.search([
|
|
('id', 'in', list(set(notebooks_id))),
|
|
])
|
|
for nb in notebooks:
|
|
cursor.execute('SELECT analysis '
|
|
'FROM "' + NotebookLine._table + '" '
|
|
'WHERE notebook = %s', (nb.id,))
|
|
nbl_analysis_ids = set(cursor.fetchall())
|
|
if not analysis.issubset(nbl_analysis_ids):
|
|
excluded_fractions.append(nb.fraction.id)
|
|
return list(set(excluded_fractions))
|
|
|
|
|
|
class CreateFractionControlStart(ModelView):
|
|
'Create Fraction Control'
|
|
__name__ = 'lims.planification.create_fraction_con.start'
|
|
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
|
|
required=True)
|
|
type = fields.Selection([
|
|
('coi', 'COI'),
|
|
('mrc', 'MRC'),
|
|
('sla', 'SLA'),
|
|
], 'Control type', sort=False, required=True)
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
required=True,
|
|
domain=[('id', 'in', Eval('product_type_domain'))],
|
|
depends=['product_type_domain'])
|
|
product_type_domain = fields.Function(fields.Many2Many(
|
|
'lims.product.type', None, None, 'Product type domain'),
|
|
'on_change_with_product_type_domain')
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
|
|
domain=[('id', 'in', Eval('matrix_domain'))],
|
|
depends=['matrix_domain'])
|
|
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
|
|
None, None, 'Matrix domain'),
|
|
'on_change_with_matrix_domain')
|
|
analysis = fields.Many2Many('lims.planification-analysis',
|
|
'planification', 'analysis', 'Analysis/Sets/Groups',
|
|
domain=[('id', 'in', Eval('analysis_domain'))],
|
|
depends=['analysis_domain'])
|
|
analysis_domain = fields.Function(fields.One2Many('lims.analysis',
|
|
None, 'Analysis domain'), 'on_change_with_analysis_domain')
|
|
label = fields.Char('Label')
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', states={
|
|
'invisible': Bool(Eval('concentration_level_invisible')),
|
|
}, depends=['concentration_level_invisible'])
|
|
concentration_level_invisible = fields.Boolean(
|
|
'Concentration level invisible')
|
|
sample_created = fields.Many2One('lims.sample', 'Sample created')
|
|
|
|
@fields.depends('type')
|
|
def on_change_with_concentration_level_invisible(self, name=None):
|
|
Config = Pool().get('lims.configuration')
|
|
config = Config(1)
|
|
if self.type == 'coi':
|
|
if (config.coi_fraction_type and
|
|
config.coi_fraction_type.control_charts):
|
|
return False
|
|
elif self.type == 'mrc':
|
|
if (config.mrc_fraction_type and
|
|
config.mrc_fraction_type.control_charts):
|
|
return False
|
|
elif self.type == 'sla':
|
|
if (config.sla_fraction_type and
|
|
config.sla_fraction_type.control_charts):
|
|
return False
|
|
return True
|
|
|
|
@fields.depends('laboratory', 'product_type', 'matrix')
|
|
def on_change_with_analysis_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
|
|
Analysis = pool.get('lims.analysis')
|
|
Typification = pool.get('lims.typification')
|
|
CalculatedTypification = pool.get('lims.typification.calculated')
|
|
|
|
if not self.laboratory or not self.product_type or not self.matrix:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(analysis) '
|
|
'FROM "' + AnalysisLaboratory._table + '" '
|
|
'WHERE laboratory = %s', (self.laboratory.id,))
|
|
analysis_sets_list = [a[0] for a in cursor.fetchall()]
|
|
if not analysis_sets_list:
|
|
return []
|
|
lab_analysis_ids = ', '.join(str(a) for a in
|
|
analysis_sets_list)
|
|
|
|
groups_list = []
|
|
groups = Analysis.search([
|
|
('type', '=', 'group'),
|
|
])
|
|
if groups:
|
|
for group in groups:
|
|
available = True
|
|
|
|
ia = Analysis.get_included_analysis_analysis(
|
|
group.id)
|
|
if not ia:
|
|
continue
|
|
included_ids = ', '.join(str(a) for a in ia)
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + Analysis._table + '" '
|
|
'WHERE id IN (' + included_ids + ') '
|
|
'AND id NOT IN (' + lab_analysis_ids +
|
|
')')
|
|
if cursor.fetchone():
|
|
available = False
|
|
|
|
if available:
|
|
groups_list.append(group.id)
|
|
|
|
analysis_domain = analysis_sets_list + groups_list
|
|
analysis_domain_ids = ', '.join(str(a) for a in analysis_domain)
|
|
|
|
cursor.execute('SELECT DISTINCT(typ.analysis) '
|
|
'FROM ('
|
|
'SELECT t.analysis FROM "' + Typification._table + '" t '
|
|
'WHERE t.product_type = %s AND t.matrix = %s AND t.valid '
|
|
'UNION '
|
|
'SELECT ct.analysis FROM "' + CalculatedTypification._table +
|
|
'" ct '
|
|
'WHERE ct.product_type = %s AND ct.matrix = %s'
|
|
') AS typ '
|
|
'WHERE typ.analysis IN (' + analysis_domain_ids + ')',
|
|
(self.product_type.id, self.matrix.id,
|
|
self.product_type.id, self.matrix.id))
|
|
typified_analysis = [a[0] for a in cursor.fetchall()]
|
|
return typified_analysis
|
|
|
|
@staticmethod
|
|
def default_product_type_domain():
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
cursor.execute('SELECT DISTINCT(product_type) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE valid')
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
def on_change_with_product_type_domain(self, name=None):
|
|
return self.default_product_type_domain()
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_product_type(self):
|
|
matrix = None
|
|
if self.product_type:
|
|
matrixs = self.on_change_with_matrix_domain()
|
|
if len(matrixs) == 1:
|
|
matrix = matrixs[0]
|
|
self.matrix = matrix
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_with_matrix_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
if not self.product_type:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(matrix) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND valid',
|
|
(self.product_type.id,))
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
@fields.depends('type', 'product_type', 'matrix',
|
|
'_parent_product_type.code', '_parent_matrix.code')
|
|
def on_change_with_label(self, name=None):
|
|
label = ''
|
|
if self.type == 'coi':
|
|
label += 'COI'
|
|
elif self.type == 'mrc':
|
|
label += 'MRC'
|
|
elif self.type == 'sla':
|
|
label += 'SLA'
|
|
if self.product_type:
|
|
label += ' ' + self.product_type.code
|
|
if self.matrix:
|
|
label += ' ' + self.matrix.code
|
|
return label
|
|
|
|
|
|
class CreateFractionControl(Wizard):
|
|
'Create Fraction Control'
|
|
__name__ = 'lims.planification.create_fraction_con'
|
|
|
|
start = StateView('lims.planification.create_fraction_con.start',
|
|
'lims.lims_create_fraction_con_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Create', 'create_', 'tryton-ok', default=True),
|
|
])
|
|
create_ = StateTransition()
|
|
open_ = StateAction('lims.act_lims_sample_list')
|
|
|
|
def default_start(self, fields):
|
|
defaults = {
|
|
'laboratory': Transaction().context.get('laboratory', None),
|
|
'concentration_level_invisible': True,
|
|
}
|
|
return defaults
|
|
|
|
def transition_create_(self):
|
|
control = self.create_control()
|
|
self.start.sample_created = control.sample.id
|
|
return 'open_'
|
|
|
|
def create_control(self):
|
|
pool = Pool()
|
|
Config = pool.get('lims.configuration')
|
|
LabWorkYear = pool.get('lims.lab.workyear')
|
|
Entry = pool.get('lims.entry')
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
config = Config(1)
|
|
if self.start.type == 'coi':
|
|
if not config.coi_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_coi_fraction_type'))
|
|
fraction_type = config.coi_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext(
|
|
'lims.msg_no_coi_default_configuration'))
|
|
elif self.start.type == 'mrc':
|
|
if not config.mrc_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_mrc_fraction_type'))
|
|
fraction_type = config.mrc_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext(
|
|
'lims.msg_no_mrc_default_configuration'))
|
|
elif self.start.type == 'sla':
|
|
if not config.sla_fraction_type:
|
|
raise UserError(gettext('lims.msg_no_sla_fraction_type'))
|
|
fraction_type = config.sla_fraction_type
|
|
if (not fraction_type.default_package_type or
|
|
not fraction_type.default_fraction_state):
|
|
raise UserError(gettext(
|
|
'lims.msg_no_sla_default_configuration'))
|
|
|
|
if (fraction_type.control_charts and not
|
|
self.start.concentration_level):
|
|
raise UserError(gettext('lims.msg_no_concentration_level'))
|
|
|
|
workyear_id = LabWorkYear.find()
|
|
workyear = LabWorkYear(workyear_id)
|
|
if not workyear.default_entry_control:
|
|
raise UserError(gettext('lims.msg_no_entry_control'))
|
|
|
|
laboratory = self.start.laboratory
|
|
entry = Entry(workyear.default_entry_control.id)
|
|
obj_description = self._get_obj_description(self.start)
|
|
|
|
# new sample
|
|
new_sample, = Sample.create([{
|
|
'entry': entry.id,
|
|
'date': datetime.now(),
|
|
'product_type': self.start.product_type.id,
|
|
'matrix': self.start.matrix.id,
|
|
'zone': entry.party.entry_zone.id,
|
|
'label': self.start.label,
|
|
'obj_description': obj_description,
|
|
'packages_quantity': 1,
|
|
'fractions': [],
|
|
}])
|
|
|
|
# new fraction
|
|
fraction_default = {
|
|
'sample': new_sample.id,
|
|
'type': fraction_type.id,
|
|
'storage_location': laboratory.related_location.id,
|
|
'packages_quantity': 1,
|
|
'package_type': fraction_type.default_package_type.id,
|
|
'fraction_state': fraction_type.default_fraction_state.id,
|
|
'services': [],
|
|
'con_type': self.start.type,
|
|
}
|
|
if fraction_type.max_storage_time:
|
|
fraction_default['storage_time'] = fraction_type.max_storage_time
|
|
elif laboratory.related_location.storage_time:
|
|
fraction_default['storage_time'] = (
|
|
laboratory.related_location.storage_time)
|
|
else:
|
|
fraction_default['storage_time'] = 3
|
|
new_fraction, = Fraction.create([fraction_default])
|
|
|
|
# new services
|
|
services_default = []
|
|
for p_analysis in self.start.analysis:
|
|
laboratory_id = (laboratory.id if p_analysis.type != 'group'
|
|
else None)
|
|
method_id = None
|
|
if new_sample.typification_domain:
|
|
for t in new_sample.typification_domain:
|
|
if (t.analysis.id == p_analysis.id and
|
|
t.by_default is True):
|
|
method_id = t.method.id
|
|
device_id = None
|
|
if p_analysis.devices:
|
|
for d in p_analysis.devices:
|
|
if (d.laboratory.id == laboratory.id and
|
|
d.by_default is True):
|
|
device_id = d.device.id
|
|
services_default.append({
|
|
'fraction': new_fraction.id,
|
|
'analysis': p_analysis.id,
|
|
'laboratory': laboratory_id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
})
|
|
for service in services_default:
|
|
new_service, = Service.create([service])
|
|
|
|
# new analysis details (on service create)
|
|
|
|
# confirm fraction: new notebook and stock move
|
|
Fraction.confirm([new_fraction])
|
|
|
|
# Edit notebook lines
|
|
if fraction_type.control_charts:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook.fraction', '=', new_fraction.id),
|
|
])
|
|
if notebook_lines:
|
|
defaults = {
|
|
'concentration_level': self.start.concentration_level.id,
|
|
}
|
|
NotebookLine.write(notebook_lines, defaults)
|
|
|
|
return new_fraction
|
|
|
|
def _get_obj_description(self, sample):
|
|
cursor = Transaction().connection.cursor()
|
|
ObjectiveDescription = Pool().get('lims.objective_description')
|
|
|
|
if not sample.product_type or not sample.matrix:
|
|
return None
|
|
|
|
cursor.execute('SELECT id '
|
|
'FROM "' + ObjectiveDescription._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND matrix = %s',
|
|
(sample.product_type.id, sample.matrix.id))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
def do_open_(self, action):
|
|
action['pyson_domain'] = PYSONEncoder().encode([
|
|
('id', '=', self.start.sample_created.id),
|
|
])
|
|
return action, {}
|
|
|
|
|
|
class ReleaseFractionStart(ModelView):
|
|
'Release Fraction'
|
|
__name__ = 'lims.planification.release_fraction.start'
|
|
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
|
|
required=True)
|
|
date_from = fields.Date('Date from', required=True)
|
|
date_to = fields.Date('Date to', required=True)
|
|
|
|
|
|
class ReleaseFractionEmpty(ModelView):
|
|
'Release Fraction'
|
|
__name__ = 'lims.planification.release_fraction.empty'
|
|
|
|
|
|
class ReleaseFractionResult(ModelView):
|
|
'Release Fraction'
|
|
__name__ = 'lims.planification.release_fraction.result'
|
|
|
|
fractions = fields.Many2Many('lims.planification.detail', None, None,
|
|
'Fractions', required=True,
|
|
domain=[('id', 'in', Eval('fractions_domain'))],
|
|
depends=['fractions_domain'])
|
|
fractions_domain = fields.One2Many('lims.planification.detail', None,
|
|
'Fractions domain')
|
|
|
|
|
|
class ReleaseFraction(Wizard):
|
|
'Release Fraction'
|
|
__name__ = 'lims.planification.release_fraction'
|
|
|
|
start = StateView('lims.planification.release_fraction.start',
|
|
'lims.lims_planification_release_fraction_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
empty = StateView('lims.planification.release_fraction.empty',
|
|
'lims.lims_planification_release_fraction_empty_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search again', 'start', 'tryton-forward', default=True),
|
|
])
|
|
result = StateView('lims.planification.release_fraction.result',
|
|
'lims.lims_planification_release_fraction_result_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Release', 'release', 'tryton-ok', default=True),
|
|
])
|
|
release = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
res = {}
|
|
for field in ('date_from', 'date_to'):
|
|
if (hasattr(self.start, field) and getattr(self.start, field)):
|
|
res[field] = getattr(self.start, field)
|
|
if (hasattr(self.start, 'laboratory') and
|
|
getattr(self.start, 'laboratory')):
|
|
res['laboratory'] = getattr(self.start, 'laboratory').id
|
|
else:
|
|
res['laboratory'] = Transaction().context.get('laboratory', None)
|
|
return res
|
|
|
|
def transition_search(self):
|
|
PlanificationDetail = Pool().get('lims.planification.detail')
|
|
|
|
details = PlanificationDetail.search([
|
|
('planification.laboratory', '=', self.start.laboratory.id),
|
|
('planification.start_date', '>=', self.start.date_from),
|
|
('planification.start_date', '<=', self.start.date_to),
|
|
('planification.state', '!=', 'draft'),
|
|
])
|
|
if details:
|
|
fractions = []
|
|
for detail in details:
|
|
available = True
|
|
for service_detail in detail.details:
|
|
if service_detail.is_control:
|
|
available = False
|
|
break
|
|
nline = service_detail.notebook_line
|
|
if not nline:
|
|
available = False
|
|
break
|
|
if (nline.result or nline.converted_result or
|
|
nline.literal_result or nline.end_date or
|
|
nline.annulment_date):
|
|
available = False
|
|
break
|
|
if available:
|
|
fractions.append(detail)
|
|
|
|
if fractions:
|
|
self.result.fractions = fractions
|
|
return 'result'
|
|
return 'empty'
|
|
|
|
def default_result(self, fields):
|
|
fractions = [f.id for f in self.result.fractions]
|
|
self.result.fractions = None
|
|
return {
|
|
'fractions_domain': fractions,
|
|
}
|
|
|
|
def transition_release(self):
|
|
self._re_update_laboratory_notebook()
|
|
self._re_update_analysis_detail()
|
|
self._unlink_fractions()
|
|
return 'end'
|
|
|
|
def _re_update_laboratory_notebook(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
for detail in self.result.fractions:
|
|
for service_detail in detail.details:
|
|
if (not service_detail.is_control and
|
|
service_detail.notebook_line):
|
|
notebook_line = NotebookLine(
|
|
service_detail.notebook_line.id)
|
|
notebook_line.start_date = None
|
|
notebook_line.laboratory_professionals = []
|
|
notebook_line.planification = None
|
|
notebook_line.controls = []
|
|
notebook_line.save()
|
|
|
|
def _re_update_analysis_detail(self):
|
|
EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
|
|
analysis_detail_ids = []
|
|
for detail in self.result.fractions:
|
|
for service_detail in detail.details:
|
|
if (not service_detail.is_control and
|
|
service_detail.notebook_line and
|
|
service_detail.notebook_line.analysis_detail):
|
|
analysis_detail_ids.append(
|
|
service_detail.notebook_line.analysis_detail.id)
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', analysis_detail_ids),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'unplanned',
|
|
})
|
|
|
|
def _unlink_fractions(self):
|
|
pool = Pool()
|
|
PlanificationServiceDetail = Pool().get(
|
|
'lims.planification.service_detail')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
|
|
details_ids = []
|
|
service_details_ids = []
|
|
for detail in self.result.fractions:
|
|
for service_detail in detail.details:
|
|
if not service_detail.is_control:
|
|
service_details_ids.append(service_detail.id)
|
|
details_ids.append(service_detail.detail.id)
|
|
|
|
service_detail = PlanificationServiceDetail.search([
|
|
('id', 'in', service_details_ids),
|
|
])
|
|
if service_detail:
|
|
PlanificationServiceDetail.delete(service_detail)
|
|
|
|
details = PlanificationDetail.search([
|
|
('id', 'in', details_ids),
|
|
('details', '=', None),
|
|
])
|
|
if details:
|
|
PlanificationDetail.delete(details)
|
|
|
|
|
|
class QualificationSituations(ModelView):
|
|
'Technicians Qualification'
|
|
__name__ = 'lims.planification.qualification.situations'
|
|
|
|
situations = fields.Many2Many('lims.planification.qualification.situation',
|
|
None, None, 'Situations')
|
|
total = fields.Integer('Total')
|
|
index = fields.Integer('Index')
|
|
|
|
|
|
class QualificationSituation(ModelSQL, ModelView):
|
|
'Qualification Situation'
|
|
__name__ = 'lims.planification.qualification.situation'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional', readonly=True)
|
|
situation = fields.Integer('Situation', readonly=True)
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(QualificationSituation,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class QualificationAction(ModelSQL):
|
|
'Qualification Action'
|
|
__name__ = 'lims.planification.qualification.action'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method')
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional')
|
|
action = fields.Integer('Action')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super(QualificationAction,
|
|
cls).__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class QualificationSituation2(ModelView):
|
|
'Technicians Qualification'
|
|
__name__ = 'lims.planification.qualification.situation.2'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional', readonly=True)
|
|
|
|
|
|
class QualificationSituation3(ModelView):
|
|
'Technicians Qualification'
|
|
__name__ = 'lims.planification.qualification.situation.3'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional', readonly=True)
|
|
|
|
|
|
class QualificationSituation4(ModelView):
|
|
'Technicians Qualification'
|
|
__name__ = 'lims.planification.qualification.situation.4'
|
|
|
|
methods = fields.Text('Methods', readonly=True)
|
|
|
|
|
|
class TechniciansQualification(Wizard):
|
|
'Technicians Qualification'
|
|
__name__ = 'lims.planification.technicians_qualification'
|
|
|
|
situations = StateView('lims.planification.qualification.situations',
|
|
'lims.lims_qualification_situations_view_form', [])
|
|
start = StateTransition()
|
|
next_ = StateTransition()
|
|
confirm = StateTransition()
|
|
|
|
sit2 = StateView('lims.planification.qualification.situation.2',
|
|
'lims.lims_qualification_situation_2_view_form', [
|
|
Button('Qualify', 'sit2_op1', 'tryton-ok', default=True),
|
|
Button('New Training', 'sit2_op2', 'tryton-ok'),
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
])
|
|
sit2_op1 = StateTransition()
|
|
sit2_op2 = StateTransition()
|
|
|
|
sit3 = StateView('lims.planification.qualification.situation.3',
|
|
'lims.lims_qualification_situation_3_view_form', [
|
|
Button('Requalify', 'sit3_op1', 'tryton-ok', default=True),
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
])
|
|
sit3_op1 = StateTransition()
|
|
|
|
sit4 = StateView('lims.planification.qualification.situation.4',
|
|
'lims.lims_qualification_situation_4_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
])
|
|
|
|
def transition_start(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
QualificationSituation = pool.get(
|
|
'lims.planification.qualification.situation')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
planification_details = PlanificationServiceDetail.search([
|
|
('planification', '=', planification.id),
|
|
])
|
|
|
|
situations = {}
|
|
for detail in planification_details:
|
|
method = (detail.notebook_line.method if detail.notebook_line
|
|
else None)
|
|
if method:
|
|
for technician in detail.staff_responsible:
|
|
key = (technician.id, method.id)
|
|
if key in situations:
|
|
continue
|
|
situations[key] = 0
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', technician.id),
|
|
('method', '=', method.id),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
if not qualifications:
|
|
situations[key] = 1
|
|
else:
|
|
if qualifications[0].state == 'training':
|
|
situations[key] = 2
|
|
elif (qualifications[0].state in
|
|
('qualified', 'requalified')):
|
|
situations[key] = 3
|
|
|
|
if situations:
|
|
self.situations.situations = QualificationSituation.create([{
|
|
'session_id': self._session_id,
|
|
'professional': k[0],
|
|
'method': k[1],
|
|
'situation': v,
|
|
} for k, v in situations.items()])
|
|
self.situations.total = len(self.situations.situations)
|
|
self.situations.index = 0
|
|
return 'next_'
|
|
return self._confirm()
|
|
|
|
def transition_next_(self):
|
|
|
|
data = self.situations.situations[self.situations.index]
|
|
if data.situation == 1:
|
|
return self.situation_1(data)
|
|
elif data.situation == 2:
|
|
return self.situation_2(data)
|
|
elif data.situation == 3:
|
|
return self.situation_3(data)
|
|
return self._continue()
|
|
|
|
def situation_1(self, data):
|
|
# The technician has not the method. Write a new training directly.
|
|
QualificationAction = Pool().get(
|
|
'lims.planification.qualification.action')
|
|
|
|
QualificationAction.create([{
|
|
'session_id': self._session_id,
|
|
'professional': data.professional.id,
|
|
'method': data.method.id,
|
|
'action': 1,
|
|
}])
|
|
return self._continue()
|
|
|
|
def situation_2(self, data):
|
|
# The technician has received at least one training. Ask if qualify
|
|
# or write a new training.
|
|
self.sit2.professional = data.professional.id
|
|
self.sit2.method = data.method.id
|
|
return 'sit2'
|
|
|
|
def default_sit2(self, fields):
|
|
professional = self.sit2.professional.id
|
|
method = self.sit2.method.id
|
|
self.sit2.professional = None
|
|
self.sit2.method = None
|
|
return {
|
|
'professional': professional,
|
|
'method': method,
|
|
}
|
|
|
|
def transition_sit2_op1(self):
|
|
# Qualify the technician
|
|
QualificationAction = Pool().get(
|
|
'lims.planification.qualification.action')
|
|
|
|
QualificationAction.create([{
|
|
'session_id': self._session_id,
|
|
'professional': self.sit2.professional.id,
|
|
'method': self.sit2.method.id,
|
|
'action': 2,
|
|
}])
|
|
return self._continue()
|
|
|
|
def transition_sit2_op2(self):
|
|
# Write a new training.
|
|
QualificationAction = Pool().get(
|
|
'lims.planification.qualification.action')
|
|
|
|
QualificationAction.create([{
|
|
'session_id': self._session_id,
|
|
'professional': self.sit2.professional.id,
|
|
'method': self.sit2.method.id,
|
|
'action': 3,
|
|
}])
|
|
return self._continue()
|
|
|
|
def situation_3(self, data):
|
|
# The technician is qualified. Check the last execution date of the
|
|
# method; if it's expired, ask if requalify or cancel the process.
|
|
# If it is valid, write a new execution
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
QualificationAction = pool.get(
|
|
'lims.planification.qualification.action')
|
|
|
|
deadline = Date.today() - relativedelta(
|
|
months=data.method.requalification_months)
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', data.professional),
|
|
('method', '=', data.method),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
last_execution = date.min
|
|
for requalification in professional_method.requalification_history:
|
|
if (requalification.last_execution_date and
|
|
last_execution < requalification.last_execution_date):
|
|
last_execution = requalification.last_execution_date
|
|
|
|
if last_execution < deadline:
|
|
self.sit3.professional = data.professional.id
|
|
self.sit3.method = data.method.id
|
|
return 'sit3'
|
|
else:
|
|
QualificationAction.create([{
|
|
'session_id': self._session_id,
|
|
'professional': data.professional.id,
|
|
'method': data.method.id,
|
|
'action': 5,
|
|
}])
|
|
return self._continue()
|
|
|
|
def default_sit3(self, fields):
|
|
professional = self.sit3.professional.id
|
|
method = self.sit3.method.id
|
|
self.sit3.professional = None
|
|
self.sit3.method = None
|
|
return {
|
|
'professional': professional,
|
|
'method': method,
|
|
}
|
|
|
|
def transition_sit3_op1(self):
|
|
# Requalify the technician
|
|
QualificationAction = Pool().get(
|
|
'lims.planification.qualification.action')
|
|
|
|
QualificationAction.create([{
|
|
'session_id': self._session_id,
|
|
'professional': self.sit3.professional.id,
|
|
'method': self.sit3.method.id,
|
|
'action': 4,
|
|
}])
|
|
return self._continue()
|
|
|
|
def situation_4(self, data):
|
|
# The method has no qualified technician
|
|
self.sit4.methods = data['methods']
|
|
return 'sit4'
|
|
|
|
def default_sit4(self, fields):
|
|
methods = self.sit4.methods
|
|
self.sit4.methods = None
|
|
return {
|
|
'methods': methods,
|
|
}
|
|
|
|
def _continue(self):
|
|
self.situations.index += 1
|
|
if self.situations.index < self.situations.total:
|
|
return 'next_'
|
|
return self._confirm()
|
|
|
|
def _confirm(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
QualificationAction = pool.get(
|
|
'lims.planification.qualification.action')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
planification_details = PlanificationServiceDetail.search([
|
|
('planification', '=', planification.id),
|
|
])
|
|
methods = []
|
|
for detail in planification_details:
|
|
method = (detail.notebook_line.method if detail.notebook_line
|
|
else None)
|
|
if method:
|
|
qualified = False
|
|
for technician in detail.staff_responsible:
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', technician.id),
|
|
('method', '=', method.id),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
if (qualifications and qualifications[0].state in
|
|
('qualified', 'requalified')):
|
|
if not method.supervised_requalification:
|
|
qualified = True
|
|
break
|
|
actions = QualificationAction.search([
|
|
('session_id', '=', self._session_id),
|
|
('professional', '=', technician.id),
|
|
('method', '=', method.id),
|
|
('action', '=', 4),
|
|
])
|
|
if not actions:
|
|
qualified = True
|
|
break
|
|
if not qualified:
|
|
methods.append('[%s] %s' % (method.code, method.name))
|
|
|
|
if methods:
|
|
return self.situation_4({'methods': '\n'.join(list(set(methods)))})
|
|
|
|
actions = QualificationAction.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
if actions:
|
|
controls = self._get_controls()
|
|
start_date = planification.start_date
|
|
for data in actions:
|
|
if data.action == 1:
|
|
self.action_1(data, controls, start_date)
|
|
elif data.action == 2:
|
|
self.action_2(data, controls, start_date)
|
|
elif data.action == 3:
|
|
self.action_3(data, controls, start_date)
|
|
elif data.action == 4:
|
|
self.action_4(data, controls, start_date)
|
|
elif data.action == 5:
|
|
self.action_5(data, controls, start_date)
|
|
return 'confirm'
|
|
|
|
def action_1(self, data, controls, start_date):
|
|
# The technician has not the method. Write a new training directly.
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
|
|
supervisors = self._get_supervisors(data)
|
|
requalification_history = [{
|
|
'type': 'training',
|
|
'date': Date.today(),
|
|
'last_execution_date': start_date,
|
|
'supervisors': [('create', supervisors)],
|
|
'controls': [('create', controls)],
|
|
}]
|
|
professional_method, = LabProfessionalMethod.create([{
|
|
'professional': data.professional.id,
|
|
'method': data.method.id,
|
|
'state': 'training',
|
|
'type': 'preparation',
|
|
'requalification_history': [('create', requalification_history)],
|
|
}])
|
|
|
|
def action_2(self, data, controls, start_date):
|
|
# Qualify the technician
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', data.professional),
|
|
('method', '=', data.method),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
supervisors = self._get_supervisors(data)
|
|
requalification_history = [{
|
|
'type': 'qualification',
|
|
'date': Date.today(),
|
|
'last_execution_date': start_date,
|
|
'supervisors': [('create', supervisors)],
|
|
'controls': [('create', controls)],
|
|
}]
|
|
LabProfessionalMethod.write([professional_method], {
|
|
'state': 'qualified',
|
|
'requalification_history': [('create', requalification_history)],
|
|
})
|
|
|
|
def action_3(self, data, controls, start_date):
|
|
# Write a new training
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', data.professional),
|
|
('method', '=', data.method),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
supervisors = self._get_supervisors(data)
|
|
requalification_history = [{
|
|
'type': 'training',
|
|
'date': Date.today(),
|
|
'last_execution_date': start_date,
|
|
'supervisors': [('create', supervisors)],
|
|
'controls': [('create', controls)],
|
|
}]
|
|
LabProfessionalMethod.write([professional_method], {
|
|
'requalification_history': [('create', requalification_history)],
|
|
})
|
|
|
|
def action_4(self, data, controls, start_date):
|
|
# Requalify the technician
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', data.professional),
|
|
('method', '=', data.method),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
supervisors = self._get_supervisors(data)
|
|
requalification_history = [{
|
|
'type': 'requalification',
|
|
'date': Date.today(),
|
|
'last_execution_date': start_date,
|
|
'supervisors': [('create', supervisors)],
|
|
'controls': [('create', controls)],
|
|
}]
|
|
LabProfessionalMethod.write([professional_method], {
|
|
'state': 'requalified',
|
|
'requalification_history': [('create', requalification_history)],
|
|
})
|
|
|
|
def action_5(self, data, controls, start_date):
|
|
# Write a new execution
|
|
pool = Pool()
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
Date = pool.get('ir.date')
|
|
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', data.professional),
|
|
('method', '=', data.method),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
supervisors = self._get_supervisors(data)
|
|
requalification_history = [{
|
|
'type': ('qualification'
|
|
if professional_method.state == 'qualified'
|
|
else 'requalification'),
|
|
'date': Date.today(),
|
|
'last_execution_date': start_date,
|
|
'supervisors': [('create', supervisors)],
|
|
'controls': [('create', controls)],
|
|
}]
|
|
LabProfessionalMethod.write([professional_method], {
|
|
'requalification_history': [('create', requalification_history)],
|
|
})
|
|
|
|
def transition_confirm(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
Config = pool.get('lims.configuration')
|
|
process_background = Config(1).planification_process_background
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
planification.state = 'confirmed'
|
|
if process_background:
|
|
planification.waiting_process = True
|
|
planification.save()
|
|
|
|
if not process_background:
|
|
Planification.do_confirm([planification])
|
|
return 'end'
|
|
|
|
def _get_supervisors(self, data):
|
|
pool = Pool()
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
QualificationAction = pool.get(
|
|
'lims.planification.qualification.action')
|
|
|
|
planification_id = Transaction().context['active_id']
|
|
supervisors = []
|
|
|
|
planification_details = PlanificationServiceDetail.search([
|
|
('planification', '=', planification_id),
|
|
('notebook_line.method', '=', data.method.id),
|
|
])
|
|
for detail in planification_details:
|
|
for technician in detail.staff_responsible:
|
|
if technician.id == data.professional.id:
|
|
continue
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', technician.id),
|
|
('method', '=', data.method.id),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
if (qualifications and qualifications[0].state in
|
|
('qualified', 'requalified')):
|
|
if not data.method.supervised_requalification:
|
|
supervisors.append(technician.id)
|
|
else:
|
|
actions = QualificationAction.search([
|
|
('session_id', '=', self._session_id),
|
|
('professional', '=', technician.id),
|
|
('method', '=', data.method.id),
|
|
('action', '=', 4),
|
|
])
|
|
if not actions:
|
|
supervisors.append(technician.id)
|
|
|
|
return [{'supervisor': t_id} for t_id in list(set(supervisors))]
|
|
|
|
def _get_controls(self):
|
|
PlanificationFraction = Pool().get('lims.planification-fraction')
|
|
|
|
planification_id = Transaction().context['active_id']
|
|
controls = []
|
|
|
|
planification_controls = PlanificationFraction.search([
|
|
('planification', '=', planification_id),
|
|
('fraction.type.requalify', '=', True),
|
|
])
|
|
for p_control in planification_controls:
|
|
controls.append(p_control.fraction.id)
|
|
|
|
return [{'control': f_id} for f_id in list(set(controls))]
|
|
|
|
|
|
class ReplaceTechnicianStart(ModelView):
|
|
'Replace Technician'
|
|
__name__ = 'lims.planification.replace_technician.start'
|
|
|
|
planification = fields.Many2One('lims.planification', 'Planification')
|
|
technician_replaced = fields.Many2One('lims.laboratory.professional',
|
|
'Technician replaced', required=True,
|
|
domain=[('id', 'in', Eval('replaced_domain'))],
|
|
depends=['replaced_domain'])
|
|
replaced_domain = fields.One2Many('lims.laboratory.professional',
|
|
None, 'Replaced domain')
|
|
technician_substitute = fields.Many2One('lims.laboratory.professional',
|
|
'Technician substitute', required=True,
|
|
domain=[('id', 'in', Eval('substitute_domain'))],
|
|
depends=['substitute_domain'])
|
|
substitute_domain = fields.Function(fields.One2Many(
|
|
'lims.laboratory.professional', None, 'Substitute domain'),
|
|
'on_change_with_substitute_domain')
|
|
|
|
@fields.depends('technician_replaced', 'planification',
|
|
'_parent_planification.laboratory')
|
|
def on_change_with_substitute_domain(self, name=None):
|
|
pool = Pool()
|
|
UserLaboratory = pool.get('lims.user-laboratory')
|
|
LaboratoryProfessional = pool.get('lims.laboratory.professional')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
|
|
if not self.technician_replaced:
|
|
return []
|
|
|
|
substitute_domain = []
|
|
users = UserLaboratory.search([
|
|
('laboratory', '=', self.planification.laboratory.id),
|
|
])
|
|
if users:
|
|
professionals = LaboratoryProfessional.search([
|
|
('party.lims_user', 'in', [u.user.id for u in users]),
|
|
])
|
|
if professionals:
|
|
substitute_domain = [p.id for p in professionals]
|
|
|
|
service_details = PlanificationServiceDetail.search([
|
|
('planification', '=', self.planification.id),
|
|
('notebook_line', '!=', None),
|
|
('staff_responsible', '=', self.technician_replaced.id),
|
|
])
|
|
methods_ids = []
|
|
for service_detail in service_details:
|
|
method = service_detail.notebook_line.method
|
|
if method:
|
|
methods_ids.append(method.id)
|
|
|
|
methods_ids = list(set(methods_ids))
|
|
for technician_id in substitute_domain:
|
|
for method_id in methods_ids:
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', technician_id),
|
|
('method', '=', method_id),
|
|
('type', '=', 'preparation'),
|
|
])
|
|
if not (qualifications and qualifications[0].state in
|
|
('qualified', 'requalified')):
|
|
substitute_domain.remove(technician_id)
|
|
break
|
|
return substitute_domain
|
|
|
|
|
|
class ReplaceTechnician(Wizard):
|
|
'Replace Technician'
|
|
__name__ = 'lims.planification.replace_technician'
|
|
|
|
start_state = 'check'
|
|
check = StateTransition()
|
|
start = StateView('lims.planification.replace_technician.start',
|
|
'lims.lims_replace_technician_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Replace', 'replace', 'tryton-ok', default=True),
|
|
])
|
|
replace = StateTransition()
|
|
|
|
def transition_check(self):
|
|
pool = Pool()
|
|
Planification = pool.get('lims.planification')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
if planification.state != 'confirmed':
|
|
return 'end'
|
|
|
|
notebook_lines = NotebookLine.search([
|
|
('planification', '=', planification.id),
|
|
('end_date', '!=', None),
|
|
])
|
|
if notebook_lines:
|
|
return 'end'
|
|
|
|
return 'start'
|
|
|
|
def default_start(self, fields):
|
|
Planification = Pool().get('lims.planification')
|
|
|
|
planification = Planification(Transaction().context['active_id'])
|
|
|
|
return {
|
|
'planification': planification.id,
|
|
'replaced_domain': [t.laboratory_professional.id
|
|
for t in planification.technicians],
|
|
'substitute_domain': [],
|
|
}
|
|
|
|
def transition_replace(self):
|
|
planification_id = Transaction().context['active_id']
|
|
technician_replaced_id = self.start.technician_replaced.id
|
|
technician_substitute_id = self.start.technician_substitute.id
|
|
|
|
self.update_planification_detail(planification_id,
|
|
technician_replaced_id, technician_substitute_id)
|
|
self.update_planification_technician(planification_id,
|
|
technician_replaced_id, technician_substitute_id)
|
|
self.update_laboratory_notebook(planification_id,
|
|
technician_replaced_id, technician_substitute_id)
|
|
return 'end'
|
|
|
|
def update_planification_detail(self, planification_id,
|
|
technician_replaced_id, technician_substitute_id):
|
|
PlanificationServiceDetailProfessional = Pool().get(
|
|
'lims.planification.service_detail-laboratory.professional')
|
|
|
|
details_professional = PlanificationServiceDetailProfessional.search([
|
|
('detail.detail.planification', '=', planification_id),
|
|
('professional', '=', technician_replaced_id),
|
|
])
|
|
PlanificationServiceDetailProfessional.write(details_professional, {
|
|
'professional': technician_substitute_id,
|
|
})
|
|
|
|
def update_planification_technician(self, planification_id,
|
|
technician_replaced_id, technician_substitute_id):
|
|
PlanificationTechnician = Pool().get('lims.planification.technician')
|
|
|
|
planification_professional = PlanificationTechnician.search([
|
|
('planification', '=', planification_id),
|
|
('laboratory_professional', '=', technician_replaced_id),
|
|
])
|
|
PlanificationTechnician.delete(planification_professional)
|
|
|
|
planification_professional = PlanificationTechnician.search([
|
|
('planification', '=', planification_id),
|
|
('laboratory_professional', '=', technician_substitute_id),
|
|
])
|
|
if not planification_professional:
|
|
PlanificationTechnician.create([{
|
|
'planification': planification_id,
|
|
'laboratory_professional': technician_substitute_id,
|
|
}])
|
|
|
|
def update_laboratory_notebook(self, planification_id,
|
|
technician_replaced_id, technician_substitute_id):
|
|
NotebookLineProfessional = Pool().get(
|
|
'lims.notebook.line-laboratory.professional')
|
|
|
|
notebook_line_professional = NotebookLineProfessional.search([
|
|
('notebook_line.planification', '=', planification_id),
|
|
('professional', '=', technician_replaced_id),
|
|
])
|
|
NotebookLineProfessional.write(notebook_line_professional, {
|
|
'professional': technician_substitute_id,
|
|
})
|
|
|
|
|
|
class LoadServices(Wizard):
|
|
'Load Services'
|
|
__name__ = 'lims.load_services'
|
|
|
|
start_state = 'check'
|
|
check = StateTransition()
|
|
load = StateTransition()
|
|
|
|
def transition_check(self):
|
|
Fraction = Pool().get('lims.fraction')
|
|
|
|
fraction = Fraction(Transaction().context['active_id'])
|
|
if (not fraction or not fraction.cie_fraction_type or
|
|
not fraction.cie_original_fraction):
|
|
return 'end'
|
|
return 'load'
|
|
|
|
def transition_load(self):
|
|
pool = Pool()
|
|
Fraction = pool.get('lims.fraction')
|
|
Service = pool.get('lims.service')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
new_fraction = Fraction(Transaction().context['active_id'])
|
|
original_fraction = new_fraction.cie_original_fraction
|
|
|
|
# new services
|
|
services = Service.search([
|
|
('fraction', '=', original_fraction),
|
|
])
|
|
for service in services:
|
|
if not Analysis.is_typified(service.analysis,
|
|
new_fraction.product_type, new_fraction.matrix):
|
|
continue
|
|
new_service, = Service.copy([service], default={
|
|
'fraction': new_fraction.id,
|
|
})
|
|
return 'end'
|
|
|
|
|
|
class PlanificationSequenceReport(Report):
|
|
'Sequence'
|
|
__name__ = 'lims.planification.sequence.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(PlanificationSequenceReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = {}
|
|
for planification in records:
|
|
if planification.state != 'confirmed':
|
|
continue
|
|
date = str(planification.start_date)
|
|
if date not in objects:
|
|
objects[date] = {
|
|
'date': planification.start_date,
|
|
'methods': {},
|
|
}
|
|
for detail in planification.details:
|
|
fraction = detail.fraction
|
|
for service_detail in detail.details:
|
|
if (service_detail.notebook_line.analysis.behavior ==
|
|
'internal_relation'):
|
|
continue
|
|
notebook_line = service_detail.notebook_line
|
|
method_id = notebook_line.method.id
|
|
if method_id not in objects[date]['methods']:
|
|
objects[date]['methods'][method_id] = {
|
|
'method': notebook_line.method.code,
|
|
'lines': {},
|
|
}
|
|
|
|
number = fraction.get_formated_number('sn-sy-fn')
|
|
number = (number + '-' + str(notebook_line.repetition))
|
|
number_parts = number.split('-')
|
|
order = (number_parts[1] + '-' + number_parts[0] + '-' +
|
|
number_parts[2] + '-' + number_parts[3])
|
|
|
|
product_type = fraction.product_type.code
|
|
matrix = fraction.matrix.code
|
|
fraction_type = fraction.type.code
|
|
comments = fraction.comments
|
|
analysis_origin = notebook_line.analysis_origin
|
|
priority = notebook_line.priority
|
|
urgent = notebook_line.urgent
|
|
report_date = (notebook_line.report_date or
|
|
notebook_line.results_estimated_date)
|
|
trace_report = fraction.sample.trace_report
|
|
sample_client_description = (
|
|
fraction.sample.sample_client_description)
|
|
key = (number, product_type, matrix, fraction_type,
|
|
analysis_origin, priority, trace_report)
|
|
if key not in objects[date]['methods'][method_id]['lines']:
|
|
objects[date]['methods'][method_id]['lines'][key] = {
|
|
'order': order,
|
|
'number': number,
|
|
'product_type': product_type,
|
|
'matrix': matrix,
|
|
'fraction_type': fraction_type,
|
|
'analysis_origin': analysis_origin,
|
|
'priority': priority,
|
|
'urgent': urgent,
|
|
'report_date': report_date,
|
|
'trace_report': trace_report,
|
|
'comments': comments,
|
|
'sample_client_description': (
|
|
sample_client_description),
|
|
}
|
|
|
|
for k1 in objects.keys():
|
|
for k2, lines in objects[k1]['methods'].items():
|
|
sorted_lines = sorted(list(lines['lines'].values()),
|
|
key=lambda x: x['order'])
|
|
objects[k1]['methods'][k2]['lines'] = sorted_lines
|
|
|
|
report_context['objects'] = objects
|
|
|
|
return report_context
|
|
|
|
|
|
class PlanificationWorksheetAnalysisReport(Report):
|
|
'Worksheet by Analysis'
|
|
__name__ = 'lims.planification.worksheet_analysis.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(PlanificationWorksheetAnalysisReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = {}
|
|
for planification in records:
|
|
if planification.state != 'confirmed':
|
|
continue
|
|
date = str(planification.start_date)
|
|
if date not in objects:
|
|
objects[date] = {
|
|
'date': planification.start_date,
|
|
'professionals': {},
|
|
}
|
|
for detail in planification.details:
|
|
fraction = detail.fraction
|
|
for service_detail in detail.details:
|
|
if not service_detail.notebook_line:
|
|
continue
|
|
if (service_detail.notebook_line.analysis.behavior ==
|
|
'internal_relation'):
|
|
continue
|
|
p_key = ()
|
|
p_names = ''
|
|
for professional in service_detail.staff_responsible:
|
|
p_key += (professional.id,)
|
|
if p_names:
|
|
p_names += ', '
|
|
p_names += professional.rec_name
|
|
if not p_key:
|
|
continue
|
|
p_key = tuple(sorted(p_key))
|
|
if (p_key not in objects[date]['professionals']):
|
|
objects[date]['professionals'][p_key] = {
|
|
'professional': p_names,
|
|
'analysis': {},
|
|
'total': 0,
|
|
}
|
|
notebook_line = service_detail.notebook_line
|
|
key = (notebook_line.analysis.id, notebook_line.method.id)
|
|
if (key not in
|
|
objects[date]['professionals'][p_key]['analysis']):
|
|
objects[date]['professionals'][p_key]['analysis'][
|
|
key] = {
|
|
'order': notebook_line.analysis.order or 9999,
|
|
'analysis': notebook_line.analysis.rec_name,
|
|
'method': notebook_line.method.rec_name,
|
|
'lines': {},
|
|
}
|
|
|
|
number = fraction.get_formated_number('pt-m-sy-sn-fn')
|
|
number = (number + '-' + str(notebook_line.repetition))
|
|
concentration_level = (
|
|
notebook_line.concentration_level.description if
|
|
notebook_line.concentration_level else '')
|
|
if (number in objects[date]['professionals'][p_key][
|
|
'analysis'][key]['lines']):
|
|
continue
|
|
|
|
number_parts = number.split('-')
|
|
order = (number_parts[3] + '-' + number_parts[2] + '-' +
|
|
number_parts[4] + '-' + number_parts[5])
|
|
comments = '%s - %s - %s' % (planification.comments or '',
|
|
notebook_line.service.comments or '',
|
|
notebook_line.service.fraction.comments or '')
|
|
record = {
|
|
'order': order,
|
|
'number': number,
|
|
'label': fraction.label,
|
|
'sample_client_description': (
|
|
fraction.sample.sample_client_description),
|
|
'party': fraction.party.code,
|
|
'storage_location': fraction.storage_location.code,
|
|
'fraction_type': fraction.type.code,
|
|
'concentration_level': concentration_level,
|
|
'device': (notebook_line.device.code if
|
|
notebook_line.device else None),
|
|
'urgent': 'SI' if notebook_line.urgent else '',
|
|
'comments': comments,
|
|
'planification_code': planification.code,
|
|
'sample_obj_description': (
|
|
fraction.sample.obj_description.description
|
|
if fraction.sample.obj_description else
|
|
fraction.sample.obj_description_manual
|
|
if fraction.sample.obj_description_manual else
|
|
''),
|
|
}
|
|
objects[date]['professionals'][p_key]['analysis'][
|
|
key]['lines'][number] = record
|
|
objects[date]['professionals'][p_key]['total'] += 1
|
|
|
|
for k1 in objects.keys():
|
|
for k2 in objects[k1]['professionals'].keys():
|
|
sorted_analysis = sorted(list(objects[k1]['professionals'][k2][
|
|
'analysis'].items()), key=lambda x: x[1]['order'])
|
|
objects[k1]['professionals'][k2]['analysis'] = []
|
|
for item in sorted_analysis:
|
|
sorted_lines = sorted(list(item[1]['lines'].items()),
|
|
key=lambda x: x[1]['order'])
|
|
item[1]['lines'] = [l[1] for l in sorted_lines]
|
|
objects[k1]['professionals'][k2]['analysis'].append(
|
|
item[1])
|
|
|
|
report_context['records'] = objects
|
|
|
|
return report_context
|
|
|
|
|
|
class PlanificationWorksheetMethodReport(Report):
|
|
'Worksheet by Method'
|
|
__name__ = 'lims.planification.worksheet_method.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(PlanificationWorksheetMethodReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = {}
|
|
for planification in records:
|
|
if planification.state != 'confirmed':
|
|
continue
|
|
date = str(planification.start_date)
|
|
if date not in objects:
|
|
objects[date] = {
|
|
'date': planification.start_date,
|
|
'professionals': {},
|
|
}
|
|
for detail in planification.details:
|
|
fraction = detail.fraction
|
|
for service_detail in detail.details:
|
|
if not service_detail.notebook_line:
|
|
continue
|
|
if (service_detail.notebook_line.analysis.behavior ==
|
|
'internal_relation'):
|
|
continue
|
|
p_key = ()
|
|
p_names = ''
|
|
for professional in service_detail.staff_responsible:
|
|
p_key += (professional.id,)
|
|
if p_names:
|
|
p_names += ', '
|
|
p_names += professional.rec_name
|
|
if not p_key:
|
|
continue
|
|
p_key = tuple(sorted(p_key))
|
|
if (p_key not in objects[date]['professionals']):
|
|
objects[date]['professionals'][p_key] = {
|
|
'professional': p_names,
|
|
'total': 0,
|
|
'lines': {},
|
|
}
|
|
notebook_line = service_detail.notebook_line
|
|
|
|
number = fraction.get_formated_number('pt-m-sy-sn-fn')
|
|
number = (number + '-' + str(notebook_line.repetition))
|
|
concentration_level = (
|
|
notebook_line.concentration_level.description if
|
|
notebook_line.concentration_level else '')
|
|
if (number not in objects[date]['professionals'][p_key][
|
|
'lines']):
|
|
number_parts = number.split('-')
|
|
order = (number_parts[3] + '-' + number_parts[2] +
|
|
'-' + number_parts[4] + '-' + number_parts[5])
|
|
|
|
comments_planif = '. '.join(cls.get_planning_legend(
|
|
fraction, planification))
|
|
if comments_planif:
|
|
comments = '%s - %s - %s - %s' % (
|
|
planification.comments or '',
|
|
notebook_line.service.comments or '',
|
|
notebook_line.service.fraction.comments or '',
|
|
comments_planif or '')
|
|
else:
|
|
comments = '%s - %s - %s' % (
|
|
planification.comments or
|
|
'', notebook_line.service.comments or '',
|
|
notebook_line.service.fraction.comments or '')
|
|
|
|
if fraction.packages_quantity != 0.0:
|
|
pack_quant = str(fraction.packages_quantity)
|
|
else:
|
|
pack_quant = ''
|
|
record = {
|
|
'order': order,
|
|
'number': number,
|
|
'label': fraction.label,
|
|
'sample_client_description': (
|
|
fraction.sample.sample_client_description),
|
|
'party': fraction.party.code,
|
|
'storage_location': fraction.storage_location.code,
|
|
'fraction_type': fraction.type.code,
|
|
'concentration_level': concentration_level,
|
|
'device': (notebook_line.device.code if
|
|
notebook_line.device else None),
|
|
'urgent': 'SI' if notebook_line.urgent else '',
|
|
'comments': comments,
|
|
'planification_code': planification.code,
|
|
'package_type':
|
|
pack_quant + ' ' +
|
|
fraction.package_type.description,
|
|
'methods': {},
|
|
'sample_obj_description': (
|
|
fraction.sample.obj_description.description
|
|
if fraction.sample.obj_description else
|
|
fraction.sample.obj_description_manual
|
|
if fraction.sample.obj_description_manual else
|
|
''),
|
|
}
|
|
objects[date]['professionals'][p_key]['lines'][
|
|
number] = record
|
|
objects[date]['professionals'][p_key]['total'] += 1
|
|
if (notebook_line.method.id not in
|
|
objects[date]['professionals'][p_key]['lines'][
|
|
number]['methods']):
|
|
objects[date]['professionals'][p_key]['lines'][
|
|
number]['methods'][notebook_line.method.id] = (
|
|
notebook_line.method.rec_name)
|
|
|
|
for k1 in objects.keys():
|
|
for k2 in objects[k1]['professionals'].keys():
|
|
objects[k1]['professionals'][k2]['methods'] = {}
|
|
fractions = list(
|
|
objects[k1]['professionals'][k2]['lines'].values())
|
|
for fraction in fractions:
|
|
m_key = ()
|
|
m_names = []
|
|
for m_id, m_name in fraction['methods'].items():
|
|
m_key += (m_id,)
|
|
m_names.append(m_name)
|
|
m_key = tuple(sorted(m_key))
|
|
if (m_key not in objects[k1]['professionals'][k2][
|
|
'methods']):
|
|
objects[k1]['professionals'][k2]['methods'][m_key] = {
|
|
'methods': m_names,
|
|
'lines': [],
|
|
}
|
|
objects[k1]['professionals'][k2]['methods'][m_key][
|
|
'lines'].append(fraction)
|
|
|
|
del objects[k1]['professionals'][k2]['lines']
|
|
for m_key in objects[k1]['professionals'][k2][
|
|
'methods'].keys():
|
|
sorted_lines = sorted(objects[k1]['professionals'][k2][
|
|
'methods'][m_key]['lines'], key=lambda x: x['order'])
|
|
objects[k1]['professionals'][k2]['methods'][m_key][
|
|
'lines'] = sorted_lines
|
|
|
|
report_context['records'] = objects
|
|
|
|
return report_context
|
|
|
|
@classmethod
|
|
def get_planning_legend(cls, fraction, planification):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
cursor.execute('SELECT DISTINCT(a.planning_legend) '
|
|
'FROM "' + PlanificationDetail._table + '" pd '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table +
|
|
'" psd '
|
|
'ON pd.id = psd.detail '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON nl.id = psd.notebook_line '
|
|
'INNER JOIN "' + Analysis._table + '" a '
|
|
'ON a.id = nl.analysis '
|
|
'WHERE pd.planification = %s '
|
|
'AND pd.fraction = %s '
|
|
'AND a.planning_legend IS NOT NULL ',
|
|
(planification.id, fraction.id))
|
|
|
|
planned_ids = [s[0] for s in cursor.fetchall()]
|
|
return planned_ids
|
|
|
|
|
|
class PlanificationWorksheetReport(Report):
|
|
'Worksheet'
|
|
__name__ = 'lims.planification.worksheet.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(PlanificationWorksheetReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = {}
|
|
for planification in records:
|
|
if planification.state != 'confirmed':
|
|
continue
|
|
date = str(planification.start_date)
|
|
if date not in objects:
|
|
objects[date] = {
|
|
'date': planification.start_date,
|
|
'professionals': {},
|
|
}
|
|
for detail in planification.details:
|
|
fraction = detail.fraction
|
|
for service_detail in detail.details:
|
|
if not service_detail.notebook_line:
|
|
continue
|
|
if (service_detail.notebook_line.analysis.behavior ==
|
|
'internal_relation'):
|
|
continue
|
|
p_key = ()
|
|
p_names = ''
|
|
for professional in service_detail.staff_responsible:
|
|
p_key += (professional.id,)
|
|
if p_names:
|
|
p_names += ', '
|
|
p_names += professional.rec_name
|
|
if not p_key:
|
|
continue
|
|
p_key = tuple(sorted(p_key))
|
|
if (p_key not in objects[date]['professionals']):
|
|
objects[date]['professionals'][p_key] = {
|
|
'professional': p_names,
|
|
'analysis': {},
|
|
'total': 0,
|
|
}
|
|
notebook_line = service_detail.notebook_line
|
|
key = service_detail.planned_service.id
|
|
if (key not in
|
|
objects[date]['professionals'][p_key]['analysis']):
|
|
objects[date]['professionals'][p_key]['analysis'][
|
|
key] = {
|
|
'analysis': (
|
|
service_detail.planned_service.rec_name),
|
|
'lines': {},
|
|
}
|
|
|
|
number = fraction.get_formated_number('pt-m-sy-sn-fn')
|
|
number = (number + '-' + str(notebook_line.repetition))
|
|
concentration_level = (
|
|
notebook_line.concentration_level.description if
|
|
notebook_line.concentration_level else '')
|
|
if (number not in objects[date]['professionals'][p_key][
|
|
'analysis'][key]['lines']):
|
|
number_parts = number.split('-')
|
|
order = (number_parts[3] + '-' + number_parts[2] +
|
|
'-' + number_parts[4] + '-' + number_parts[5])
|
|
|
|
comments_planif = '. '.join(cls.get_planning_legend(
|
|
fraction, planification))
|
|
if comments_planif:
|
|
comments = '%s - %s - %s - %s' % (
|
|
planification.comments or '',
|
|
notebook_line.service.comments or '',
|
|
notebook_line.service.fraction.comments or '',
|
|
comments_planif or '')
|
|
else:
|
|
comments = '%s - %s - %s' % (
|
|
planification.comments or
|
|
'', notebook_line.service.comments or '',
|
|
notebook_line.service.fraction.comments or '')
|
|
|
|
if fraction.packages_quantity != 0.0:
|
|
pack_quant = str(fraction.packages_quantity)
|
|
else:
|
|
pack_quant = ''
|
|
record = {
|
|
'order': order,
|
|
'number': number,
|
|
'label': fraction.label,
|
|
'sample_client_description': (
|
|
fraction.sample.sample_client_description if
|
|
fraction.sample.sample_client_description else
|
|
''),
|
|
'party': fraction.party.code,
|
|
'storage_location': fraction.storage_location.code,
|
|
'fraction_type': fraction.type.code,
|
|
'concentration_level': concentration_level,
|
|
'device': (notebook_line.device.code if
|
|
notebook_line.device else None),
|
|
'urgent': 'SI' if notebook_line.urgent else '',
|
|
'comments': comments,
|
|
'planification_code': planification.code,
|
|
'package_type':
|
|
pack_quant + ' ' +
|
|
fraction.package_type.description,
|
|
'methods': {},
|
|
'sample_obj_description': (
|
|
fraction.sample.obj_description.description
|
|
if fraction.sample.obj_description else
|
|
fraction.sample.obj_description_manual
|
|
if fraction.sample.obj_description_manual else
|
|
''),
|
|
}
|
|
objects[date]['professionals'][p_key]['analysis'][key][
|
|
'lines'][number] = record
|
|
objects[date]['professionals'][p_key]['total'] += 1
|
|
if (notebook_line.method.id not in
|
|
objects[date]['professionals'][p_key]['analysis'][
|
|
key]['lines'][number]['methods']):
|
|
objects[date]['professionals'][p_key]['analysis'][
|
|
key]['lines'][number]['methods'][
|
|
notebook_line.method.id] = (
|
|
notebook_line.method.rec_name)
|
|
|
|
for k1 in objects.keys():
|
|
for k2 in objects[k1]['professionals'].keys():
|
|
for k3 in objects[k1]['professionals'][k2][
|
|
'analysis'].keys():
|
|
objects[k1]['professionals'][k2]['analysis'][k3][
|
|
'methods'] = {}
|
|
fractions = list(
|
|
objects[k1]['professionals'][k2]['analysis'][
|
|
k3]['lines'].values())
|
|
for fraction in fractions:
|
|
m_key = ()
|
|
m_names = []
|
|
for m_id, m_name in fraction['methods'].items():
|
|
m_key += (m_id,)
|
|
m_names.append(m_name)
|
|
m_key = tuple(sorted(m_key))
|
|
if (m_key not in objects[k1]['professionals'][k2][
|
|
'analysis'][k3]['methods']):
|
|
objects[k1]['professionals'][k2]['analysis'][k3][
|
|
'methods'][m_key] = {
|
|
'methods': m_names,
|
|
'lines': [],
|
|
}
|
|
objects[k1]['professionals'][k2]['analysis'][k3][
|
|
'methods'][m_key]['lines'].append(fraction)
|
|
|
|
del objects[k1]['professionals'][k2]['analysis'][k3][
|
|
'lines']
|
|
for m_key in objects[k1]['professionals'][k2][
|
|
'analysis'][k3]['methods'].keys():
|
|
sorted_lines = sorted(objects[k1]['professionals'][k2][
|
|
'analysis'][k3]['methods'][m_key]['lines'],
|
|
key=lambda x: x['order'])
|
|
objects[k1]['professionals'][k2]['analysis'][k3][
|
|
'methods'][m_key]['lines'] = sorted_lines
|
|
|
|
report_context['records'] = objects
|
|
return report_context
|
|
|
|
@classmethod
|
|
def get_planning_legend(cls, fraction, planification):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
PlanificationServiceDetail = pool.get(
|
|
'lims.planification.service_detail')
|
|
PlanificationDetail = pool.get('lims.planification.detail')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
cursor.execute('SELECT DISTINCT(a.planning_legend) '
|
|
'FROM "' + PlanificationDetail._table + '" pd '
|
|
'INNER JOIN "' + PlanificationServiceDetail._table +
|
|
'" psd '
|
|
'ON pd.id = psd.detail '
|
|
'INNER JOIN "' + NotebookLine._table + '" nl '
|
|
'ON nl.id = psd.notebook_line '
|
|
'INNER JOIN "' + Analysis._table + '" a '
|
|
'ON a.id = nl.analysis '
|
|
'WHERE pd.planification = %s '
|
|
'AND pd.fraction = %s '
|
|
'AND a.planning_legend IS NOT NULL ',
|
|
(planification.id, fraction.id))
|
|
|
|
planned_ids = [s[0] for s in cursor.fetchall()]
|
|
return planned_ids
|
|
|
|
|
|
class PrintPendingServicesUnplannedReportStart(ModelView):
|
|
'Print Pending Services Unplanned Report Start'
|
|
__name__ = 'lims.pending_services_unplanned.start'
|
|
|
|
start_date = fields.Date('Start Date', required=True)
|
|
end_date = fields.Date('End Date', required=True)
|
|
party = fields.Many2One('party.party', 'Party')
|
|
include_method = fields.Boolean('Include method')
|
|
|
|
|
|
class PrintPendingServicesUnplannedReport(Wizard):
|
|
'Print Pending Services Unplanned Report'
|
|
__name__ = 'lims.pending_services_unplanned'
|
|
|
|
start = StateView('lims.pending_services_unplanned.start',
|
|
'lims.print_pending_services_unplanned_report_start'
|
|
'_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-print', default=True),
|
|
Button('Save', 'save', 'tryton-save'),
|
|
])
|
|
print_ = StateAction(
|
|
'lims.report_pending_services_unplanned')
|
|
save = StateAction(
|
|
'lims.report_pending_services_unplanned_spreadsheet')
|
|
|
|
def do_print_(self, action):
|
|
data = {
|
|
'start_date': self.start.start_date,
|
|
'end_date': self.start.end_date or None,
|
|
'party': self.start.party and self.start.party.id or None,
|
|
'include_method': self.start.include_method,
|
|
}
|
|
return action, data
|
|
|
|
def do_save(self, action):
|
|
data = {
|
|
'start_date': self.start.start_date,
|
|
'end_date': self.start.end_date or None,
|
|
'party': self.start.party and self.start.party.id or None,
|
|
'include_method': self.start.include_method,
|
|
}
|
|
return action, data
|
|
|
|
|
|
class PendingServicesUnplannedReport(Report):
|
|
'Pending Services Unplanned'
|
|
__name__ = 'lims.pending_services_unplanned.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Laboratory = pool.get('lims.laboratory')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
report_context = super(PendingServicesUnplannedReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
report_context['start_date'] = (data['start_date']
|
|
if data['start_date'] else '')
|
|
report_context['end_date'] = (data['end_date']
|
|
if data['end_date'] else '')
|
|
report_context['include_method'] = data['include_method']
|
|
|
|
if report_context['user'].laboratory:
|
|
labs = [report_context['user'].laboratory.id]
|
|
else:
|
|
labs = [l.id for l in Laboratory.search([])]
|
|
|
|
clause = [
|
|
('plannable', '=', True),
|
|
('state', '=', 'unplanned'),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
('service.fraction.confirmed', '=', True),
|
|
]
|
|
if data['start_date']:
|
|
clause.append(
|
|
('service.confirmation_date', '>=', data['start_date']))
|
|
if data['end_date']:
|
|
clause.append(
|
|
('service.confirmation_date', '<=', data['end_date']))
|
|
if data['party']:
|
|
clause.append(('party', '=', data['party']))
|
|
|
|
objects = {}
|
|
with Transaction().set_user(0):
|
|
details = EntryDetailAnalysis.search(clause)
|
|
for detail in details:
|
|
# Laboratory
|
|
laboratory_id = detail.laboratory.id
|
|
if laboratory_id not in labs:
|
|
continue
|
|
|
|
if laboratory_id not in objects:
|
|
objects[laboratory_id] = {
|
|
'laboratory': detail.laboratory.rec_name,
|
|
'services': {},
|
|
'total': 0,
|
|
}
|
|
|
|
# Service
|
|
analysis_id = detail.service.analysis.id
|
|
if analysis_id not in objects[laboratory_id]['services']:
|
|
objects[laboratory_id]['services'][analysis_id] = {
|
|
'service': detail.service.analysis.rec_name,
|
|
'parties': {},
|
|
'total': 0,
|
|
}
|
|
|
|
# Party
|
|
party_id = detail.party.id
|
|
if (party_id not in objects[laboratory_id]['services'][
|
|
analysis_id]['parties']):
|
|
objects[laboratory_id]['services'][analysis_id]['parties'][
|
|
party_id] = {
|
|
'party': detail.party.code,
|
|
'lines': {},
|
|
'total': 0,
|
|
}
|
|
|
|
# Key
|
|
if data['include_method']:
|
|
key = (detail.fraction.id, detail.method.id)
|
|
else:
|
|
key = detail.fraction.id
|
|
|
|
if (key in objects[laboratory_id]['services'][analysis_id][
|
|
'parties'][party_id]['lines']):
|
|
continue
|
|
|
|
number = detail.service.fraction.get_formated_number(
|
|
'pt-m-sn-sy-fn')
|
|
number = (number + '-' + str(detail.service.sample.label))
|
|
number_parts = number.split('-')
|
|
order = (number_parts[3] + '-' + number_parts[2] + '-' +
|
|
number_parts[4])
|
|
|
|
results_estimated_date = None
|
|
results_estimated_waiting = cls._get_estimated_waiting(detail.id)
|
|
if results_estimated_waiting:
|
|
results_estimated_date = (detail.service.confirmation_date +
|
|
relativedelta(days=results_estimated_waiting))
|
|
|
|
record = {
|
|
'order': order,
|
|
'number': number,
|
|
'date': detail.service.sample.date2,
|
|
'sample_client_description': (
|
|
detail.service.sample.sample_client_description),
|
|
'current_location': (
|
|
detail.service.fraction.current_location.code
|
|
if detail.service.fraction.current_location else ''),
|
|
'fraction_type': detail.service.fraction.type.code,
|
|
'priority': detail.service.priority,
|
|
'urgent': 'Yes' if detail.service.urgent else 'No',
|
|
'method': detail.method.code if data['include_method'] else '',
|
|
'comments': ('%s - %s' % (
|
|
detail.service.fraction.comments or '',
|
|
detail.service.sample.comments or '')),
|
|
'report_date': detail.service.report_date,
|
|
'confirmation_date': (detail.service.confirmation_date
|
|
if detail.service.confirmation_date else ''),
|
|
'results_estimated_date': results_estimated_date or '',
|
|
}
|
|
objects[laboratory_id]['services'][analysis_id]['parties'][
|
|
party_id]['lines'][key] = record
|
|
objects[laboratory_id]['services'][analysis_id]['parties'][
|
|
party_id]['total'] += 1
|
|
objects[laboratory_id]['services'][analysis_id][
|
|
'total'] += 1
|
|
objects[laboratory_id]['total'] += 1
|
|
|
|
for k1 in objects.keys():
|
|
for k2 in objects[k1]['services'].keys():
|
|
for k3, lines in objects[k1]['services'][k2][
|
|
'parties'].items():
|
|
sorted_lines = sorted(lines['lines'].values(),
|
|
key=lambda x: x['order'])
|
|
objects[k1]['services'][k2]['parties'][k3]['lines'] = (
|
|
sorted_lines)
|
|
|
|
report_context['objects'] = objects
|
|
return report_context
|
|
|
|
@classmethod
|
|
def _get_estimated_waiting(cls, detail_id):
|
|
cursor = Transaction().connection.cursor()
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
cursor.execute('SELECT MIN(results_estimated_waiting) '
|
|
'FROM "' + NotebookLine._table + '" '
|
|
'WHERE analysis_detail = %s '
|
|
'AND results_estimated_waiting IS NOT NULL',
|
|
(detail_id,))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
|
|
class PendingServicesUnplannedSpreadsheet(Report):
|
|
'Pending Services Unplanned'
|
|
__name__ = 'lims.pending_services_unplanned.spreadsheet'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Laboratory = pool.get('lims.laboratory')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
report_context = super(PendingServicesUnplannedSpreadsheet,
|
|
cls).get_context(records, data)
|
|
report_context['company'] = report_context['user'].company
|
|
report_context['start_date'] = (data['start_date']
|
|
if data['start_date'] else '')
|
|
report_context['end_date'] = (data['end_date']
|
|
if data['end_date'] else '')
|
|
report_context['include_method'] = data['include_method']
|
|
|
|
if report_context['user'].laboratory:
|
|
labs = [report_context['user'].laboratory.id]
|
|
else:
|
|
labs = [l.id for l in Laboratory.search([])]
|
|
|
|
clause = [
|
|
('plannable', '=', True),
|
|
('state', '=', 'unplanned'),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
('service.fraction.confirmed', '=', True),
|
|
]
|
|
if data['start_date']:
|
|
clause.append(
|
|
('service.confirmation_date', '>=', data['start_date']))
|
|
if data['end_date']:
|
|
clause.append(
|
|
('service.confirmation_date', '<=', data['end_date']))
|
|
if data['party']:
|
|
clause.append(('party', '=', data['party']))
|
|
|
|
objects = {}
|
|
with Transaction().set_user(0):
|
|
details = EntryDetailAnalysis.search(clause)
|
|
for detail in details:
|
|
# Laboratory
|
|
laboratory_id = detail.laboratory.id
|
|
if laboratory_id not in labs:
|
|
continue
|
|
|
|
# Key
|
|
if data['include_method']:
|
|
key = (detail.fraction.id, detail.method.id)
|
|
else:
|
|
key = detail.fraction.id
|
|
|
|
if key in objects:
|
|
continue
|
|
|
|
number = detail.service.fraction.get_formated_number(
|
|
'pt-m-sn-sy-fn')
|
|
number = (number + '-' + str(detail.service.sample.label))
|
|
number_parts = number.split('-')
|
|
order = (number_parts[3] + '-' + number_parts[2] + '-' +
|
|
number_parts[4])
|
|
|
|
results_estimated_date = None
|
|
results_estimated_waiting = cls._get_estimated_waiting(detail.id)
|
|
if results_estimated_waiting:
|
|
results_estimated_date = (detail.service.confirmation_date +
|
|
relativedelta(days=results_estimated_waiting))
|
|
|
|
notice = None
|
|
report_date = detail.service.report_date
|
|
today = get_print_date().date()
|
|
|
|
if report_date:
|
|
if report_date < today:
|
|
notice = 'Timed out'
|
|
else:
|
|
d = (report_date - today).days
|
|
if d >= 0 and d < 3:
|
|
notice = 'To expire'
|
|
elif results_estimated_date:
|
|
if results_estimated_date < today:
|
|
notice = 'Timed out'
|
|
else:
|
|
d = (results_estimated_date - today).days
|
|
if d >= 0 and d < 3:
|
|
notice = 'To expire'
|
|
|
|
record = {
|
|
'laboratory': detail.laboratory.rec_name,
|
|
'service': detail.service.analysis.rec_name,
|
|
'party': detail.party.code,
|
|
'order': order,
|
|
'number': number,
|
|
'date': detail.service.sample.date2,
|
|
'sample_client_description': (
|
|
detail.service.sample.sample_client_description),
|
|
'current_location': (
|
|
detail.service.fraction.current_location.code
|
|
if detail.service.fraction.current_location else ''),
|
|
'fraction_type': detail.service.fraction.type.code,
|
|
'priority': detail.service.priority,
|
|
'urgent': 'Yes' if detail.service.urgent else 'No',
|
|
'method': detail.method.code if data['include_method'] else '',
|
|
'comments': ('%s - %s' % (
|
|
detail.service.fraction.comments or '',
|
|
detail.service.sample.comments or '')),
|
|
'report_date': detail.service.report_date,
|
|
'confirmation_date': (detail.service.confirmation_date
|
|
if detail.service.confirmation_date else ''),
|
|
'results_estimated_date': results_estimated_date or '',
|
|
'results_estimated_waiting': results_estimated_waiting or '',
|
|
'notice': notice or '',
|
|
}
|
|
objects[key] = record
|
|
|
|
objects = sorted(objects.values(), key=lambda x: (
|
|
x['laboratory'], x['service'], x['party'], x['order']))
|
|
|
|
report_context['objects'] = objects
|
|
return report_context
|
|
|
|
@classmethod
|
|
def _get_estimated_waiting(cls, detail_id):
|
|
cursor = Transaction().connection.cursor()
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
cursor.execute('SELECT MIN(results_estimated_waiting) '
|
|
'FROM "' + NotebookLine._table + '" '
|
|
'WHERE analysis_detail = %s '
|
|
'AND results_estimated_waiting IS NOT NULL',
|
|
(detail_id,))
|
|
res = cursor.fetchone()
|
|
return res and res[0] or None
|
|
|
|
|
|
class PrintBlindSampleReportStart(ModelView):
|
|
'Blind Samples Report'
|
|
__name__ = 'lims.print_blind_sample_report.start'
|
|
|
|
date_from = fields.Date('Date from', required=True)
|
|
date_to = fields.Date('Date to', required=True)
|
|
|
|
|
|
class PrintBlindSampleReport(Wizard):
|
|
'Blind Samples Report'
|
|
__name__ = 'lims.print_blind_sample_report'
|
|
|
|
start = StateView('lims.print_blind_sample_report.start',
|
|
'lims.lims_print_blind_sample_report_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-print', default=True),
|
|
])
|
|
print_ = StateAction('lims.report_blind_sample')
|
|
|
|
def do_print_(self, action):
|
|
BlindSample = Pool().get('lims.blind_sample')
|
|
|
|
blind_samples = BlindSample.search_count([
|
|
('date', '>=', self.start.date_from),
|
|
('date', '<=', self.start.date_to),
|
|
('line.result', '!=', None),
|
|
])
|
|
if blind_samples > 0:
|
|
data = {
|
|
'date_from': self.start.date_from,
|
|
'date_to': self.start.date_to,
|
|
}
|
|
return action, data
|
|
|
|
def transition_print_(self):
|
|
return 'end'
|
|
|
|
|
|
class BlindSampleReport(Report):
|
|
'Blind Samples Report'
|
|
__name__ = 'lims.blind_sample_report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(BlindSampleReport, cls).get_context(records,
|
|
data)
|
|
BlindSample = Pool().get('lims.blind_sample')
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = []
|
|
blind_samples = BlindSample.search([
|
|
('date', '>=', data['date_from']),
|
|
('date', '<=', data['date_to']),
|
|
('line.result', '!=', None),
|
|
], order=[('date', 'ASC')])
|
|
for bs in blind_samples:
|
|
|
|
if bs.line.converted_result:
|
|
result = float(bs.line.converted_result)
|
|
unit = bs.line.final_unit
|
|
concentration = bs.line.final_concentration
|
|
result = round(result, bs.line.decimals)
|
|
elif bs.line.result:
|
|
result = float(bs.line.result)
|
|
unit = bs.line.initial_unit
|
|
concentration = bs.line.initial_concentration
|
|
result = round(result, bs.line.decimals)
|
|
else:
|
|
result = None
|
|
unit = None
|
|
concentration = ''
|
|
|
|
record = {
|
|
'date': bs.date,
|
|
'fraction': bs.fraction.rec_name,
|
|
'report': (bs.line.results_report.number if
|
|
bs.line.results_report else ''),
|
|
'repetition': bs.line.repetition,
|
|
'analysis_origin': bs.line.analysis_origin,
|
|
'analysis': bs.analysis.rec_name,
|
|
'result': result,
|
|
'unit': unit.symbol if unit else '',
|
|
'concentration': concentration,
|
|
'original_fraction': '',
|
|
'original_report': '',
|
|
'original_result': '',
|
|
'original_unit': '',
|
|
'original_concentration': '',
|
|
'error': '',
|
|
'max_sd': '',
|
|
'two_max_sd': '',
|
|
'accepted': '',
|
|
}
|
|
if bs.original_fraction:
|
|
record['original_fraction'] = bs.original_fraction.rec_name
|
|
if bs.original_line:
|
|
if bs.original_line.results_report:
|
|
record['original_report'] = (
|
|
bs.original_line.results_report.number)
|
|
if bs.original_line.converted_result:
|
|
original_result = float(
|
|
bs.original_line.converted_result)
|
|
original_unit = bs.original_line.final_unit
|
|
original_concentration = (
|
|
bs.original_line.final_concentration)
|
|
elif bs.original_line.result:
|
|
original_result = float(bs.original_line.result)
|
|
original_unit = bs.original_line.initial_unit
|
|
original_concentration = (
|
|
bs.original_line.initial_concentration)
|
|
else:
|
|
original_result = None
|
|
if original_result:
|
|
original_result = round(original_result,
|
|
bs.original_line.decimals)
|
|
record['original_result'] = original_result
|
|
record['original_unit'] = (original_unit.symbol
|
|
if original_unit else '')
|
|
record['original_concentration'] = (
|
|
original_concentration)
|
|
if unit == original_unit:
|
|
average = (result + original_result) / 2
|
|
difference = result - original_result
|
|
record['error'] = round(difference * 100 / average,
|
|
2)
|
|
try:
|
|
maximum_concentration = float(
|
|
unit.maximum_concentration)
|
|
rsd_horwitz = float(unit.rsd_horwitz)
|
|
except (TypeError, ValueError):
|
|
maximum_concentration = 0
|
|
rsd_horwitz = 0
|
|
|
|
if result <= maximum_concentration:
|
|
record['max_sd'] = round(average * rsd_horwitz,
|
|
2)
|
|
record['two_max_sd'] = round(record['max_sd'] *
|
|
2, 2)
|
|
if abs(difference) <= record['two_max_sd']:
|
|
record['accepted'] = 'a'
|
|
else:
|
|
record['accepted'] = 'r'
|
|
else:
|
|
if bs.min_value and bs.max_value:
|
|
min_value = float(bs.min_value)
|
|
max_value = float(bs.max_value)
|
|
if min_value <= result and result <= max_value:
|
|
record['error'] = 'OK'
|
|
elif result < min_value:
|
|
record['error'] = round(result - min_value, 2)
|
|
elif result > max_value:
|
|
record['error'] = round(result - max_value, 2)
|
|
objects.append(record)
|
|
|
|
report_context['objects'] = objects
|
|
|
|
report_context['date_from'] = data['date_from']
|
|
report_context['date_to'] = data['date_to']
|
|
|
|
return report_context
|
|
|
|
def _get_variables(self, formula, notebook_line):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for var in ('DI',):
|
|
while True:
|
|
idx = formula.find(var)
|
|
if idx >= 0:
|
|
variables[var] = 0
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var == 'DI':
|
|
ic = float(notebook_line.final_concentration)
|
|
result = VolumeConversion.brixToDensity(ic)
|
|
if result:
|
|
variables[var] = result
|
|
return variables
|
|
|
|
|
|
class PlanificationSequenceAnalysisReport(Report):
|
|
'Sequence Analysis'
|
|
__name__ = 'lims.planification.sequence.analysis.report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
report_context = super(PlanificationSequenceAnalysisReport,
|
|
cls).get_context(records, data)
|
|
|
|
report_context['company'] = report_context['user'].company
|
|
|
|
objects = {}
|
|
for planification in records:
|
|
if planification.state != 'confirmed':
|
|
continue
|
|
date = str(planification.start_date)
|
|
if date not in objects:
|
|
objects[date] = {
|
|
'date': planification.start_date,
|
|
'methods': {},
|
|
}
|
|
for detail in planification.details:
|
|
fraction = detail.fraction
|
|
for service_detail in detail.details:
|
|
if (service_detail.notebook_line.analysis.behavior ==
|
|
'internal_relation'):
|
|
continue
|
|
notebook_line = service_detail.notebook_line
|
|
method_id = notebook_line.method.id
|
|
if method_id not in objects[date]['methods']:
|
|
objects[date]['methods'][method_id] = {
|
|
'method': notebook_line.method.code,
|
|
'lines': {},
|
|
}
|
|
|
|
number = fraction.get_formated_number('sn-sy-fn')
|
|
number = (number + '-' + str(notebook_line.repetition))
|
|
number_parts = number.split('-')
|
|
order = (number_parts[1] + '-' + number_parts[0] + '-' +
|
|
number_parts[2] + '-' + number_parts[3])
|
|
|
|
product_type = fraction.product_type.code
|
|
matrix = fraction.matrix.code
|
|
fraction_type = fraction.type.code
|
|
analysis = notebook_line.analysis.rec_name
|
|
initial_unit = (notebook_line.initial_unit.symbol
|
|
if notebook_line.initial_unit else '')
|
|
priority = notebook_line.priority
|
|
urgent = notebook_line.urgent
|
|
report_date = (notebook_line.report_date or
|
|
notebook_line.results_estimated_date)
|
|
trace_report = fraction.sample.trace_report
|
|
sample_client_description = (
|
|
fraction.sample.sample_client_description)
|
|
key = (number, product_type, matrix, fraction_type,
|
|
analysis, priority, trace_report)
|
|
if key not in objects[date]['methods'][method_id]['lines']:
|
|
objects[date]['methods'][method_id]['lines'][key] = {
|
|
'order': order,
|
|
'number': number,
|
|
'product_type': product_type,
|
|
'matrix': matrix,
|
|
'fraction_type': fraction_type,
|
|
'analysis': analysis,
|
|
'priority': priority,
|
|
'urgent': urgent,
|
|
'report_date': report_date,
|
|
'trace_report': trace_report,
|
|
'sample_client_description': (
|
|
sample_client_description),
|
|
'initial_unit': initial_unit,
|
|
}
|
|
|
|
for k1 in objects.keys():
|
|
for k2, lines in objects[k1]['methods'].items():
|
|
sorted_lines = sorted(list(lines['lines'].values()),
|
|
key=lambda x: x['order'])
|
|
objects[k1]['methods'][k2]['lines'] = sorted_lines
|
|
|
|
report_context['objects'] = objects
|
|
|
|
return report_context
|