lims_industry: edit sample wizard: add edition log

This commit is contained in:
Adrián Bernardi 2020-12-17 20:09:50 -03:00
parent 0afdc805a9
commit 9f4a5ef933
9 changed files with 323 additions and 17 deletions

View File

@ -34,6 +34,7 @@ def register():
analysis.Analysis,
sample.Entry,
sample.Sample,
sample.SampleEditionLog,
sample.Fraction,
sample.FractionType,
sample.CreateSampleStart,

View File

@ -466,6 +466,10 @@ msgctxt "field:lims.sample,component:"
msgid "Component"
msgstr "Componente"
msgctxt "field:lims.sample,edition_log:"
msgid "Edition log"
msgstr "Registro de cambios"
msgctxt "field:lims.sample,equipment:"
msgid "Equipment"
msgstr "Equipo"
@ -566,6 +570,10 @@ msgctxt "field:lims.sample.attribute.set,name:"
msgid "Name"
msgstr "Nombre"
msgctxt "field:lims.sample.edit.start,comercial_product:"
msgid "Comercial Product"
msgstr "Producto comercial"
msgctxt "field:lims.sample.edit.start,component:"
msgid "Component"
msgstr "Componente"
@ -576,12 +584,32 @@ msgstr "Equipo"
msgctxt "field:lims.sample.edit.start,party:"
msgid "Party"
msgstr "Tercero"
msgstr "Entidad"
msgctxt "field:lims.sample.edit.start,plant:"
msgid "Plant"
msgstr "Planta"
msgctxt "field:lims.sample.edition.log,create_date2:"
msgid "Created at"
msgstr "Creado el"
msgctxt "field:lims.sample.edition.log,field:"
msgid "Field"
msgstr "Campo"
msgctxt "field:lims.sample.edition.log,final_value:"
msgid "Final value"
msgstr "Valor final"
msgctxt "field:lims.sample.edition.log,initial_value:"
msgid "Initial value"
msgstr "Valor inicial"
msgctxt "field:lims.sample.edition.log,sample:"
msgid "Sample"
msgstr "Muestra"
msgctxt "field:lims.sampling.type,description:"
msgid "Description"
msgstr "Descripción"
@ -726,6 +754,16 @@ msgctxt "model:ir.message,text:msg_component_type_unique"
msgid "A component of the same type already exists for the equipment"
msgstr "Ya existe un componente del mismo tipo para el equipo"
msgctxt "model:ir.message,text:msg_edit_entry_party"
msgid "You must select all samples from the same entry to change the Party"
msgstr ""
"Debe seleccionar todas las muestras del mismo ingreso para cambiar la "
"entidad"
msgctxt "model:ir.message,text:msg_edit_results_report_party"
msgid "Sample \"%(sample)s\" has a Results report already released"
msgstr "La muestra «%(sample)s» tiene un Informe de resultados ya publicado"
msgctxt "model:ir.message,text:msg_equipment_name_unique"
msgid "There is already an equipment with the same name for the plant"
msgstr "Ya existe un equipo con el mismo nombre para la planta"
@ -859,6 +897,10 @@ msgctxt "model:lims.sample.edit.start,name:"
msgid "Edit Samples"
msgstr "Modificar muestras"
msgctxt "model:lims.sample.edition.log,name:"
msgid "Sample Edition Log"
msgstr "Registro de cambios en Muestra"
msgctxt "model:lims.sampling.type,name:"
msgid "Sampling Type"
msgstr "Muestreo"
@ -871,6 +913,30 @@ msgctxt "selection:lims.configuration,mail_ack_report_grouping:"
msgid "Plant"
msgstr "Planta"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Comercial Product"
msgstr "Producto comercial"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Component"
msgstr "Componente"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Equipment"
msgstr "Equipo"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Matrix"
msgstr "Matriz"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Party"
msgstr "Entidad"
msgctxt "selection:lims.sample.edition.log,field:"
msgid "Product type"
msgstr "Tipo de producto"
msgctxt "selection:lims.trend.chart,filter:"
msgid "Same Component"
msgstr "Mismo Componente"
@ -999,6 +1065,14 @@ msgctxt "view:lims.results_report.version.detail.sample:"
msgid "Precedents"
msgstr "Antecedentes"
msgctxt "view:lims.sample.edition.log:"
msgid "Time"
msgstr "Hora"
msgctxt "view:lims.sample:"
msgid "Edition log"
msgstr "Registro de cambios"
msgctxt "view:lims.sample:"
msgid "Industry"
msgstr "Industria"

View File

@ -49,5 +49,11 @@
<record model="ir.message" id="msg_equipment_template_unique">
<field name="text">There is already a equipment template with the same type, brand and model</field>
</record>
<record model="ir.message" id="msg_edit_entry_party">
<field name="text">You must select all samples from the same entry to change the Party</field>
</record>
<record model="ir.message" id="msg_edit_results_report_party">
<field name="text">Sample "%(sample)s" has a Results report already released</field>
</record>
</data>
</tryton>

