lims: add new wizard to generate results report

This commit is contained in:
Adrián Bernardi 2020-05-22 16:37:29 -03:00
parent a7aa3e0507
commit 5821abbb2a
9 changed files with 698 additions and 80 deletions

View File

@ -173,6 +173,7 @@ def register():
results_report.GenerateResultsReportResultAutExcludedNotebookLine, results_report.GenerateResultsReportResultAutExcludedNotebookLine,
results_report.GenerateResultsReportResultMan, results_report.GenerateResultsReportResultMan,
results_report.OpenSamplesPendingReportingStart, results_report.OpenSamplesPendingReportingStart,
results_report.GenerateReportStart,
certification.DuplicateAnalysisFamilyStart, certification.DuplicateAnalysisFamilyStart,
results_report.ResultsReportAnnulationStart, results_report.ResultsReportAnnulationStart,
sample.CountersampleStorageStart, sample.CountersampleStorageStart,
@ -303,6 +304,7 @@ def register():
results_report.DivideReports, results_report.DivideReports,
results_report.GenerateResultsReport, results_report.GenerateResultsReport,
results_report.OpenSamplesPendingReporting, results_report.OpenSamplesPendingReporting,
results_report.GenerateReport,
results_report.PrintResultsReport, results_report.PrintResultsReport,
certification.DuplicateAnalysisFamily, certification.DuplicateAnalysisFamily,
results_report.ServiceResultsReport, results_report.ServiceResultsReport,

View File

@ -2591,6 +2591,38 @@ msgctxt "field:lims.notebook.add_internal_relations.start,analysis_domain:"
msgid "Internal relations domain" msgid "Internal relations domain"
msgstr "Dominio para Relaciones internas" msgstr "Dominio para Relaciones internas"
msgctxt "field:lims.notebook.generate_results_report.start,corrective:"
msgid "Corrective"
msgstr "Rectificativo"
msgctxt "field:lims.notebook.generate_results_report.start,notebooks:"
msgid "Samples"
msgstr "Muestras"
msgctxt "field:lims.notebook.generate_results_report.start,preliminary:"
msgid "Preliminary"
msgstr "Preliminar"
msgctxt "field:lims.notebook.generate_results_report.start,report:"
msgid "Target Report"
msgstr "Informe destino"
msgctxt "field:lims.notebook.generate_results_report.start,report_domain:"
msgid "Target Report domain"
msgstr "Dominio para Informe destino"
msgctxt "field:lims.notebook.generate_results_report.start,report_readonly:"
msgid "Target Report readonly"
msgstr "Informe destino sólo lectura"
msgctxt "field:lims.notebook.generate_results_report.start,reports_created:"
msgid "Reports created"
msgstr "Informes creados"
msgctxt "field:lims.notebook.generate_results_report.start,type:"
msgid "Type"
msgstr "Tipo"
msgctxt "" msgctxt ""
"field:lims.notebook.internal_relations_calc_1.relation,internal_relation:" "field:lims.notebook.internal_relations_calc_1.relation,internal_relation:"
msgid "Internal relation" msgid "Internal relation"
@ -4684,6 +4716,10 @@ msgctxt "field:lims.results_report,english_report:"
msgid "English report" msgid "English report"
msgstr "Informe en inglés" msgstr "Informe en inglés"
msgctxt "field:lims.results_report,entry:"
msgid "Entry"
msgstr "Ingreso"
msgctxt "field:lims.results_report,generation_type:" msgctxt "field:lims.results_report,generation_type:"
msgid "Generation type" msgid "Generation type"
msgstr "Tipo de generación" msgstr "Tipo de generación"
@ -6691,6 +6727,10 @@ msgctxt "model:ir.action,name:wiz_notebook_evaluate_rules"
msgid "Evaluate Rules" msgid "Evaluate Rules"
msgstr "10) Evaluar Reglas de cuaderno" msgstr "10) Evaluar Reglas de cuaderno"
msgctxt "model:ir.action,name:wiz_notebook_generate_results_report"
msgid "Generate Results Report"
msgstr "Generar informe de resultados"
msgctxt "model:ir.action,name:wiz_notebook_line_evaluate_rules" msgctxt "model:ir.action,name:wiz_notebook_line_evaluate_rules"
msgid "Evaluate Rules" msgid "Evaluate Rules"
msgstr "10) Evaluar Reglas de cuaderno" msgstr "10) Evaluar Reglas de cuaderno"
@ -8383,6 +8423,10 @@ msgctxt "model:lims.notebook.evaluate_rules.start,name:"
msgid "Evaluate Rules" msgid "Evaluate Rules"
msgstr "Evaluar Reglas de cuaderno" msgstr "Evaluar Reglas de cuaderno"
msgctxt "model:lims.notebook.generate_results_report.start,name:"
msgid "Generate Results Report"
msgstr "Generar informe de resultados"
msgctxt "model:lims.notebook.initial_concentration_calc.start,name:" msgctxt "model:lims.notebook.initial_concentration_calc.start,name:"
msgid "Initial Concentration Calculation" msgid "Initial Concentration Calculation"
msgstr "Cálculo de concentración inicial" msgstr "Cálculo de concentración inicial"
@ -11263,6 +11307,22 @@ msgctxt "selection:lims.laboratory,section:"
msgid "Microbiology" msgid "Microbiology"
msgstr "Microbiología" msgstr "Microbiología"
msgctxt "selection:lims.notebook.generate_results_report.start,type:"
msgid "Complementary"
msgstr "Complementario"
msgctxt "selection:lims.notebook.generate_results_report.start,type:"
msgid "Corrective"
msgstr "Rectificativo"
msgctxt "selection:lims.notebook.generate_results_report.start,type:"
msgid "Final"
msgstr "Final"
msgctxt "selection:lims.notebook.generate_results_report.start,type:"
msgid "Preliminary"
msgstr "Preliminar"
msgctxt "" msgctxt ""
"selection:lims.notebook.internal_relations_calc_2.variable,converted_result_modifier:" "selection:lims.notebook.internal_relations_calc_2.variable,converted_result_modifier:"
msgid "<" msgid "<"
@ -13610,6 +13670,14 @@ msgctxt "wizard_button:lims.notebook.evaluate_rules,start,ok:"
msgid "Ok" msgid "Ok"
msgstr "Aceptar" msgstr "Aceptar"
msgctxt "wizard_button:lims.notebook.generate_results_report,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:lims.notebook.generate_results_report,start,generate:"
msgid "Generate"
msgstr "Generar"
msgctxt "wizard_button:lims.notebook.initial_concentration_calc,start,end:" msgctxt "wizard_button:lims.notebook.initial_concentration_calc,start,end:"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"

