diff --git a/lims_industry/__init__.py b/lims_industry/__init__.py
index 7ea5678b..01e3a4cb 100644
--- a/lims_industry/__init__.py
+++ b/lims_industry/__init__.py
@@ -34,6 +34,7 @@ def register():
analysis.Analysis,
sample.Entry,
sample.Sample,
+ sample.SampleEditionLog,
sample.Fraction,
sample.FractionType,
sample.CreateSampleStart,
diff --git a/lims_industry/locale/es.po b/lims_industry/locale/es.po
index c59a7728..0a2e873e 100644
--- a/lims_industry/locale/es.po
+++ b/lims_industry/locale/es.po
@@ -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"
diff --git a/lims_industry/message.xml b/lims_industry/message.xml
index df7f561a..79b90418 100644
--- a/lims_industry/message.xml
+++ b/lims_industry/message.xml
@@ -49,5 +49,11 @@
There is already a equipment template with the same type, brand and model
+
+ You must select all samples from the same entry to change the Party
+
+
+ Sample "%(sample)s" has a Results report already released
+
diff --git a/lims_industry/sample.py b/lims_industry/sample.py
index 688fb14d..62b646e1 100644
--- a/lims_industry/sample.py
+++ b/lims_industry/sample.py
@@ -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))
diff --git a/lims_industry/sample.xml b/lims_industry/sample.xml
index e4bb50b9..e69163da 100644
--- a/lims_industry/sample.xml
+++ b/lims_industry/sample.xml
@@ -15,6 +15,19 @@
sample_list
+
+
+
+ lims.sample.edition.log
+ tree
+ sample_edition_log_list
+
+
+ lims.sample.edition.log
+ form
+ sample_edition_log_form
+
+
diff --git a/lims_industry/view/sample_edit_form.xml b/lims_industry/view/sample_edit_form.xml
index 83fa2172..614be01e 100644
--- a/lims_industry/view/sample_edit_form.xml
+++ b/lims_industry/view/sample_edit_form.xml
@@ -8,4 +8,6 @@
+
+
diff --git a/lims_industry/view/sample_edition_log_form.xml b/lims_industry/view/sample_edition_log_form.xml
new file mode 100644
index 00000000..65e68c21
--- /dev/null
+++ b/lims_industry/view/sample_edition_log_form.xml
@@ -0,0 +1,17 @@
+
+
diff --git a/lims_industry/view/sample_edition_log_list.xml b/lims_industry/view/sample_edition_log_list.xml
new file mode 100644
index 00000000..d45ec960
--- /dev/null
+++ b/lims_industry/view/sample_edition_log_list.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/lims_industry/view/sample_form.xml b/lims_industry/view/sample_form.xml
index 49e4ea23..4ae88ad2 100644
--- a/lims_industry/view/sample_form.xml
+++ b/lims_industry/view/sample_form.xml
@@ -62,4 +62,9 @@
+
+
+
+
+