View File

@ -2,11 +2,13 @@
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.model import ModelView, fields
from trytond.model import ModelSQL, ModelView, fields
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, Bool
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
class Entry(metaclass=PoolMeta):
@ -64,6 +66,8 @@ class Sample(metaclass=PoolMeta):
changed_oil = fields.Boolean('Did change Oil?')
changed_oil_filter = fields.Boolean('Did change Oil Filter?')
changed_air_filter = fields.Boolean('Did change Air Filter?')
edition_log = fields.One2Many('lims.sample.edition.log', 'sample',
'Edition log', readonly=True)
@classmethod
def __setup__(cls):
@ -182,6 +186,48 @@ class Sample(metaclass=PoolMeta):
return result
class SampleEditionLog(ModelSQL, ModelView):
'Sample Edition Log'
__name__ = 'lims.sample.edition.log'
create_date2 = fields.Function(fields.DateTime('Created at'),
'get_create_date2', searcher='search_create_date2')
sample = fields.Many2One('lims.sample', 'Sample', required=True,
ondelete='CASCADE', select=True, readonly=True)
field = fields.Selection([
('party', 'Party'),
('equipment', 'Equipment'),
('component', 'Component'),
('product_type', 'Product type'),
('comercial_product', 'Comercial Product'),
('matrix', 'Matrix'),
], 'Field', readonly=True)
initial_value = fields.Text('Initial value', readonly=True)
final_value = fields.Text('Final value', readonly=True)
@classmethod
def __setup__(cls):
super().__setup__()
cls._order.insert(0, ('create_date', 'ASC'))
def get_create_date2(self, name):
return self.create_date.replace(microsecond=0)
@classmethod
def search_create_date2(cls, name, clause):
cursor = Transaction().connection.cursor()
operator_ = clause[1:2][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE create_date' + operator_ + ' %s',
clause[2:3])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
def order_create_date2(cls, tables):
return cls.create_date.convert_order('create_date', tables, cls)
class Fraction(metaclass=PoolMeta):
__name__ = 'lims.fraction'
@ -450,6 +496,13 @@ class EditSampleStart(ModelView):
component = fields.Many2One('lims.component', 'Component',
domain=[('equipment', '=', Eval('equipment'))],
depends=['equipment'])
comercial_product = fields.Many2One('lims.comercial.product',
'Comercial Product')
@fields.depends('component')
def on_change_component(self):
if self.component and self.component.comercial_product:
self.comercial_product = self.component.comercial_product.id
class EditSample(Wizard):
@ -466,7 +519,8 @@ class EditSample(Wizard):
def _get_filtered_samples(self):
Sample = Pool().get('lims.sample')
samples = Sample.browse(Transaction().context['active_ids'])
return [s for s in samples if s.entry.state == 'draft']
#return [s for s in samples if s.entry.state == 'draft']
return samples
def default_start(self, fields):
samples = self._get_filtered_samples()
@ -482,29 +536,154 @@ class EditSample(Wizard):
}
def transition_confirm(self):
component_changed = bool(self.start.component)
equipment_changed = bool(self.start.equipment)
party_changed = bool(self.start.party)
SampleEditionLog = Pool().get('lims.sample.edition.log')
samples = self._get_filtered_samples()
samples_to_edit_party = []
for sample in samples:
if component_changed:
sample.component = self.start.component.id
if equipment_changed:
check_typifications = False
log = []
if (self.start.party and
self.start.party != sample.party):
log.append({
'sample': sample.id,
'field': 'party',
'initial_value': sample.party.rec_name,
'final_value': self.start.party.rec_name,
})
samples_to_edit_party.append(sample)
if (self.start.equipment and
self.start.equipment != sample.equipment):
log.append({
'sample': sample.id,
'field': 'equipment',
'initial_value': (sample.equipment and
sample.equipment.rec_name or None),
'final_value': self.start.equipment.rec_name,
})
sample.equipment = self.start.equipment.id
if party_changed:
if self.start.party.id != sample.party.id:
entry = self._new_entry()
sample.entry = entry.id
if (self.start.component and
self.start.component != sample.component):
log.append({
'sample': sample.id,
'field': 'component',
'initial_value': (sample.component and
sample.component.rec_name or None),
'final_value': self.start.component.rec_name,
})
sample.component = self.start.component.id
if (self.start.component.product_type and
self.start.component.product_type !=
sample.product_type):
check_typifications = True
log.append({
'sample': sample.id,
'field': 'product_type',
'initial_value': sample.product_type.rec_name,
'final_value': (
self.start.component.product_type.rec_name),
})
sample.product_type = self.start.component.product_type.id
if (self.start.comercial_product and
self.start.comercial_product != sample.comercial_product):
log.append({
'sample': sample.id,
'field': 'comercial_product',
'initial_value': (sample.comercial_product and
sample.comercial_product.rec_name),
'final_value': self.start.comercial_product.rec_name,
})
sample.comercial_product = self.start.comercial_product.id
if (self.start.comercial_product.matrix and
self.start.comercial_product.matrix !=
self.start.comercial_product.matrix):
check_typifications = True
log.append({
'sample': sample.id,
'field': 'matrix',
'initial_value': sample.matrix.rec_name,
'final_value': (
self.start.comercial_product.matrix.rec_name),
})
sample.matrix = self.start.comercial_product.matrix.id
if check_typifications:
self.check_typifications(sample)
sample.save()
if log:
SampleEditionLog.create(log)
for sample in samples_to_edit_party:
self.edit_party(sample, samples)
return 'end'
def _new_entry(self):
def edit_party(self, sample, samples):
self._edit_entry_party(sample, samples)
self._edit_results_report_party(sample, samples)
def _edit_entry_party(self, sample, samples):
pool = Pool()
Sample = pool.get('lims.sample')
Entry = pool.get('lims.entry')
entry = Entry()
if Sample.search_count([
('entry', '=', sample.entry.id),
('id', 'not in', [s.id for s in samples]),
]) > 0:
raise UserError(gettext('lims_industry.msg_edit_entry_party'))
entry = Entry(sample.entry.id)
entry.party = self.start.party.id
entry.invoice_party = self.start.party.id
entry.state = 'draft'
entry.ack_report_format = None
entry.ack_report_cache = None
entry.save()
return entry
def _edit_results_report_party(self, sample, samples):
cursor = Transaction().connection.cursor()
pool = Pool()
Fraction = pool.get('lims.fraction')
Notebook = pool.get('lims.notebook')
ResultsSample = pool.get('lims.results_report.version.detail.sample')
ResultsDetail = pool.get('lims.results_report.version.detail')
ResultsVersion = pool.get('lims.results_report.version')
ResultsReport = pool.get('lims.results_report')
if sample.has_results_report:
raise UserError(gettext(
'lims_industry.msg_edit_results_report_party',
sample=sample.rec_name))
cursor.execute('SELECT rv.results_report '
'FROM "' + ResultsVersion._table + '" rv '
'INNER JOIN "' + ResultsDetail._table + '" rd '
'ON rv.id = rd.report_version '
'INNER JOIN "' + ResultsSample._table + '" rs '
'ON rd.id = rs.version_detail '
'INNER JOIN "' + Notebook._table + '" n '
'ON n.id = rs.notebook '
'INNER JOIN "' + Fraction._table + '" f '
'ON f.id = n.fraction '
'WHERE f.sample = %s '
'AND rd.state NOT IN (\'released\', \'annulled\')',
(str(sample.id),))
reports_ids = [x[0] for x in cursor.fetchall()]
if not reports_ids:
return
reports = ResultsReport.browse(reports_ids)
ResultsReport.write(reports, {'party': self.start.party.id})
def check_typifications(self, sample):
analysis_domain_ids = sample.on_change_with_analysis_domain()
for f in sample.fractions:
for s in f.services:
if s.analysis.id not in analysis_domain_ids:
raise UserError(gettext('lims.msg_not_typified',
analysis=s.analysis.rec_name,
product_type=sample.product_type.rec_name,
matrix=sample.matrix.rec_name))