View File

@ -6,6 +6,7 @@ from io import BytesIO
from datetime import datetime from datetime import datetime
from PyPDF2 import PdfFileMerger from PyPDF2 import PdfFileMerger
from trytond import backend
from trytond.model import ModelView, ModelSQL, fields from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \ from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
Button Button
@ -28,11 +29,11 @@ __all__ = ['ResultsReport', 'ResultsReportVersion',
'GenerateResultsReportResultAutExcludedNotebook', 'GenerateResultsReportResultAutExcludedNotebook',
'GenerateResultsReportResultAutExcludedNotebookLine', 'GenerateResultsReportResultAutExcludedNotebookLine',
'GenerateResultsReport', 'OpenSamplesPendingReportingStart', 'GenerateResultsReport', 'OpenSamplesPendingReportingStart',
'OpenSamplesPendingReporting', 'PrintResultsReport', 'OpenSamplesPendingReporting', 'GenerateReportStart', 'GenerateReport',
'ServiceResultsReport', 'FractionResultsReport', 'SampleResultsReport', 'PrintResultsReport', 'ServiceResultsReport', 'FractionResultsReport',
'ResultsReportSample', 'ResultsReportAnnulationStart', 'SampleResultsReport', 'ResultsReportSample',
'ResultsReportAnnulation', 'ResultReport', 'GlobalResultReport', 'ResultsReportAnnulationStart', 'ResultsReportAnnulation', 'ResultReport',
'ResultReportTranscription'] 'GlobalResultReport', 'ResultReportTranscription']
def get_print_date(): def get_print_date():
@ -53,11 +54,24 @@ class ResultsReport(ModelSQL, ModelView):
number = fields.Char('Number', select=True, readonly=True) number = fields.Char('Number', select=True, readonly=True)
versions = fields.One2Many('lims.results_report.version', versions = fields.One2Many('lims.results_report.version',
'results_report', 'Laboratories', readonly=True) 'results_report', 'Laboratories', readonly=True)
party = fields.Many2One('party.party', 'Party', readonly=True)
entry = fields.Many2One('lims.entry', 'Entry', readonly=True)
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook')
report_grouper = fields.Integer('Report Grouper') report_grouper = fields.Integer('Report Grouper')
generation_type = fields.Char('Generation type') generation_type = fields.Char('Generation type')
cie_fraction_type = fields.Boolean('QA', readonly=True) cie_fraction_type = fields.Boolean('QA', readonly=True)
party = fields.Many2One('party.party', 'Party', readonly=True) english_report = fields.Boolean('English report')
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook') single_sending_report = fields.Function(fields.Boolean(
'Single sending'), 'get_single_sending_report',
searcher='search_single_sending_report')
single_sending_report_ready = fields.Function(fields.Boolean(
'Single sending Ready'), 'get_single_sending_report_ready')
create_date2 = fields.Function(fields.DateTime('Create Date'),
'get_create_date2', searcher='search_create_date2')
write_date2 = fields.DateTime('Write Date', readonly=True)
attachments = fields.One2Many('ir.attachment', 'resource', 'Attachments')
# PDF Report Cache
report_cache = fields.Binary('Report cache', readonly=True, report_cache = fields.Binary('Report cache', readonly=True,
file_id='report_cache_id', store_prefix='results_report') file_id='report_cache_id', store_prefix='results_report')
report_cache_id = fields.Char('Report cache id', readonly=True) report_cache_id = fields.Char('Report cache id', readonly=True)
@ -66,16 +80,24 @@ class ResultsReport(ModelSQL, ModelView):
file_id='report_cache_eng_id', store_prefix='results_report') file_id='report_cache_eng_id', store_prefix='results_report')
report_cache_eng_id = fields.Char('Report cache id', readonly=True) report_cache_eng_id = fields.Char('Report cache id', readonly=True)
report_format_eng = fields.Char('Report format', readonly=True) report_format_eng = fields.Char('Report format', readonly=True)
single_sending_report = fields.Function(fields.Boolean(
'Single sending'), 'get_single_sending_report', @classmethod
searcher='search_single_sending_report') def __register__(cls, module_name):
single_sending_report_ready = fields.Function(fields.Boolean( TableHandler = backend.get('TableHandler')
'Single sending Ready'), 'get_single_sending_report_ready') tablehandler = TableHandler(cls, module_name)
english_report = fields.Boolean('English report')
create_date2 = fields.Function(fields.DateTime('Create Date'), notebook_exist = tablehandler.column_exist('notebook')
'get_create_date2', searcher='search_create_date2') entry_exist = tablehandler.column_exist('entry')
write_date2 = fields.DateTime('Write Date', readonly=True) super(ResultsReport, cls).__register__(module_name)
attachments = fields.One2Many('ir.attachment', 'resource', 'Attachments')
if notebook_exist and not entry_exist:
cursor = Transaction().connection.cursor()
cursor.execute('UPDATE "lims_results_report" r '
'SET entry = s.entry '
'FROM "lims_sample" s '
'INNER JOIN "lims_fraction" f ON s.id = f.sample '
'INNER JOIN "lims_notebook" n ON f.id = n.fraction '
'WHERE r.notebook = n.id')
@classmethod @classmethod
def __setup__(cls): def __setup__(cls):
@ -120,47 +142,42 @@ class ResultsReport(ModelSQL, ModelView):
return [ return [
'number', 'number',
'versions', 'versions',
'party',
'entry',
'notebook',
'report_grouper', 'report_grouper',
'generation_type', 'generation_type',
'cie_fraction_type', 'cie_fraction_type',
'party',
'notebook',
'english_report', 'english_report',
'attachments', 'attachments',
] ]
def get_single_sending_report(self, name): def get_single_sending_report(self, name):
pool = Pool() if self.entry:
Notebook = pool.get('lims.notebook') return self.entry.single_sending_report
if self.notebook:
with Transaction().set_user(0):
notebook = Notebook(self.notebook.id)
return notebook.fraction.sample.entry.single_sending_report
return False return False
@classmethod @classmethod
def search_single_sending_report(cls, name, clause): def search_single_sending_report(cls, name, clause):
return [('notebook.fraction.sample.entry.' + name,) + return [('entry.' + name,) + tuple(clause[1:])]
tuple(clause[1:])]
def get_single_sending_report_ready(self, name): @classmethod
pool = Pool() def get_single_sending_report_ready(cls, reports, name):
Notebook = pool.get('lims.notebook') EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis') result = {}
for r in reports:
if not self.single_sending_report: if not r.single_sending_report or not not r.entry:
return False result[r.id] = False
with Transaction().set_user(0): elif EntryDetailAnalysis.search_count([
notebook = Notebook(self.notebook.id) ('entry', '=', r.entry),
if EntryDetailAnalysis.search([ ('report_grouper', '=', r.report_grouper),
('fraction', '=', notebook.fraction.id), ('report', '=', True),
('report', '=', True), ('state', '!=', 'reported'),
('report_grouper', '=', self.report_grouper), ]) > 0:
('state', '!=', 'reported'), result[r.id] = False
]): else:
return False result[r.id] = True
return True return result
def get_create_date2(self, name): def get_create_date2(self, name):
return self.create_date.replace(microsecond=0) return self.create_date.replace(microsecond=0)
@ -587,44 +604,46 @@ class ResultsReportVersionDetail(ModelSQL, ModelView):
@classmethod @classmethod
@ModelView.button @ModelView.button
def release(cls, details): def release(cls, details):
ResultsLine = Pool().get('lims.results_report.version.detail.line') ResultsSample = Pool().get('lims.results_report.version.detail.sample')
cls.link_notebook_lines(details) cls.link_notebook_lines(details)
for detail in details: for detail in details:
defaults = { # copy samples from previous valid version
'state': 'released',
'valid': True,
'release_uid': int(Transaction().user),
'release_date': datetime.now(),
}
valid_details = cls.search([ valid_details = cls.search([
('report_version', '=', detail.report_version.id), ('report_version', '=', detail.report_version.id),
('valid', '=', True), ('valid', '=', True),
]) ])
if valid_details: for vd in valid_details:
vd_ids = [] for valid_sample in vd.samples:
samples = [] sample_default = ResultsSample._get_sample_copy(
for vd in valid_details: valid_sample)
vd_ids.append(vd.id) existing_sample = ResultsSample.search([
for sample in vd.samples: ('version_detail', '=', detail.id),
notebook_lines = [ ('notebook', '=', valid_sample.notebook.id),
{'notebook_line': nline.notebook_line.id} ], limit=1)
for nline in sample.notebook_lines] if not existing_sample:
samples.append({ sample_default['version_detail'] = detail.id
'notebook': sample.notebook.id, sample_default['notebook'] = valid_sample.notebook.id
'notebook_lines': [('create', notebook_lines)], ResultsSample.create([sample_default])
}) else:
if samples: ResultsSample.write(existing_sample, sample_default)
defaults['samples'] = [('create', samples)]
cls.write(valid_details, { # delete samples from previous valid version
'valid': False, old_samples = ResultsSample.search([
}) ('version_detail.report_version', '=',
old_lines = ResultsLine.search([ detail.report_version.id),
('detail_sample.version_detail.id', 'in', vd_ids), ('version_detail.valid', '=', True),
]) ])
ResultsLine.delete(old_lines) ResultsSample.delete(old_samples)
cls.write([detail], defaults) cls.write(valid_details, {
'valid': False,
})
cls.write([detail], {
'state': 'released',
'valid': True,
'release_uid': int(Transaction().user),
'release_date': datetime.now(),
})
detail.generate_report() detail.generate_report()
@classmethod @classmethod
@ -843,11 +862,12 @@ class ResultsReportVersionDetail(ModelSQL, ModelView):
def get_fraction_comments(cls, details, name): def get_fraction_comments(cls, details, name):
result = {} result = {}
for d in details: for d in details:
result[d.id] = None comments = []
notebook = getattr(d.report_version.results_report, for sample in d.samples:
'notebook', None) fraction_comments = sample.notebook.fraction_comments
if notebook: if fraction_comments:
result[d.id] = getattr(notebook, 'fraction_comments') comments.append(fraction_comments)
result[d.id] = comments and '\n'.join(comments) or None
return result return result
def get_icon(self, name): def get_icon(self, name):
@ -855,6 +875,19 @@ class ResultsReportVersionDetail(ModelSQL, ModelView):
return 'lims-blue' return 'lims-blue'
return 'lims-white' return 'lims-white'
@classmethod
def _get_detail_copy(cls, detail):
detail_default = {}
detail_default['report_type_forced'] = detail.report_type_forced
detail_default['report_result_type_forced'] = (
detail.report_result_type_forced)
if detail.signer:
detail_default['signer'] = detail.signer.id
if detail.resultrange_origin:
detail_default['resultrange_origin'] = detail.resultrange_origin.id
detail_default['comments'] = str(detail.comments or '')
return detail_default
class ResultsReportVersionDetailSample(ModelSQL, ModelView): class ResultsReportVersionDetailSample(ModelSQL, ModelView):
'Results Report Version Detail Sample' 'Results Report Version Detail Sample'
@ -885,6 +918,16 @@ class ResultsReportVersionDetailSample(ModelSQL, ModelView):
result[name][s.id] = getattr(s.notebook, name, None) result[name][s.id] = getattr(s.notebook, name, None)
return result return result
@classmethod
def _get_sample_copy(cls, sample):
sample_default = {}
notebook_lines = [
{'notebook_line': nline.notebook_line.id}
for nline in sample.notebook_lines]
if notebook_lines:
sample_default['notebook_lines'] = [('create', notebook_lines)]
return sample_default
class ResultsReportVersionDetailLine(ModelSQL, ModelView): class ResultsReportVersionDetailLine(ModelSQL, ModelView):
'Results Report Version Detail Line' 'Results Report Version Detail Line'
@ -1647,6 +1690,7 @@ class GenerateResultsReport(Wizard):
if line.notebook.id not in notebooks: if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = { notebooks[line.notebook.id] = {
'party': line.notebook.party.id, 'party': line.notebook.party.id,
'entry': line.notebook.fraction.entry.id,
'notebook': line.notebook.id, 'notebook': line.notebook.id,
'divided_report': line.notebook.divided_report, 'divided_report': line.notebook.divided_report,
'english_report': ( 'english_report': (
@ -1676,6 +1720,7 @@ class GenerateResultsReport(Wizard):
} }
reports = { reports = {
'party': notebook['party'], 'party': notebook['party'],
'entry': notebook['entry'],
'notebook': notebook['notebook'], 'notebook': notebook['notebook'],
'report_grouper': 0, 'report_grouper': 0,
'generation_type': 'aut', 'generation_type': 'aut',
@ -1711,6 +1756,7 @@ class GenerateResultsReport(Wizard):
} }
reports = { reports = {
'party': notebook['party'], 'party': notebook['party'],
'entry': notebook['entry'],
'notebook': notebook['notebook'], 'notebook': notebook['notebook'],
'report_grouper': grouper, 'report_grouper': grouper,
'generation_type': 'aut', 'generation_type': 'aut',
@ -1907,6 +1953,7 @@ class GenerateResultsReport(Wizard):
if key not in parties: if key not in parties:
parties[key] = { parties[key] = {
'party': line.notebook.party.id, 'party': line.notebook.party.id,
'entry': line.notebook.fraction.entry.id,
'english_report': ( 'english_report': (
line.notebook.fraction.entry.english_report), line.notebook.fraction.entry.english_report),
'cie_fraction_type': ( 'cie_fraction_type': (
@ -1953,6 +2000,7 @@ class GenerateResultsReport(Wizard):
} }
reports = { reports = {
'party': party['party'], 'party': party['party'],
'entry': party['entry'],
'notebook': None, 'notebook': None,
'report_grouper': grouper, 'report_grouper': grouper,
'generation_type': 'man', 'generation_type': 'man',
@ -1971,6 +2019,7 @@ class GenerateResultsReport(Wizard):
if line.notebook.id not in notebooks: if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = { notebooks[line.notebook.id] = {
'party': line.notebook.party.id, 'party': line.notebook.party.id,
'entry': line.notebook.fraction.entry.id,
'notebook': line.notebook.id, 'notebook': line.notebook.id,
'divided_report': line.notebook.divided_report, 'divided_report': line.notebook.divided_report,
'english_report': ( 'english_report': (
@ -2001,6 +2050,7 @@ class GenerateResultsReport(Wizard):
} }
reports = { reports = {
'party': notebook['party'], 'party': notebook['party'],
'entry': notebook['entry'],
'notebook': notebook['notebook'], 'notebook': notebook['notebook'],
'report_grouper': 0, 'report_grouper': 0,
'generation_type': 'man', 'generation_type': 'man',
@ -2039,6 +2089,7 @@ class GenerateResultsReport(Wizard):
} }
reports = { reports = {
'party': notebook['party'], 'party': notebook['party'],
'entry': notebook['entry'],
'notebook': notebook['notebook'], 'notebook': notebook['notebook'],
'report_grouper': grouper, 'report_grouper': grouper,
'generation_type': 'man', 'generation_type': 'man',
@ -2069,6 +2120,7 @@ class GenerateResultsReport(Wizard):
actual_report = ResultsReport.search([ actual_report = ResultsReport.search([
('party', '=', reports['party']), ('party', '=', reports['party']),
('entry', '=', reports['entry']),
('notebook', '=', reports['notebook']), ('notebook', '=', reports['notebook']),
('report_grouper', '=', reports['report_grouper']), ('report_grouper', '=', reports['report_grouper']),
('generation_type', '=', reports['generation_type']), ('generation_type', '=', reports['generation_type']),
@ -2191,6 +2243,425 @@ class OpenSamplesPendingReporting(Wizard):
return 'end' return 'end'
class GenerateReportStart(ModelView):
'Generate Results Report'
__name__ = 'lims.notebook.generate_results_report.start'
notebooks = fields.One2Many('lims.notebook', None, 'Samples',
readonly=True)
report = fields.Many2One('lims.results_report', 'Target Report',
states={'readonly': Bool(Eval('report_readonly'))},
domain=[('id', 'in', Eval('report_domain'))],
depends=['report_readonly', 'report_domain'])
report_readonly = fields.Boolean('Target Report readonly')
report_domain = fields.One2Many('lims.results_report', None,
'Target Report domain')
type = fields.Selection([
('preliminary', 'Preliminary'),
('final', 'Final'),
('complementary', 'Complementary'),
('corrective', 'Corrective'),
], 'Type', states={'readonly': True})
preliminary = fields.Boolean('Preliminary')
corrective = fields.Boolean('Corrective',
states={'readonly': ~Eval('type').in_(
['complementary', 'corrective'])},
depends=['type'])
reports_created = fields.One2Many('lims.results_report.version.detail',
None, 'Reports created')
@fields.depends('report', 'preliminary', 'corrective')
def on_change_with_type(self, name=None):
if self.preliminary:
return 'preliminary'
report_state = self._get_report_state()
if report_state == 'draft':
return 'final'
if self.corrective:
return 'corrective'
return 'complementary'
def _get_report_state(self):
ResultsDetail = Pool().get('lims.results_report.version.detail')
if not self.report:
return 'draft'
report_id = self.report.id
laboratory_id = Transaction().context.get(
'samples_pending_reporting_laboratory', None)
if not laboratory_id:
return 'draft'
last_detail = ResultsDetail.search([
('report_version.results_report', '=', report_id),
('report_version.laboratory', '=', laboratory_id),
], order=[('id', 'DESC')], limit=1)
if last_detail:
return last_detail[0].state
return 'draft'
class GenerateReport(Wizard):
'Generate Results Report'
__name__ = 'lims.notebook.generate_results_report'
start = StateView('lims.notebook.generate_results_report.start',
'lims.notebook_generate_results_report_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Generate', 'generate', 'tryton-ok', default=True),
])
generate = StateTransition()
open_ = StateAction('lims.act_lims_results_report_version_detail')
def default_start(self, fields):
pool = Pool()
Notebook = pool.get('lims.notebook')
ResultsReport = pool.get('lims.results_report')
ResultsDetail = pool.get('lims.results_report.version.detail')
res = {
'notebooks': [],
'report_readonly': False,
'report_domain': [],
'type': 'final',
'preliminary': False,
'corrective': False,
}
party = None
report_grouper = None
cie_fraction_type = None
for notebook in Notebook.browse(Transaction().context['active_ids']):
res['notebooks'].append(notebook.id)
if not res['report_readonly']:
if not party:
party = notebook.party.id
elif party != notebook.party.id:
res['report_readonly'] = True
for line in notebook.lines:
if not report_grouper:
report_grouper = line.analysis_detail.report_grouper
elif report_grouper != line.analysis_detail.report_grouper:
res['report_readonly'] = True
break
if cie_fraction_type is None:
cie_fraction_type = notebook.fraction.cie_fraction_type
elif cie_fraction_type != notebook.fraction.cie_fraction_type:
res['report_readonly'] = True
if notebook.state != 'complete':
res['preliminary'] = True
res['type'] = 'preliminary'
if not res['report_readonly']:
if res['preliminary']:
laboratory_id = Transaction().context.get(
'samples_pending_reporting_laboratory', None)
last_detail = ResultsDetail.search([
('party', '=', party),
('laboratory', '=', laboratory_id),
], order=[('id', 'DESC')], limit=1)
if last_detail and last_detail[0].state == 'preliminary':
res['report_domain'] = [
last_detail[0].report_version.results_report.id]
else:
reports = ResultsReport.search([
('party', '=', party),
('report_grouper', '=', report_grouper),
('cie_fraction_type', '=', cie_fraction_type),
])
if reports:
res['report_domain'] = [r.id for r in reports]
return res
def transition_generate(self):
pool = Pool()
Laboratory = pool.get('lims.laboratory')
ResultsVersion = pool.get('lims.results_report.version')
ResultsDetail = pool.get('lims.results_report.version.detail')
ResultsSample = pool.get('lims.results_report.version.detail.sample')
laboratory_id = Transaction().context.get(
'samples_pending_reporting_laboratory', None)
signer = Laboratory(laboratory_id).default_signer.id
reports_created = []
state = ('in_progress' if self.start.type == 'preliminary' else
'complete')
if self.start.report: # Result report selected
samples = []
for notebook in self.start.notebooks:
lines = self._get_notebook_lines(notebook.id, laboratory_id,
state)
notebook_lines = [{'notebook_line': line.id} for line in lines]
samples.append({
'notebook': notebook.id,
'notebook_lines': [('create', notebook_lines)],
})
details = {
'report_type_forced': 'polisample',
'type': self.start.type,
'signer': signer,
'samples': [('create', samples)],
}
actual_version = ResultsVersion.search([
('results_report', '=', self.start.report.id),
('laboratory', '=', laboratory_id),
], limit=1)
if not actual_version:
version, = ResultsVersion.create([{
'results_report': self.start.report.id,
'laboratory': laboratory_id,
'details': [('create', [details])],
}])
reports_details = [d.id for d in version.details]
else:
actual_version = actual_version[0]
draft_detail = ResultsDetail.search([
('report_version', '=', actual_version.id),
('state', '=', 'draft'),
], limit=1)
if not draft_detail:
details['report_version'] = actual_version.id
valid_detail = ResultsDetail.search([
('report_version', '=', actual_version.id),
('valid', '=', True),
], limit=1)
if valid_detail:
valid_detail = valid_detail[0]
details.update(ResultsDetail._get_detail_copy(
valid_detail))
detail, = ResultsDetail.create([details])
reports_details = [detail.id]
else:
draft_detail = draft_detail[0]
for sample in samples:
existing_sample = ResultsSample.search([
('version_detail', '=', draft_detail.id),
('notebook', '=', sample['notebook']),
], limit=1)
if not existing_sample:
sample['version_detail'] = draft_detail.id
ResultsSample.create([sample])
else:
del sample['notebook']
ResultsSample.write(existing_sample, sample)
del details['type']
del details['signer']
del details['samples']
ResultsDetail.write([draft_detail], details)
reports_details = [draft_detail.id]
reports_created.extend(reports_details)
else: # Not Result report selected
parties = {}
for notebook in self.start.notebooks:
key = (notebook.party.id, notebook.fraction.cie_fraction_type)
if key not in parties:
parties[key] = {
'party': notebook.party.id,
'entry': notebook.fraction.entry.id,
'cie_fraction_type': (
notebook.fraction.cie_fraction_type),
'english_report': (
notebook.fraction.entry.english_report),
'lines': [],
}
lines = self._get_notebook_lines(notebook.id, laboratory_id,
state)
parties[key]['lines'].extend(lines)
reports_details = []
for party in parties.values():
grouped_reports = {}
for line in party['lines']:
report_grouper = line.analysis_detail.report_grouper
if report_grouper not in grouped_reports:
grouped_reports[report_grouper] = []
grouped_reports[report_grouper].append(line)
for grouper, nlines in grouped_reports.items():
notebooks = {}
for line in nlines:
if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = {
'notebook': line.notebook.id,
'lines': [],
}
notebooks[line.notebook.id]['lines'].append(line)
samples = []
for notebook in notebooks.values():
notebook_lines = [{'notebook_line': line.id}
for line in notebook['lines']]
samples.append({
'notebook': notebook['notebook'],
'notebook_lines': [('create', notebook_lines)],
})
details = {
'report_type_forced': 'polisample',
'type': self.start.type,
'signer': signer,
'samples': [('create', samples)],
}
versions = {
'laboratory': laboratory_id,
'details': [('create', [details])],
}
reports = {
'party': party['party'],
'entry': party['entry'],
'notebook': None,
'report_grouper': grouper,
'cie_fraction_type': party['cie_fraction_type'],
'english_report': party['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(laboratory_id,
reports, versions, details, samples)
reports_details.extend(report_detail)
reports_created.extend(reports_details)
self.start.reports_created = reports_created
return 'open_'
def _get_notebook_lines(self, notebook_id, laboratory_id, state):
cursor = Transaction().connection.cursor()
pool = Pool()
ResultsLine = pool.get('lims.results_report.version.detail.line')
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
Notebook = pool.get('lims.notebook')
draft_lines_ids = []
draft_lines = ResultsLine.search([
('detail_sample.notebook', '=', notebook_id),
('detail_sample.version_detail.laboratory', '=', laboratory_id),
('detail_sample.version_detail.state', 'in', ['draft', 'revised']),
('detail_sample.version_detail.type', '!=', 'preliminary'),
])
if draft_lines:
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
clause = [
('notebook', '=', notebook_id),
('laboratory', '=', laboratory_id),
('notebook.fraction.type.report', '=', True),
('report', '=', True),
('annulled', '=', False),
('results_report', '=', None),
('id', 'not in', draft_lines_ids),
]
if state == 'in_progress':
clause.extend(Notebook._get_samples_in_progress_clause())
else:
clause.append(('accepted', '=', True))
excluded_notebooks = Notebook._get_excluded_notebooks(
[notebook_id], laboratory_id)
if excluded_notebooks:
for n_id, grouper in excluded_notebooks:
cursor.execute('SELECT nl.id '
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + EntryDetailAnalysis._table + '" d '
'ON d.id = nl.analysis_detail '
'WHERE nl.notebook = %s AND d.report_grouper = %s',
(n_id, grouper))
excluded_notebook_lines = [x[0] for x in cursor.fetchall()]
clause.append(('id', 'not in', excluded_notebook_lines))
return NotebookLine.search(clause)
def _get_results_report(self, laboratory_id, reports, versions, details,
samples, append=True):
pool = Pool()
ResultsReport = pool.get('lims.results_report')
ResultsVersion = pool.get('lims.results_report.version')
ResultsDetail = pool.get('lims.results_report.version.detail')
ResultsSample = pool.get('lims.results_report.version.detail.sample')
if not append:
report, = ResultsReport.create([reports])
reports_details = [d.id for d in report.versions[0].details]
return reports_details
actual_report = ResultsReport.search([
('party', '=', reports['party']),
('entry', '=', reports['entry']),
('report_grouper', '=', reports['report_grouper']),
('cie_fraction_type', '=', reports['cie_fraction_type']),
], limit=1)
if not actual_report:
report, = ResultsReport.create([reports])
reports_details = [d.id for d in report.versions[0].details]
return reports_details
actual_report = actual_report[0]
actual_version = ResultsVersion.search([
('results_report', '=', actual_report.id),
('laboratory', '=', laboratory_id),
], limit=1)
if not actual_version:
version, = ResultsVersion.create([{
'results_report': actual_report.id,
'laboratory': laboratory_id,
'details': [('create', [details])],
}])
reports_details = [d.id for d in version.details]
return reports_details
actual_version = actual_version[0]
draft_detail = ResultsDetail.search([
('report_version', '=', actual_version.id),
('state', '=', 'draft'),
], limit=1)
if not draft_detail:
details['report_version'] = actual_version.id
valid_detail = ResultsDetail.search([
('report_version', '=', actual_version.id),
('valid', '=', True),
], limit=1)
if valid_detail:
valid_detail = valid_detail[0]
details.update(ResultsDetail._get_detail_copy(valid_detail))
detail, = ResultsDetail.create([details])
reports_details = [detail.id]
return reports_details
draft_detail = draft_detail[0]
for sample in samples:
existing_sample = ResultsSample.search([
('version_detail', '=', draft_detail.id),
('notebook', '=', sample['notebook']),
], limit=1)
if not existing_sample:
sample['version_detail'] = draft_detail.id
ResultsSample.create([sample])
else:
del sample['notebook']
ResultsSample.write(existing_sample, sample)
reports_details = [draft_detail.id]
return reports_details
def do_open_(self, action):
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', [r.id for r in self.start.reports_created]),
])
self.start.reports_created = None
return action, {}
def transition_open_(self):
return 'end'
def end(self):
return 'reload'
class PrintResultsReport(Wizard): class PrintResultsReport(Wizard):
'Print Results Report' 'Print Results Report'
__name__ = 'lims.print_results_report' __name__ = 'lims.print_results_report'

View File

@ -285,6 +285,24 @@
<field name="wiz_name">lims.samples_pending_reporting</field> <field name="wiz_name">lims.samples_pending_reporting</field>
</record> </record>
<!-- Wizard Generate Results Report -->
<record model="ir.ui.view" id="notebook_generate_results_report_view_form">
<field name="model">lims.notebook.generate_results_report.start</field>
<field name="type">form</field>
<field name="name">notebook_generate_results_report_form</field>
</record>
<record model="ir.action.wizard" id="wiz_notebook_generate_results_report">
<field name="name">Generate Results Report</field>
<field name="wiz_name">lims.notebook.generate_results_report</field>
</record>
<record model="ir.action.keyword" id="wiz_notebook_generate_results_report_keyword">
<field name="keyword">form_action</field>
<field name="model">lims.notebook,-2</field>
<field name="action" ref="wiz_notebook_generate_results_report"/>
</record>
<!-- Wizard Generate Results Reports --> <!-- Wizard Generate Results Reports -->
<record model="ir.ui.view" id="lims_generate_results_report_start_view_form"> <record model="ir.ui.view" id="lims_generate_results_report_start_view_form">

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<form>
<label name="report"/>
<field name="report"/>
<group id="type" colspan="2" col="4">
<label name="type"/>
<field name="type"/>
<label name="corrective"/>
<field name="corrective" xexpand="0"/>
</group>
<field name="notebooks" colspan="4" mode="tree,form"
view_ids="lims.lims_samples_pending_reporting_view_list,lims.lims_samples_pending_reporting_view_form"/>
<group id="invisible" colspan="4" col="1">
<field name="report_readonly" invisible="1"/>
<field name="report_domain" invisible="1"/>
<field name="preliminary" invisible="1"/>
</group>
</form>

View File

@ -56,6 +56,16 @@ class ResultsReportVersionDetail(metaclass=PoolMeta):
for sample in self.samples: for sample in self.samples:
sample.diagnosis = content sample.diagnosis = content
@classmethod
def _get_detail_copy(cls, detail):
detail_default = super(ResultsReportVersionDetail,
cls)._get_detail_copy(detail)
if detail.diagnostician:
detail_default['diagnostician'] = detail.diagnostician.id
if detail.diagnosis_template:
detail_default['diagnosis_template'] = detail.diagnosis_template.id
return detail_default
class ResultsReportVersionDetailSample(metaclass=PoolMeta): class ResultsReportVersionDetailSample(metaclass=PoolMeta):
__name__ = 'lims.results_report.version.detail.sample' __name__ = 'lims.results_report.version.detail.sample'
@ -78,6 +88,14 @@ class ResultsReportVersionDetailSample(metaclass=PoolMeta):
self.version_detail.diagnosis_template.diagnosis_states] self.version_detail.diagnosis_template.diagnosis_states]
return [] return []
@classmethod
def _get_sample_copy(cls, sample):
sample_default = super(ResultsReportVersionDetailSample,
cls)._get_sample_copy(sample)
sample_default['diagnosis'] = sample.diagnosis
sample_default['diagnosis_states'] = sample.diagnosis_states
return sample_default
class ResultReport(metaclass=PoolMeta): class ResultReport(metaclass=PoolMeta):
__name__ = 'lims.result_report' __name__ = 'lims.result_report'

View File

@ -30,6 +30,18 @@ class ResultsReportVersionDetailSample(metaclass=PoolMeta):
domain=[('component', '=', Eval('component'))], domain=[('component', '=', Eval('component'))],
depends=['component']) depends=['component'])
@classmethod
def _get_sample_copy(cls, sample):
sample_default = super(ResultsReportVersionDetailSample,
cls)._get_sample_copy(sample)
sample_default['precedent1'] = (sample.precedent1 and
sample.precedent1 or None)
sample_default['precedent2'] = (sample.precedent2 and
sample.precedent2 or None)
sample_default['precedent3'] = (sample.precedent3 and
sample.precedent3 or None)
return sample_default
class ResultsReportVersionDetailLine(metaclass=PoolMeta): class ResultsReportVersionDetailLine(metaclass=PoolMeta):
__name__ = 'lims.results_report.version.detail.line' __name__ = 'lims.results_report.version.detail.line'
@ -91,6 +103,7 @@ class ResultsReportVersionDetailLine(metaclass=PoolMeta):
res = round(float(result), decimals) res = round(float(result), decimals)
if decimals == 0: if decimals == 0:
res = int(res) res = int(res)
res = str(res)
else: else:
res = '' res = ''

View File

@ -1137,6 +1137,8 @@
{% endif %} <!-- END POLISAMPLE --> {% endif %} <!-- END POLISAMPLE -->
<br/><br/>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>

View File

@ -29,6 +29,14 @@ class ResultsReportVersionDetail(metaclass=PoolMeta):
if 'required' in cls.resultrange_origin.states: if 'required' in cls.resultrange_origin.states:
del cls.resultrange_origin.states['required'] del cls.resultrange_origin.states['required']
@classmethod
def _get_detail_copy(cls, detail):
detail_default = super(ResultsReportVersionDetail,
cls)._get_detail_copy(detail)
if detail.template:
detail_default['template'] = detail.template.id
return detail_default
class ResultReport(metaclass=PoolMeta): class ResultReport(metaclass=PoolMeta):
__name__ = 'lims.result_report' __name__ = 'lims.result_report'