View File

@ -15,6 +15,19 @@
<field name="name">sample_list</field>
</record>
<!-- Sample Edition Log -->
<record model="ir.ui.view" id="sample_edition_log_view_list">
<field name="model">lims.sample.edition.log</field>
<field name="type">tree</field>
<field name="name">sample_edition_log_list</field>
</record>
<record model="ir.ui.view" id="sample_edition_log_view_form">
<field name="model">lims.sample.edition.log</field>
<field name="type">form</field>
<field name="name">sample_edition_log_form</field>
</record>
<!-- Fraction Type -->
<record model="ir.ui.view" id="fraction_type_view_form">

View File

@ -8,4 +8,6 @@
<field name="equipment"/>
<label name="component"/>
<field name="component"/>
<label name="comercial_product"/>
<field name="comercial_product"/>
</form>

View File

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<form>
<label name="create_date2"/>
<field name="create_date2"/>
<label name="create_uid"/>
<field name="create_uid"/>
<label name="field"/>
<field name="field" colspan="3"/>
<group id="initial_value" colspan="2" yexpand="1" yfill="1" col="1">
<separator name="initial_value"/>
<field name="initial_value"/>
</group>
<group id="final_value" colspan="2" yexpand="1" yfill="1" col="1">
<separator name="final_value"/>
<field name="final_value"/>
</group>
</form>

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<tree>
<field name="create_date2" widget="date"/>
<field name="create_date2" widget="time" string="Time"/>
<field name="create_uid"/>
<field name="field"/>
<field name="initial_value"/>
<field name="final_value"/>
</tree>

View File

@ -62,4 +62,9 @@
<field name="attributes_domain" colspan="4" invisible="1"/>
</page>
</xpath>
<xpath expr="/form/notebook/page[@id='comments']" position="after">
<page id="edition_log" string="Edition log">
<field name="edition_log" colspan="4"/>
</page>
</xpath>
</data>