Update to 5.2
This commit is contained in:
parent
82df43a145
commit
8d566322e0
|
@ -38,6 +38,7 @@ def register():
|
|||
configuration.ConfigurationProductCategory,
|
||||
configuration.LabWorkYear,
|
||||
configuration.LabWorkYearSequence,
|
||||
configuration.Cron,
|
||||
configuration.ModelDoc,
|
||||
configuration.Model,
|
||||
laboratory.LaboratoryProfessional,
|
||||
|
|
|
@ -13,6 +13,8 @@ from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
|
|||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, Or, And
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['ProductType', 'Matrix', 'ObjectiveDescription', 'Formula',
|
||||
'FormulaVariable', 'Analysis', 'Typification', 'TypificationAditional',
|
||||
|
@ -109,14 +111,6 @@ class Typification(ModelSQL, ModelView):
|
|||
Unique(t, t.product_type, t.matrix, t.analysis, t.method),
|
||||
'This typification already exists'),
|
||||
]
|
||||
cls._error_messages.update({
|
||||
'limits': ('Quantification limit must be greater than'
|
||||
' Detection limit'),
|
||||
'default_typification': ('There is already a default typification'
|
||||
' for this combination of product type, matrix and analysis'),
|
||||
'not_default_typification': ('This typification should be the'
|
||||
' default'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_limit_digits():
|
||||
|
@ -270,7 +264,7 @@ class Typification(ModelSQL, ModelView):
|
|||
def check_limits(self):
|
||||
if (self.detection_limit and
|
||||
self.quantification_limit <= self.detection_limit):
|
||||
self.raise_user_error('limits')
|
||||
raise UserError(gettext('lims.lims.msg_limits'))
|
||||
|
||||
def check_default(self):
|
||||
if self.by_default:
|
||||
|
@ -283,7 +277,7 @@ class Typification(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if typifications:
|
||||
self.raise_user_error('default_typification')
|
||||
raise UserError(gettext('lims.lims.msg_default_typification'))
|
||||
else:
|
||||
if self.valid:
|
||||
typifications = self.search([
|
||||
|
@ -294,7 +288,8 @@ class Typification(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if not typifications:
|
||||
self.raise_user_error('not_default_typification')
|
||||
raise UserError(
|
||||
gettext('lims.msg_not_default_typification'))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
|
@ -897,20 +892,6 @@ class Analysis(Workflow, ModelSQL, ModelView):
|
|||
('code_uniq', Unique(t, t.code),
|
||||
'Analysis code must be unique'),
|
||||
]
|
||||
cls._error_messages.update({
|
||||
'description_uniq': 'Analysis description must be unique',
|
||||
'not_laboratory': 'Must define a Laboratory',
|
||||
'set_laboratories': ('A Set can be assigned to a single'
|
||||
' laboratory'),
|
||||
'analysis_laboratory': ('The "%(analysis)s" analysis is not'
|
||||
' defined in laboratory "%(laboratory)s"'),
|
||||
'not_laboratory_change': ('You can not change the laboratory'
|
||||
' because the analysis is included in a set/group with this'
|
||||
' laboratory'),
|
||||
'end_date': 'The leaving date cannot be lower than entry date',
|
||||
'end_date_wrong': ('End date should not be greater than the '
|
||||
'current date'),
|
||||
})
|
||||
cls._transitions |= set((
|
||||
('draft', 'active'),
|
||||
('active', 'disabled'),
|
||||
|
@ -1079,31 +1060,31 @@ class Analysis(Workflow, ModelSQL, ModelView):
|
|||
('type', '=', type),
|
||||
('end_date', '=', None),
|
||||
]) > count:
|
||||
cls.raise_user_error('description_uniq')
|
||||
raise UserError(gettext('lims.msg_description_uniq'))
|
||||
|
||||
def check_set(self):
|
||||
if self.type == 'set':
|
||||
if self.laboratories and len(self.laboratories) > 1:
|
||||
self.raise_user_error('set_laboratories')
|
||||
raise UserError(gettext('lims.msg_set_laboratories'))
|
||||
if self.included_analysis and not self.laboratories:
|
||||
self.raise_user_error('not_laboratory')
|
||||
raise UserError(gettext('lims.msg_not_laboratory'))
|
||||
if self.included_analysis:
|
||||
set_laboratory = self.laboratories[0].laboratory
|
||||
for ia in self.included_analysis:
|
||||
included_analysis_laboratories = [lab.laboratory
|
||||
for lab in ia.included_analysis.laboratories]
|
||||
if (set_laboratory not in included_analysis_laboratories):
|
||||
self.raise_user_error('analysis_laboratory', {
|
||||
'analysis': ia.included_analysis.rec_name,
|
||||
'laboratory': set_laboratory.rec_name,
|
||||
})
|
||||
raise UserError(gettext('lims.msg_analysis_laboratory',
|
||||
analysis=ia.included_analysis.rec_name,
|
||||
laboratory=set_laboratory.rec_name,
|
||||
))
|
||||
|
||||
def check_end_date(self):
|
||||
if self.end_date:
|
||||
if not self.start_date or self.end_date < self.start_date:
|
||||
self.raise_user_error('end_date')
|
||||
raise UserError(gettext('lims.msg_end_date'))
|
||||
if not self.start_date or self.end_date > datetime.now().date():
|
||||
self.raise_user_error('end_date_wrong')
|
||||
raise UserError(gettext('lims.msg_end_date_wrong'))
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
|
@ -1131,7 +1112,8 @@ class Analysis(Workflow, ModelSQL, ModelView):
|
|||
('laboratory', '=', laboratory),
|
||||
])
|
||||
if parent:
|
||||
cls.raise_user_error('not_laboratory_change')
|
||||
raise UserError(
|
||||
gettext('lims.msg_not_laboratory_change'))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('lims.wiz_lims_relate_analysis')
|
||||
|
@ -1529,14 +1511,6 @@ class AnalysisIncluded(ModelSQL, ModelView):
|
|||
laboratory_domain = fields.Function(fields.Many2Many('lims.laboratory',
|
||||
None, None, 'Laboratory domain'), 'on_change_with_laboratory_domain')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AnalysisIncluded, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'duplicated_analysis': 'The analysis "%s" is already included',
|
||||
'not_set_laboratory': 'No Laboratory loaded for the Set',
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def validate(cls, included_analysis):
|
||||
super(AnalysisIncluded, cls).validate(included_analysis)
|
||||
|
@ -1559,8 +1533,8 @@ class AnalysisIncluded(ModelSQL, ModelView):
|
|||
analysis_ids.extend(Analysis.get_included_analysis(
|
||||
ai.included_analysis.id))
|
||||
if self.included_analysis.id in analysis_ids:
|
||||
self.raise_user_error('duplicated_analysis',
|
||||
(self.included_analysis.rec_name,))
|
||||
raise UserError(gettext('lims.msg_duplicated_analysis',
|
||||
analysis=self.included_analysis.rec_name))
|
||||
|
||||
@fields.depends('included_analysis', 'analysis', 'laboratory',
|
||||
'_parent_analysis.type', '_parent_analysis.laboratories')
|
||||
|
@ -1606,7 +1580,6 @@ class AnalysisIncluded(ModelSQL, ModelView):
|
|||
laboratories=[]):
|
||||
cursor = Transaction().connection.cursor()
|
||||
pool = Pool()
|
||||
AnalysisIncluded = pool.get('lims.analysis.included')
|
||||
Analysis = pool.get('lims.analysis')
|
||||
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
|
||||
|
||||
|
@ -1615,7 +1588,7 @@ class AnalysisIncluded(ModelSQL, ModelView):
|
|||
|
||||
if analysis_type == 'set':
|
||||
if len(laboratories) != 1:
|
||||
AnalysisIncluded.raise_user_error('not_set_laboratory')
|
||||
raise UserError(gettext('lims.msg_not_set_laboratory'))
|
||||
set_laboratory_id = laboratories[0]
|
||||
not_parent_clause = ''
|
||||
if analysis_id:
|
||||
|
@ -1882,14 +1855,6 @@ class AnalysisLabMethod(ModelSQL):
|
|||
method = fields.Many2One('lims.lab.method', 'Method',
|
||||
ondelete='CASCADE', select=True, required=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AnalysisLabMethod, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'typificated_method': ('You can not delete method "%s" because '
|
||||
'is typificated'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def delete(cls, methods):
|
||||
cls.check_delete(methods)
|
||||
|
@ -1905,8 +1870,8 @@ class AnalysisLabMethod(ModelSQL):
|
|||
('valid', '=', True),
|
||||
])
|
||||
if typifications != 0:
|
||||
cls.raise_user_error('typificated_method',
|
||||
(method.method.code,))
|
||||
raise UserError(gettext('lims.msg_typificated_method',
|
||||
method=method.method.code))
|
||||
|
||||
|
||||
class AnalysisDevice(ModelSQL, ModelView):
|
||||
|
@ -1926,14 +1891,6 @@ class AnalysisDevice(ModelSQL, ModelView):
|
|||
depends=['laboratory'])
|
||||
by_default = fields.Boolean('By default')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AnalysisDevice, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'default_device': ('There is already a default device for this'
|
||||
' analysis on this laboratory'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_by_default():
|
||||
return True
|
||||
|
@ -1953,7 +1910,7 @@ class AnalysisDevice(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if devices:
|
||||
self.raise_user_error('default_device')
|
||||
raise UserError(gettext('lims.msg_default_device'))
|
||||
|
||||
@staticmethod
|
||||
def default_laboratory_domain():
|
||||
|
@ -2211,13 +2168,6 @@ class RelateAnalysis(Wizard):
|
|||
])
|
||||
relate = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(RelateAnalysis, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'not_set_laboratory': 'No Laboratory loaded for the Set',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
cursor = Transaction().connection.cursor()
|
||||
pool = Pool()
|
||||
|
@ -2229,7 +2179,7 @@ class RelateAnalysis(Wizard):
|
|||
'analysis_domain': [],
|
||||
}
|
||||
if len(analysis.laboratories) != 1:
|
||||
self.raise_user_error('not_set_laboratory')
|
||||
raise UserError(gettext('lims.msg_not_set_laboratory'))
|
||||
|
||||
cursor.execute('SELECT DISTINCT(al.analysis) '
|
||||
'FROM "' + AnalysisLaboratory._table + '" al '
|
||||
|
|
|
@ -7,6 +7,8 @@ from trytond.wizard import Wizard, StateTransition, StateView, Button
|
|||
from trytond.pyson import Eval
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['CertificationType', 'TechnicalScope', 'TechnicalScopeVersion',
|
||||
'TechnicalScopeVersionLine', 'AnalysisFamily', 'AnalysisFamilyCertificant',
|
||||
|
@ -76,10 +78,6 @@ class TechnicalScopeVersion(ModelSQL, ModelView):
|
|||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(TechnicalScopeVersion, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'active_version': ('Only one version can be active for each '
|
||||
'technical scope'),
|
||||
})
|
||||
cls._buttons.update({
|
||||
'open_typifications': {},
|
||||
'add_typifications': {
|
||||
|
@ -108,7 +106,7 @@ class TechnicalScopeVersion(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if versions:
|
||||
self.raise_user_error('active_version')
|
||||
raise UserError(gettext('lims.msg_active_version'))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('lims.act_open_typifications')
|
||||
|
|
|
@ -9,12 +9,14 @@ from trytond.transaction import Transaction
|
|||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.modules.company.model import (
|
||||
CompanyMultiValueMixin, CompanyValueMixin)
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['NotebookView', 'NotebookViewColumn', 'UserRole', 'UserRoleGroup',
|
||||
'Printer', 'User', 'UserLaboratory', 'Configuration',
|
||||
'ConfigurationLaboratory', 'ConfigurationSequence',
|
||||
'ConfigurationProductCategory', 'LabWorkYear', 'LabWorkYearSequence',
|
||||
'ModelDoc', 'Model']
|
||||
'Cron', 'ModelDoc', 'Model']
|
||||
sequence_names = [
|
||||
'entry_sequence', 'sample_sequence', 'service_sequence',
|
||||
'results_report_sequence']
|
||||
|
@ -428,11 +430,6 @@ class LabWorkYear(ModelSQL, ModelView, CompanyMultiValueMixin):
|
|||
def __setup__(cls):
|
||||
super(LabWorkYear, cls).__setup__()
|
||||
cls._order.insert(0, ('start_date', 'ASC'))
|
||||
cls._error_messages.update({
|
||||
'workyear_overlaps': ('Work year "%(first)s" and '
|
||||
'"%(second)s" overlap.'),
|
||||
'no_workyear_date': 'No work year defined for "%s".',
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
|
@ -476,10 +473,10 @@ class LabWorkYear(ModelSQL, ModelView, CompanyMultiValueMixin):
|
|||
second_id = cursor.fetchone()
|
||||
if second_id:
|
||||
second = self.__class__(second_id[0])
|
||||
self.raise_user_error('workyear_overlaps', {
|
||||
'first': self.rec_name,
|
||||
'second': second.rec_name,
|
||||
})
|
||||
raise UserError(gettext('lims.msg_workyear_overlaps',
|
||||
first=self.rec_name,
|
||||
second=second.rec_name,
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def find(cls, date=None, exception=True):
|
||||
|
@ -497,7 +494,8 @@ class LabWorkYear(ModelSQL, ModelView, CompanyMultiValueMixin):
|
|||
if exception:
|
||||
lang = Lang.get()
|
||||
formatted = lang.strftime(date)
|
||||
cls.raise_user_error('no_workyear_date', (formatted,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_workyear_date', date=formatted))
|
||||
else:
|
||||
return None
|
||||
return workyears[0].id
|
||||
|
@ -563,6 +561,22 @@ class LabWorkYearSequence(ModelSQL, CompanyValueMixin):
|
|||
return None
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.extend([
|
||||
('lims.entry|cron_acknowledgment_of_receipt',
|
||||
"Lims Acknowledgment of Receipt (Samples)"),
|
||||
('lims.fraction|confirm_waiting_fractions',
|
||||
"Lims Confirm Waiting Entries"),
|
||||
('lims.planification|process_waiting_planifications',
|
||||
"Lims Process Waiting Planification"),
|
||||
])
|
||||
|
||||
|
||||
class ModelDoc(ModelSQL, ModelView):
|
||||
'Model Doc'
|
||||
__name__ = 'ir.model.doc'
|
||||
|
|
|
@ -13,6 +13,8 @@ from trytond.pyson import PYSONEncoder, Eval, Bool
|
|||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['RangeType', 'Range', 'ControlTendency', 'ControlTendencyDetail',
|
||||
'ControlTendencyDetailRule', 'MeansDeviationsCalcStart',
|
||||
|
@ -52,15 +54,6 @@ class RangeType(ModelSQL, ModelView):
|
|||
translate=True, states={'invisible': Eval('use') != 'result_range'},
|
||||
depends=['use'])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(RangeType, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'default_range_type':
|
||||
'There is already a default origin'
|
||||
' for this use',
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_by_default():
|
||||
return False
|
||||
|
@ -79,7 +72,7 @@ class RangeType(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if range_types:
|
||||
self.raise_user_error('default_range_type')
|
||||
raise UserError(gettext('lims.msg_default_range_type'))
|
||||
|
||||
|
||||
class Range(ModelSQL, ModelView):
|
||||
|
@ -1209,22 +1202,6 @@ class PrintControlChart(Wizard):
|
|||
return 'start'
|
||||
return 'end'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(PrintControlChart, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'number': 'Measurement',
|
||||
'result': 'Result',
|
||||
'ucl': 'UCL (M+3D)',
|
||||
'uwl': 'UWL (M+2D)',
|
||||
'upl': 'UPL (M+D)',
|
||||
'cl': 'CL (M)',
|
||||
'lpl': 'LPL (M-D)',
|
||||
'lwl': 'LWL (M-2D)',
|
||||
'lcl': 'LCL (M-3D)',
|
||||
'cv': 'CV (%)',
|
||||
})
|
||||
|
||||
|
||||
class ControlChartReport(Report):
|
||||
'Control Chart'
|
||||
|
@ -1258,31 +1235,19 @@ class ControlChartReport(Report):
|
|||
|
||||
@classmethod
|
||||
def _get_objects(cls, tendency):
|
||||
pool = Pool()
|
||||
PrintControlChart = pool.get('lims.control_chart.print',
|
||||
type='wizard')
|
||||
|
||||
objects = {
|
||||
'number': {'name': PrintControlChart.raise_user_error('number',
|
||||
raise_exception=False), 'order': 1, 'recs': {}},
|
||||
'result': {'name': PrintControlChart.raise_user_error('result',
|
||||
raise_exception=False), 'order': 2, 'recs': {}},
|
||||
'ucl': {'name': PrintControlChart.raise_user_error('ucl',
|
||||
raise_exception=False), 'order': 3, 'recs': {}},
|
||||
'uwl': {'name': PrintControlChart.raise_user_error('uwl',
|
||||
raise_exception=False), 'order': 4, 'recs': {}},
|
||||
'upl': {'name': PrintControlChart.raise_user_error('upl',
|
||||
raise_exception=False), 'order': 5, 'recs': {}},
|
||||
'cl': {'name': PrintControlChart.raise_user_error('cl',
|
||||
raise_exception=False), 'order': 6, 'recs': {}},
|
||||
'lpl': {'name': PrintControlChart.raise_user_error('lpl',
|
||||
raise_exception=False), 'order': 7, 'recs': {}},
|
||||
'lwl': {'name': PrintControlChart.raise_user_error('lwl',
|
||||
raise_exception=False), 'order': 8, 'recs': {}},
|
||||
'lcl': {'name': PrintControlChart.raise_user_error('lcl',
|
||||
raise_exception=False), 'order': 9, 'recs': {}},
|
||||
'cv': {'name': PrintControlChart.raise_user_error('cv',
|
||||
raise_exception=False), 'order': 10, 'recs': {}},
|
||||
'number': {
|
||||
'name': gettext('lims.msg_number'), 'order': 1, 'recs': {}},
|
||||
'result': {
|
||||
'name': gettext('lims.msg_result'), 'order': 2, 'recs': {}},
|
||||
'ucl': {'name': gettext('lims.msg_ucl'), 'order': 3, 'recs': {}},
|
||||
'uwl': {'name': gettext('lims.msg_uwl'), 'order': 4, 'recs': {}},
|
||||
'upl': {'name': gettext('lims.msg_upl'), 'order': 5, 'recs': {}},
|
||||
'cl': {'name': gettext('lims.msg_cl'), 'order': 6, 'recs': {}},
|
||||
'lpl': {'name': gettext('lims.msg_lpl'), 'order': 7, 'recs': {}},
|
||||
'lwl': {'name': gettext('lims.msg_lwl'), 'order': 8, 'recs': {}},
|
||||
'lcl': {'name': gettext('lims.msg_lcl'), 'order': 9, 'recs': {}},
|
||||
'cv': {'name': gettext('lims.msg_cv'), 'order': 10, 'recs': {}},
|
||||
}
|
||||
count = 1
|
||||
for detail in tendency.details:
|
||||
|
@ -1303,9 +1268,6 @@ class ControlChartReport(Report):
|
|||
def _get_plot(cls, columns, records):
|
||||
if not CAN_PLOT:
|
||||
return None
|
||||
pool = Pool()
|
||||
PrintControlChart = pool.get('lims.control_chart.print',
|
||||
type='wizard')
|
||||
|
||||
index = columns
|
||||
cols = []
|
||||
|
@ -1317,44 +1279,28 @@ class ControlChartReport(Report):
|
|||
df = df.reindex_axis(cols, axis=1)
|
||||
|
||||
try:
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('ucl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_ucl'),
|
||||
]].plot(kind='line', color='red', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='-')
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('uwl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_uwl'),
|
||||
]].plot(kind='line', color='orange', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='-', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('upl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_upl'),
|
||||
]].plot(kind='line', color='yellow', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='--', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('cl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_cl'),
|
||||
]].plot(kind='line', color='green', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='-', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('lpl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_lpl'),
|
||||
]].plot(kind='line', color='yellow', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='--', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('lwl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_lwl'),
|
||||
]].plot(kind='line', color='orange', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='-', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('lcl',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_lcl'),
|
||||
]].plot(kind='line', color='red', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), linestyle='-', ax=ax)
|
||||
ax = df[[
|
||||
PrintControlChart.raise_user_error('result',
|
||||
raise_exception=False),
|
||||
ax = df[[gettext('lims.msg_result'),
|
||||
]].plot(kind='line', color='blue', rot=45, fontsize=7,
|
||||
figsize=(10, 7.5), marker='o', linestyle='-', ax=ax)
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# the full copyright notices and license terms.
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Department', 'UserDepartment']
|
||||
|
||||
|
@ -27,14 +29,6 @@ class UserDepartment(ModelSQL, ModelView):
|
|||
required=True)
|
||||
default = fields.Boolean('By default')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(UserDepartment, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'default_department': ('There is already a default department'
|
||||
' for this user'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_default():
|
||||
return False
|
||||
|
@ -53,4 +47,4 @@ class UserDepartment(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if user_departments:
|
||||
self.raise_user_error('default_department')
|
||||
raise UserError(gettext('lims.msg_default_department'))
|
||||
|
|
|
@ -17,6 +17,8 @@ from trytond.tools import get_smtp_server
|
|||
from trytond.config import config
|
||||
from trytond.report import Report
|
||||
from trytond.rpc import RPC
|
||||
from trytond.exceptions import UserError, UserWarning
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Entry', 'EntryInvoiceContact', 'EntryReportContact',
|
||||
'EntryAcknowledgmentContact', 'EntrySuspensionReason',
|
||||
|
@ -141,19 +143,6 @@ class Entry(Workflow, ModelSQL, ModelView):
|
|||
'invisible': ~Eval('state').in_(['draft']),
|
||||
},
|
||||
})
|
||||
cls._error_messages.update({
|
||||
'no_entry_sequence': ('There is no entry sequence for '
|
||||
'the work year "%s".'),
|
||||
'delete_entry': ('You can not delete entry "%s" because '
|
||||
'it is not in draft state'),
|
||||
'not_fraction': ('You can not confirm entry "%s" because '
|
||||
'has not fractions'),
|
||||
'missing_entry_contacts': ('Missing contacts in entry "%s"'),
|
||||
'enac_acredited': ('The analysis marked with * are not '
|
||||
'covered by the Accreditation.'),
|
||||
'english_report': ('Do not forget to load the translations '
|
||||
'into English'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_date():
|
||||
|
@ -382,8 +371,8 @@ class Entry(Workflow, ModelSQL, ModelView):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('entry')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_entry_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext('lims.msg_no_entry_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
@ -487,12 +476,16 @@ class Entry(Workflow, ModelSQL, ModelView):
|
|||
if (not self.invoice_contacts or
|
||||
not self.report_contacts or
|
||||
not self.acknowledgment_contacts):
|
||||
self.raise_user_error('missing_entry_contacts', (self.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_missing_entry_contacts', entry=self.rec_name))
|
||||
|
||||
def warn_english_report(self):
|
||||
Warning = Pool().get('res.user.warning')
|
||||
|
||||
if self.english_report:
|
||||
self.raise_user_warning('lims_english_report@%s' %
|
||||
self.number, 'english_report')
|
||||
key = 'lims_english_report@%s' % self.number
|
||||
if Warning.check(key):
|
||||
raise UserWarning(gettext('lims.english_report'))
|
||||
|
||||
def print_report(self):
|
||||
if self.ack_report_cache:
|
||||
|
@ -608,14 +601,16 @@ class Entry(Workflow, ModelSQL, ModelView):
|
|||
Company = Pool().get('company.company')
|
||||
companies = Company.search([])
|
||||
if self.party.id not in [c.party.id for c in companies]:
|
||||
self.raise_user_error('not_fraction', (self.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_not_fraction', entry=self.rec_name))
|
||||
Fraction.confirm(fractions)
|
||||
|
||||
@classmethod
|
||||
def check_delete(cls, entries):
|
||||
for entry in entries:
|
||||
if entry.state != 'draft':
|
||||
cls.raise_user_error('delete_entry', (entry.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_delete_entry', entry=entry.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, entries):
|
||||
|
@ -702,11 +697,6 @@ class EntrySuspensionReason(ModelSQL, ModelView):
|
|||
('code_uniq', Unique(t, t.code),
|
||||
'Suspension reason code must be unique'),
|
||||
]
|
||||
cls._error_messages.update({
|
||||
'default_suspension_reason':
|
||||
'There is already a default '
|
||||
'suspension reason',
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_by_default():
|
||||
|
@ -742,7 +732,7 @@ class EntrySuspensionReason(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if reasons:
|
||||
self.raise_user_error('default_suspension_reason')
|
||||
raise UserError(gettext('lims.msg_default_suspension_reason'))
|
||||
|
||||
|
||||
class EntryDetailAnalysis(ModelSQL, ModelView):
|
||||
|
@ -801,10 +791,6 @@ class EntryDetailAnalysis(ModelSQL, ModelView):
|
|||
def __setup__(cls):
|
||||
super(EntryDetailAnalysis, cls).__setup__()
|
||||
cls._order.insert(0, ('service', 'DESC'))
|
||||
cls._error_messages.update({
|
||||
'delete_detail': ('You can not delete the analysis detail because '
|
||||
'its fraction is confirmed'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def copy(cls, details, default=None):
|
||||
|
@ -819,7 +805,7 @@ class EntryDetailAnalysis(ModelSQL, ModelView):
|
|||
def check_delete(cls, details):
|
||||
for detail in details:
|
||||
if detail.fraction and detail.fraction.confirmed:
|
||||
cls.raise_user_error('delete_detail')
|
||||
raise UserError(gettext('lims.msg_delete_detail'))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, details):
|
||||
|
@ -835,7 +821,6 @@ class EntryDetailAnalysis(ModelSQL, ModelView):
|
|||
Method = pool.get('lims.lab.method')
|
||||
WaitingTime = pool.get('lims.lab.method.results_waiting')
|
||||
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
Notebook = pool.get('lims.notebook')
|
||||
Company = pool.get('company.company')
|
||||
|
||||
|
@ -927,8 +912,8 @@ class EntryDetailAnalysis(ModelSQL, ModelView):
|
|||
if not lines_create:
|
||||
companies = Company.search([])
|
||||
if fraction.party.id not in [c.party.id for c in companies]:
|
||||
Fraction.raise_user_error('not_services',
|
||||
(fraction.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_not_services', fraction=fraction.rec_name))
|
||||
|
||||
with Transaction().set_user(0):
|
||||
notebook = Notebook.search([
|
||||
|
@ -1455,8 +1440,8 @@ class AcknowledgmentOfReceipt(Report):
|
|||
|
||||
for v in s_methods.values():
|
||||
if v['enac']:
|
||||
v['enac_label'] = (Entry.raise_user_error(
|
||||
'enac_acredited', raise_exception=False))
|
||||
v['enac_label'] = gettext(
|
||||
'lims.msg_enac_acredited')
|
||||
else:
|
||||
v['enac_label'] = ''
|
||||
sorted_analysis = sorted(v['analysis'],
|
||||
|
|
|
@ -286,29 +286,15 @@
|
|||
<!-- Cron -->
|
||||
|
||||
<record model="ir.cron" id="cron_lims_acknowledgment_of_receipt">
|
||||
<field name="name">Lims Acknowledgment of Receipt (Samples)</field>
|
||||
<field name="request_user" ref="res.user_admin"/>
|
||||
<field name="user" ref="user_acknowledgment_of_receipt"/>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number" eval="20"/>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="number_calls" eval="-1"/>
|
||||
<field name="repeat_missed" eval="False"/>
|
||||
<field name="model">lims.entry</field>
|
||||
<field name="function">cron_acknowledgment_of_receipt</field>
|
||||
<field name="method">lims.entry|cron_acknowledgment_of_receipt</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="cron_lims_confirm_waiting_entries">
|
||||
<field name="name">Lims Confirm Waiting Entries</field>
|
||||
<field name="request_user" ref="res.user_admin"/>
|
||||
<field name="user" ref="user_entry_confirm"/>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="number_calls" eval="-1"/>
|
||||
<field name="repeat_missed" eval="False"/>
|
||||
<field name="model">lims.fraction</field>
|
||||
<field name="function">confirm_waiting_fractions</field>
|
||||
<field name="method">lims.fraction|confirm_waiting_fractions</field>
|
||||
</record>
|
||||
|
||||
<!-- Wizard Forward Acknowledgment of Samples Receipt -->
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# the full copyright notices and license terms.
|
||||
|
||||
from trytond.model import Model
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['FormulaParser']
|
||||
|
||||
|
@ -11,24 +13,6 @@ __all__ = ['FormulaParser']
|
|||
class FormulaParser(Model):
|
||||
'Formula Parser'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(FormulaParser, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'variable_redefine': 'Cannot redefine the value of "%s"',
|
||||
'unexpected_character': ('Unexpected character found: "%s"'
|
||||
' at index %s'),
|
||||
'division_zero': 'Division by 0 (occured at index %s)',
|
||||
'closing_parenthesis': ('No closing parenthesis found at'
|
||||
' character %s'),
|
||||
'unrecognized_variable': 'Unrecognized variable: "%s"',
|
||||
'extra_period': ('Found an extra period in a number at'
|
||||
' character %s'),
|
||||
'unexpected_end': 'Unexpected end found',
|
||||
'number_expected': ('Expecting to find a number at character'
|
||||
' %s but instead there is a "%s"'),
|
||||
})
|
||||
|
||||
def __init__(self, string, vars={}, id=None, **kwargs):
|
||||
self.string = string
|
||||
self.index = 0
|
||||
|
@ -38,7 +22,8 @@ class FormulaParser(Model):
|
|||
}
|
||||
for var in list(vars.keys()):
|
||||
if self.vars.get(var) is not None:
|
||||
self.raise_user_error('variable_redefine', (var,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_variable_redefine', variable=var))
|
||||
self.vars[var] = vars[var]
|
||||
super(FormulaParser, self).__init__(id, **kwargs)
|
||||
|
||||
|
@ -46,8 +31,8 @@ class FormulaParser(Model):
|
|||
value = self.parseExpression()
|
||||
self.skipWhitespace()
|
||||
if self.hasNext():
|
||||
self.raise_user_error('unexpected_character',
|
||||
(self.peek(), str(self.index)))
|
||||
raise UserError(gettext('lims.msg_unexpected_character',
|
||||
character=self.peek(), index=str(self.index)))
|
||||
return value
|
||||
|
||||
def peek(self):
|
||||
|
@ -90,11 +75,12 @@ class FormulaParser(Model):
|
|||
self.index += 1
|
||||
values.append(self.parsePower())
|
||||
elif char == '/':
|
||||
#div_index = self.index
|
||||
# div_index = self.index
|
||||
self.index += 1
|
||||
denominator = self.parsePower()
|
||||
if denominator == 0:
|
||||
#self.raise_user_error('division_zero', (str(div_index),))
|
||||
# raise UserError(gettext(
|
||||
# 'lims.msg_division_zero', index=str(div_index)))
|
||||
return 0.0
|
||||
values.append(1.0 / denominator)
|
||||
else:
|
||||
|
@ -127,8 +113,8 @@ class FormulaParser(Model):
|
|||
value = self.parseExpression()
|
||||
self.skipWhitespace()
|
||||
if self.peek() != ')':
|
||||
self.raise_user_error('closing_parenthesis',
|
||||
(str(self.index),))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_closing_parenthesis', index=str(self.index)))
|
||||
self.index += 1
|
||||
return value
|
||||
else:
|
||||
|
@ -164,7 +150,8 @@ class FormulaParser(Model):
|
|||
|
||||
value = self.vars.get(var, None)
|
||||
if value is None:
|
||||
self.raise_user_error('unrecognized_variable', (var,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_unrecognized_variable', variable=var))
|
||||
if value == '':
|
||||
return float(0)
|
||||
try:
|
||||
|
@ -183,7 +170,8 @@ class FormulaParser(Model):
|
|||
char = self.peek()
|
||||
if char == '.':
|
||||
if decimal_found:
|
||||
self.raise_user_error('extra_period', (str(self.index),))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_extra_period', index=str(self.index)))
|
||||
decimal_found = True
|
||||
strValue += '.'
|
||||
elif char in '0123456789':
|
||||
|
@ -194,9 +182,9 @@ class FormulaParser(Model):
|
|||
|
||||
if len(strValue) == 0:
|
||||
if char == '':
|
||||
self.raise_user_error('unexpected_end')
|
||||
raise UserError(gettext('lims.msg_unexpected_end'))
|
||||
else:
|
||||
self.raise_user_error('number_expected',
|
||||
(str(self.index), char))
|
||||
raise UserError(gettext('lims.msg_number_expected',
|
||||
index=str(self.index), character=char))
|
||||
|
||||
return float(strValue)
|
||||
|
|
|
@ -6,6 +6,8 @@ from trytond.model import ModelView, ModelSQL, fields, Unique
|
|||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pyson import Eval, Bool
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['LaboratoryProfessional', 'Laboratory', 'LaboratoryCVCorrection',
|
||||
'LabMethod', 'LabMethodWaitingTime', 'LabDeviceType', 'LabDevice',
|
||||
|
@ -360,14 +362,6 @@ class LabDeviceLaboratory(ModelSQL, ModelView):
|
|||
required=True)
|
||||
physically_here = fields.Boolean('Physically here')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(LabDeviceLaboratory, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'physically_elsewhere': ('This Device is physically in another'
|
||||
' Laboratory'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_physically_here():
|
||||
return True
|
||||
|
@ -386,4 +380,4 @@ class LabDeviceLaboratory(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if laboratories:
|
||||
self.raise_user_error('physically_elsewhere')
|
||||
raise UserError(gettext('lims.msg_physically_elsewhere'))
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_limits">
|
||||
<field name="text">Quantification limit must be greater than Detection limit</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_default_typification">
|
||||
<field name="text">There is already a default typification for this combination of product type, matrix and analysis</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_default_typification">
|
||||
<field name="text">This typification should be the default</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_description_uniq">
|
||||
<field name="text">Analysis description must be unique</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_laboratory">
|
||||
<field name="text">Must define a Laboratory</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_set_laboratories">
|
||||
<field name="text">A Set can be assigned to a single laboratory</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_analysis_laboratory">
|
||||
<field name="text">The "%(analysis)s" analysis is not defined in laboratory "%(laboratory)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_laboratory_change">
|
||||
<field name="text">You can not change the laboratory because the analysis is included in a set/group with this laboratory</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_end_date">
|
||||
<field name="text">The leaving date cannot be lower than entry date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_end_date_wrong">
|
||||
<field name="text">End date should not be greater than the current date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_duplicated_analysis">
|
||||
<field name="text">The analysis "%(analysis)s" is already included</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_set_laboratory">
|
||||
<field name="text">No Laboratory loaded for the Set</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_typificated_method">
|
||||
<field name="text">You can not delete method "%(method)s" because is typificated</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_default_device">
|
||||
<field name="text">There is already a default device for this analysis on this laboratory</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_active_version">
|
||||
<field name="text">Only one version can be active for each technical scope</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_workyear_overlaps">
|
||||
<field name="text">Work year "%(first)s" and "%(second)s" overlap.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_workyear_date">
|
||||
<field name="text">No work year defined for "%(date)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_default_range_type">
|
||||
<field name="text">There is already a default origin for this use</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_number">
|
||||
<field name="text">Measurement</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_result">
|
||||
<field name="text">Result</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_ucl">
|
||||
<field name="text">UCL (M+3D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_uwl">
|
||||
<field name="text">UWL (M+2D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_upl">
|
||||
<field name="text">UPL (M+D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_cl">
|
||||
<field name="text">CL (M)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_lpl">
|
||||
<field name="text">LPL (M-D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_lwl">
|
||||
<field name="text">LWL (M-2D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_lcl">
|
||||
<field name="text">LCL (M-3D)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_cv">
|
||||
<field name="text">CV (%)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_default_department">
|
||||
<field name="text">There is already a default department for this user</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_entry_sequence">
|
||||
<field name="text">There is no entry sequence for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_entry">
|
||||
<field name="text">You can not delete entry "%(entry)s" because it is not in draft state</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_fraction">
|
||||
<field name="text">You can not confirm entry "%(entry)s" because has not fractions</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_missing_entry_contacts">
|
||||
<field name="text">Missing contacts in entry "%(entry)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_enac_acredited">
|
||||
<field name="text">The analysis marked with * are not covered by the Accreditation.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_english_report">
|
||||
<field name="text">Do not forget to load the translations into English</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_default_suspension_reason">
|
||||
<field name="text">There is already a default suspension reason</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_detail">
|
||||
<field name="text">You can not delete the analysis detail because its fraction is confirmed</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_variable_redefine">
|
||||
<field name="text">Cannot redefine the value of "%(variable)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_unexpected_character">
|
||||
<field name="text">Unexpected character found: "%(character)s" at index %(index)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_division_zero">
|
||||
<field name="text">Division by 0 (occured at index %(index)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_closing_parenthesis">
|
||||
<field name="text">No closing parenthesis found at character %(index)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_unrecognized_variable">
|
||||
<field name="text">Unrecognized variable: "%(variable)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_extra_period">
|
||||
<field name="text">Found an extra period in a number at character %(index)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_unexpected_end">
|
||||
<field name="text">Unexpected end found</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_number_expected">
|
||||
<field name="text">Expecting to find a number at character %(index)s but instead there is a "%(character)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_physically_elsewhere">
|
||||
<field name="text">This Device is physically in another Laboratory</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_line_end_date">
|
||||
<field name="text">The end date cannot be lower than start date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_accepted">
|
||||
<field name="text">The analysis "%(analysis)s" is already accepted</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_1">
|
||||
<field name="text">The analysis is not reported</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_2">
|
||||
<field name="text">The analysis is annulled</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_3">
|
||||
<field name="text">The analysis has not End date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_4">
|
||||
<field name="text">The analysis has not Result / Converted result</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_5">
|
||||
<field name="text">The Converted result modifier is invalid</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_6">
|
||||
<field name="text">The Result modifier is invalid</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_accepted_7">
|
||||
<field name="text">The Converted result / Converted result modifier is invalid</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_accepted_1">
|
||||
<field name="text">The analysis is already reported (%(report)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_ok">
|
||||
<field name="text">OK</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_ok*">
|
||||
<field name="text">OK*</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_out">
|
||||
<field name="text">Out of Range</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_acceptable">
|
||||
<field name="text">Acceptable / Factor: %(factor)s - CV: %(cv)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_unacceptable">
|
||||
<field name="text">Unacceptable / Factor: %(factor)s - CV: %(cv)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_address">
|
||||
<field name="text">There is already a address with invoice type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_start_date">
|
||||
<field name="text">The planification must have a start date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invalid_start_date">
|
||||
<field name="text">The start date must be after "%(date)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_technician">
|
||||
<field name="text">The following fractions have not a responsible technician: %(fractions)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_waiting_process">
|
||||
<field name="text">Planification "%(planification)s" is still waiting for processing</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_planification">
|
||||
<field name="text">You can not delete planification "%(planification)s" because it is not in draft or pre-planned state</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_copy_planification">
|
||||
<field name="text">You can not copy planifications</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_entry_control">
|
||||
<field name="text">There is no default entry control for this work year</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_con_fraction_type">
|
||||
<field name="text">There is no Control fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_concentration_level">
|
||||
<field name="text">Missing concentration level for this control type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_rm_fraction_type">
|
||||
<field name="text">There is no RM fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_bmz_fraction_type">
|
||||
<field name="text">There is no BMZ fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_rm_default_configuration">
|
||||
<field name="text">Missing default configuration for RM fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_bmz_default_configuration">
|
||||
<field name="text">Missing default configuration for BMZ fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_typified">
|
||||
<field name="text">The analysis "%(analysis)s" is not typified for product type "%(product_type)s" and matrix "%(matrix)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_bre_fraction_type">
|
||||
<field name="text">There is no BRE fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_bre_default_configuration">
|
||||
<field name="text">Missing default configuration for BRE fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_mrt_fraction_type">
|
||||
<field name="text">There is no MRT fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_mrt_default_configuration">
|
||||
<field name="text">Missing default configuration for MRT fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_coi_fraction_type">
|
||||
<field name="text">There is no COI fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_mrc_fraction_type">
|
||||
<field name="text">There is no MRC fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_sla_fraction_type">
|
||||
<field name="text">There is no SLA fraction type configured</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_coi_default_configuration">
|
||||
<field name="text">Missing default configuration for COI fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_mrc_default_configuration">
|
||||
<field name="text">Missing default configuration for MRC fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_sla_default_configuration">
|
||||
<field name="text">Missing default configuration for SLA fraction type</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_sequence">
|
||||
<field name="text">There is no results report sequence for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_missing_module">
|
||||
<field name="text">Missing PyPDF2 module</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_empty_report">
|
||||
<field name="text">The report has not details to print</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_detail_not_draft">
|
||||
<field name="text">You can not delete a detail that is not in draft state</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_multiple_reports">
|
||||
<field name="text">Please, select only one report to print</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_annulled_report">
|
||||
<field name="text">This report is annulled</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_replace_number">
|
||||
<field name="text">Supplants the Results Report N° %(report)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_quantification_limit">
|
||||
<field name="text">< LoQ = %(loq)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_detection_limit">
|
||||
<field name="text">(LoD = %(detection_limit)s %(initial_unit)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_detection_limit_2">
|
||||
<field name="text">(LoD = %(detection_limit)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_uncertainty">
|
||||
<field name="text">(U± %(res)s %(initial_unit)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_obs_uncert">
|
||||
<field name="text">U = Uncertainty</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_neg">
|
||||
<field name="text">Negative</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_pos">
|
||||
<field name="text">Positive</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_nd">
|
||||
<field name="text">Not detected</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_pre">
|
||||
<field name="text">Presence</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_abs">
|
||||
<field name="text">Absence</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_enac_all_acredited">
|
||||
<field name="text">Uncertainty for the analysis covered by the Accreditation is available.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_concentration_label_1">
|
||||
<field name="text">(Expressed at the concentration of the received sample)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_concentration_label_2">
|
||||
<field name="text">(Expressed at %(concentration)s° Brix)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_concentration_label_3">
|
||||
<field name="text">(Expressed at %(concentration)s)</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_final_unit_label_1">
|
||||
<field name="text">Expressed at %(concentration)s %% Alcohol</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_final_unit_label_2">
|
||||
<field name="text">Expressed at %(concentration)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_final_unit_label_3">
|
||||
<field name="text">Expressed at %(concentration)s Bx</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_final_unit_label_4">
|
||||
<field name="text">Expressed at dry matter</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_obs_ql">
|
||||
<field name="text">LoQ= Limit of Quantitation. If the Detection Limit is reported, Result <LoQ indicates that the detected value is between LoD (Limit of Detection) and LoQ (Limit of Quantitation).</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_obs_dl">
|
||||
<field name="text">LoD= Limit of Detection.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_caa_min">
|
||||
<field name="text">min: %(min)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_caa_max">
|
||||
<field name="text">max: %(max)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_obs_rm_c_f">
|
||||
<field name="text">Elements results are reported without recovery correction.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_data_not_specified">
|
||||
<field name="text">NOT SPECIFIED BY THE CLIENT</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_done">
|
||||
<field name="text">Not done by the company</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_service_sequence">
|
||||
<field name="text">There is no service sequence for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_service">
|
||||
<field name="text">You can not delete service "%(service)s" because its fraction is confirmed</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_duplicated_analysis_service">
|
||||
<field name="text">The analysis "%(analysis)s" is assigned more than once to the fraction "%(fraction)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_missing_fraction_product">
|
||||
<field name="text">Missing "Fraction product" on Lims configuration</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_fraction">
|
||||
<field name="text">You can not delete fraction "%(fraction)s" because it is confirmed</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_services">
|
||||
<field name="text">You can not confirm fraction "%(fraction)s" because has not services</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_divided">
|
||||
<field name="text">You can not confirm fraction because is not yet divided</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_no_sample_sequence">
|
||||
<field name="text">There is no sample sequence for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_duplicated_label">
|
||||
<field name="text">The label "%(label)s" is already present in another sample</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_sample">
|
||||
<field name="text">You can not delete sample "%(sample)s" because its entry is not in draft state</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_counter_sample_date">
|
||||
<field name="text">Reverse counter sample storage to enter the service </field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_reference">
|
||||
<field name="text">Countersamples Storage</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_reference_reversion">
|
||||
<field name="text">Countersamples Storage Reversion</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_reference_discharge">
|
||||
<field name="text">Countersamples Discharge</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_reference_fractions_discharge">
|
||||
<field name="text">Fractions Discharge</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_reference_fractions_discharge_reversion">
|
||||
<field name="text">Fractions Discharge Reversion</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -13,6 +13,8 @@ from trytond.pool import Pool
|
|||
from trytond.pyson import PYSONEncoder, Eval, Bool, Not, Or
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
from .formula_parser import FormulaParser
|
||||
from .results_report import get_print_date
|
||||
|
||||
|
@ -227,7 +229,8 @@ class Notebook(ModelSQL, ModelView):
|
|||
domain = ('current_location', 'in', [l.id for l in locations])
|
||||
|
||||
all_notebooks = cls.search([])
|
||||
current_locations = iter(cls.get_current_location(all_notebooks).items())
|
||||
current_locations = iter(
|
||||
cls.get_current_location(all_notebooks).items())
|
||||
|
||||
processed_lines = [{
|
||||
'fraction': fraction,
|
||||
|
@ -401,21 +404,6 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
super(NotebookLine, cls).__setup__()
|
||||
cls._order.insert(0, ('analysis_order', 'ASC'))
|
||||
cls._order.insert(1, ('repetition', 'ASC'))
|
||||
cls._error_messages.update({
|
||||
'end_date': 'The end date cannot be lower than start date',
|
||||
'end_date_wrong': ('End date should not be greater than the '
|
||||
'current date'),
|
||||
'accepted': 'The analysis "%s" is already accepted',
|
||||
'not_accepted_1': 'The analysis is not reported',
|
||||
'not_accepted_2': 'The analysis is annulled',
|
||||
'not_accepted_3': 'The analysis has not End date',
|
||||
'not_accepted_4': 'The analysis has not Result / Converted result',
|
||||
'not_accepted_5': 'The Converted result modifier is invalid',
|
||||
'not_accepted_6': 'The Result modifier is invalid',
|
||||
'not_accepted_7': ('The Converted result / Converted result '
|
||||
'modifier is invalid'),
|
||||
'accepted_1': 'The analysis is already reported (%s)',
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_repetition():
|
||||
|
@ -532,9 +520,9 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
def check_end_date(self):
|
||||
if self.end_date:
|
||||
if not self.start_date or self.end_date < self.start_date:
|
||||
self.raise_user_error('end_date')
|
||||
raise UserError(gettext('lims.msg_line_end_date'))
|
||||
if not self.start_date or self.end_date > datetime.now().date():
|
||||
self.raise_user_error('end_date_wrong')
|
||||
raise UserError(gettext('lims.msg_end_date_wrong'))
|
||||
|
||||
def check_accepted(self):
|
||||
if self.accepted:
|
||||
|
@ -545,7 +533,8 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if accepted_lines:
|
||||
self.raise_user_error('accepted', (self.analysis.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_accepted', analysis=self.analysis.rec_name))
|
||||
|
||||
@classmethod
|
||||
def get_analysis_order(cls, notebook_lines, name):
|
||||
|
@ -721,16 +710,13 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
if self.accepted:
|
||||
if not self.report:
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_1', raise_exception=False)
|
||||
self.not_accepted_message = gettext('lims.msg_not_accepted_1')
|
||||
elif self.annulled:
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_2', raise_exception=False)
|
||||
self.not_accepted_message = gettext('lims.msg_not_accepted_2')
|
||||
elif not self.end_date:
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_3', raise_exception=False)
|
||||
self.not_accepted_message = gettext('lims.msg_not_accepted_3')
|
||||
elif not (self.result or self.converted_result or
|
||||
self.literal_result or
|
||||
self.result_modifier in
|
||||
|
@ -738,27 +724,26 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
self.converted_result_modifier in
|
||||
('nd', 'pos', 'neg', 'ni', 'abs', 'pre')):
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_4', raise_exception=False)
|
||||
self.not_accepted_message = gettext('lims.msg_not_accepted_4')
|
||||
else:
|
||||
if (self.converted_result and self.converted_result_modifier
|
||||
not in ('ni', 'eq', 'low')):
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_5', raise_exception=False)
|
||||
self.not_accepted_message = gettext(
|
||||
'lims.msg_not_accepted_5')
|
||||
elif (self.result and self.result_modifier
|
||||
not in ('ni', 'eq', 'low')):
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_6', raise_exception=False)
|
||||
self.not_accepted_message = gettext(
|
||||
'lims.msg_not_accepted_6')
|
||||
elif (self.result_modifier == 'ni' and
|
||||
not self.literal_result and
|
||||
(not self.converted_result_modifier or
|
||||
not self.converted_result) and
|
||||
self.converted_result_modifier != 'nd'):
|
||||
self.accepted = False
|
||||
self.not_accepted_message = self.raise_user_error(
|
||||
'not_accepted_7', raise_exception=False)
|
||||
self.not_accepted_message = gettext(
|
||||
'lims.msg_not_accepted_7')
|
||||
else:
|
||||
self.acceptance_date = datetime.now()
|
||||
else:
|
||||
|
@ -771,9 +756,8 @@ class NotebookLine(ModelSQL, ModelView):
|
|||
if report_lines:
|
||||
self.accepted = True
|
||||
report_detail = report_lines[0].report_version_detail
|
||||
self.not_accepted_message = self.raise_user_error('accepted_1',
|
||||
(report_detail.report_version.results_report.number,),
|
||||
raise_exception=False)
|
||||
self.not_accepted_message = gettext('lims.msg_accepted_1',
|
||||
report=report_detail.report_version.results_report.number)
|
||||
else:
|
||||
self.acceptance_date = None
|
||||
|
||||
|
@ -4083,15 +4067,6 @@ class NotebookResultsVerification(Wizard):
|
|||
])
|
||||
ok = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(NotebookResultsVerification, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'ok': 'OK',
|
||||
'ok*': 'OK*',
|
||||
'out': 'Out of Range',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
RangeType = Pool().get('lims.range.type')
|
||||
|
||||
|
@ -4266,12 +4241,9 @@ class NotebookResultsVerification(Wizard):
|
|||
|
||||
verifications = {}
|
||||
with Transaction().set_context(language=lang.code):
|
||||
verifications['ok'] = self.raise_user_error('ok',
|
||||
raise_exception=False)
|
||||
verifications['ok*'] = self.raise_user_error('ok*',
|
||||
raise_exception=False)
|
||||
verifications['out'] = self.raise_user_error('out',
|
||||
raise_exception=False)
|
||||
verifications['ok'] = gettext('lims.msg_ok')
|
||||
verifications['ok*'] = gettext('lims.msg_ok*')
|
||||
verifications['out'] = gettext('lims.msg_out')
|
||||
|
||||
return verifications
|
||||
|
||||
|
@ -4490,14 +4462,6 @@ class NotebookPrecisionControl(Wizard):
|
|||
])
|
||||
ok = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(NotebookPrecisionControl, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'acceptable': 'Acceptable / Factor: %s - CV: %s',
|
||||
'unacceptable': 'Unacceptable / Factor: %s - CV: %s',
|
||||
})
|
||||
|
||||
def transition_check(self):
|
||||
Notebook = Pool().get('lims.notebook')
|
||||
|
||||
|
@ -4589,11 +4553,11 @@ class NotebookPrecisionControl(Wizard):
|
|||
error = abs(rep_0 - rep_1) / average * 100
|
||||
|
||||
if error < (cv * self.start.factor):
|
||||
res = self.raise_user_error('acceptable', (
|
||||
self.start.factor, cv), raise_exception=False)
|
||||
res = gettext(
|
||||
'lims.msg_acceptable', factor=self.start.factor, cv=cv)
|
||||
else:
|
||||
res = self.raise_user_error('unacceptable', (
|
||||
self.start.factor, cv), raise_exception=False)
|
||||
res = gettext(
|
||||
'lims.msg_unacceptable', factor=self.start.factor, cv=cv)
|
||||
notebook_line.verification = res
|
||||
lines_to_save.append(notebook_line)
|
||||
if lines_to_save:
|
||||
|
|
|
@ -79,13 +79,6 @@ class Address(metaclass=PoolMeta):
|
|||
states={'readonly': ~Bool(Eval('invoice_contact'))},
|
||||
depends=['invoice_contact'])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Address, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'invoice_address': 'There is already a address with invoice type',
|
||||
})
|
||||
|
||||
@fields.depends('report_contact')
|
||||
def on_change_report_contact(self):
|
||||
if not self.report_contact:
|
||||
|
|
|
@ -13,6 +13,8 @@ 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',
|
||||
|
@ -152,17 +154,6 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
'readonly': (Eval('state') != 'preplanned'),
|
||||
},
|
||||
})
|
||||
cls._error_messages.update({
|
||||
'not_start_date': 'The planification must have a start date',
|
||||
'invalid_start_date': 'The start date must be after "%s"',
|
||||
'no_technician': ('The following fractions have not a responsible '
|
||||
'technician:%s'),
|
||||
'waiting_process': ('Planification "%s" is still waiting for '
|
||||
'processing'),
|
||||
'delete_planification': ('You can not delete planification "%s" '
|
||||
'because it is not in draft or pre-planned state'),
|
||||
'copy_planification': ('You can not copy planifications'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_state():
|
||||
|
@ -262,8 +253,8 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
def check_delete(cls, planifications):
|
||||
for planification in planifications:
|
||||
if planification.state not in ['draft', 'preplanned']:
|
||||
cls.raise_user_error('delete_planification',
|
||||
(planification.rec_name,))
|
||||
raise UserError(gettext('lims.msg_delete_planification',
|
||||
planification=planification.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, planifications):
|
||||
|
@ -272,7 +263,7 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
|
||||
@classmethod
|
||||
def copy(cls, planifications, default=None):
|
||||
cls.raise_user_error('copy_planification')
|
||||
raise UserError(gettext('lims.msg_copy_planification'))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('lims.wiz_lims_add_analysis')
|
||||
|
@ -305,11 +296,11 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
|
||||
def check_start_date(self):
|
||||
if not self.start_date:
|
||||
self.raise_user_error('not_start_date')
|
||||
raise UserError(gettext('lims.msg_not_start_date'))
|
||||
for detail in self.details:
|
||||
if detail.fraction.sample.date2 > self.start_date:
|
||||
self.raise_user_error('invalid_start_date',
|
||||
(detail.fraction.sample.date2,))
|
||||
raise UserError(gettext('lims.msg_invalid_start_date',
|
||||
date=detail.fraction.sample.date2))
|
||||
|
||||
def check_technicians(self):
|
||||
fractions = {}
|
||||
|
@ -322,9 +313,10 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
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)
|
||||
self.raise_user_error('no_technician',
|
||||
('\n' + '\n'.join(sorted_fractions) + '\n',))
|
||||
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):
|
||||
|
@ -412,8 +404,8 @@ class Planification(Workflow, ModelSQL, ModelView):
|
|||
for planification in planifications:
|
||||
# Check if is still waiting for confirmation
|
||||
if planification.waiting_process:
|
||||
cls.raise_user_error('waiting_process',
|
||||
(planification.code,))
|
||||
raise UserError(gettext('lims.msg_waiting_process',
|
||||
planification=planification.code))
|
||||
if process_background:
|
||||
planification.waiting_process = True
|
||||
planification.save()
|
||||
|
@ -1740,8 +1732,8 @@ class AddFractionControlStart(ModelView):
|
|||
if self.original_fraction:
|
||||
label += self.original_fraction.label
|
||||
if self.concentration_level:
|
||||
label += (' (' +
|
||||
self.concentration_level.description + ')')
|
||||
label += (' (' +
|
||||
self.concentration_level.description + ')')
|
||||
label += ' ' + str(Date.today())
|
||||
return label
|
||||
|
||||
|
@ -1757,18 +1749,6 @@ class AddFractionControl(Wizard):
|
|||
])
|
||||
add = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AddFractionControl, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'no_entry_control': ('There is no default entry control for this '
|
||||
'work year'),
|
||||
'no_con_fraction_type': ('There is no Control fraction type '
|
||||
'configured'),
|
||||
'no_concentration_level': ('Missing concentration level '
|
||||
'for this control type'),
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
Config = Pool().get('lims.configuration')
|
||||
config = Config(1)
|
||||
|
@ -1804,16 +1784,16 @@ class AddFractionControl(Wizard):
|
|||
config = Config(1)
|
||||
fraction_type = config.con_fraction_type
|
||||
if not fraction_type:
|
||||
self.raise_user_error('no_con_fraction_type')
|
||||
raise UserError(gettext('lims.msg_no_con_fraction_type'))
|
||||
|
||||
if (fraction_type.control_charts and not
|
||||
self.start.concentration_level):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
original_fraction = self.start.original_fraction
|
||||
|
@ -2238,26 +2218,6 @@ class AddFractionRMBMZ(Wizard):
|
|||
])
|
||||
add = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AddFractionRMBMZ, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'no_entry_control': ('There is no default entry control for this '
|
||||
'work year'),
|
||||
'no_rm_fraction_type': ('There is no RM fraction type '
|
||||
'configured'),
|
||||
'no_bmz_fraction_type': ('There is no BMZ fraction type '
|
||||
'configured'),
|
||||
'no_rm_default_configuration': ('Missing default configuration '
|
||||
'for RM fraction type'),
|
||||
'no_bmz_default_configuration': ('Missing default configuration '
|
||||
'for BMZ fraction type'),
|
||||
'no_concentration_level': ('Missing concentration level '
|
||||
'for this control type'),
|
||||
'not_typified': ('The analysis "%(analysis)s" is not typified '
|
||||
'for product type "%(product_type)s" and matrix "%(matrix)s"'),
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
defaults = {
|
||||
'planification': Transaction().context['active_id'],
|
||||
|
@ -2294,21 +2254,21 @@ class AddFractionRMBMZ(Wizard):
|
|||
config = Config(1)
|
||||
if self.start.type == 'rm':
|
||||
if not config.rm_fraction_type:
|
||||
self.raise_user_error('no_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:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
original_fraction = self.start.reference_fraction
|
||||
|
@ -2463,27 +2423,29 @@ class AddFractionRMBMZ(Wizard):
|
|||
config = Config(1)
|
||||
if self.start.type == 'rm':
|
||||
if not config.rm_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_rm_default_configuration')
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_rm_default_configuration'))
|
||||
elif self.start.type == 'bmz':
|
||||
if not config.bmz_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_bmz_default_configuration')
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_bmz_default_configuration'))
|
||||
|
||||
if (fraction_type.control_charts and not
|
||||
self.start.concentration_level):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
laboratory = self.start.planification.laboratory
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
|
@ -2534,11 +2496,11 @@ class AddFractionRMBMZ(Wizard):
|
|||
for p_analysis in self.start.planification.analysis:
|
||||
if not Analysis.is_typified(p_analysis,
|
||||
new_sample.product_type, new_sample.matrix):
|
||||
self.raise_user_error('not_typified', {
|
||||
'analysis': p_analysis.rec_name,
|
||||
'product_type': new_sample.product_type.rec_name,
|
||||
'matrix': new_sample.matrix.rec_name,
|
||||
})
|
||||
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
|
||||
|
@ -2604,21 +2566,6 @@ class AddFractionRMBMZ(Wizard):
|
|||
|
||||
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')
|
||||
|
@ -2879,22 +2826,6 @@ class AddFractionBRE(Wizard):
|
|||
])
|
||||
add = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AddFractionBRE, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'no_entry_control': ('There is no default entry control for this '
|
||||
'work year'),
|
||||
'no_bre_fraction_type': ('There is no BRE fraction type '
|
||||
'configured'),
|
||||
'no_bre_default_configuration': ('Missing default configuration '
|
||||
'for BRE fraction type'),
|
||||
'no_concentration_level': ('Missing concentration level '
|
||||
'for this control type'),
|
||||
'not_typified': ('The analysis "%(analysis)s" is not typified '
|
||||
'for product type "%(product_type)s" and matrix "%(matrix)s"'),
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
Config = Pool().get('lims.configuration')
|
||||
config = Config(1)
|
||||
|
@ -2928,20 +2859,20 @@ class AddFractionBRE(Wizard):
|
|||
|
||||
config = Config(1)
|
||||
if not config.bre_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_bre_default_configuration')
|
||||
raise UserError(gettext('lims.msg_no_bre_default_configuration'))
|
||||
|
||||
if (fraction_type.control_charts and not
|
||||
self.start.concentration_level):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
laboratory = self.start.planification.laboratory
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
|
@ -2991,11 +2922,11 @@ class AddFractionBRE(Wizard):
|
|||
for p_analysis in self.start.planification.analysis:
|
||||
if not Analysis.is_typified(p_analysis,
|
||||
new_sample.product_type, new_sample.matrix):
|
||||
self.raise_user_error('not_typified', {
|
||||
'analysis': p_analysis.rec_name,
|
||||
'product_type': new_sample.product_type.rec_name,
|
||||
'matrix': new_sample.matrix.rec_name,
|
||||
})
|
||||
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
|
||||
|
@ -3252,22 +3183,6 @@ class AddFractionMRT(Wizard):
|
|||
])
|
||||
add = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(AddFractionMRT, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'no_entry_control': ('There is no default entry control for this '
|
||||
'work year'),
|
||||
'no_mrt_fraction_type': ('There is no MRT fraction type '
|
||||
'configured'),
|
||||
'no_mrt_default_configuration': ('Missing default configuration '
|
||||
'for MRT fraction type'),
|
||||
'no_concentration_level': ('Missing concentration level '
|
||||
'for this control type'),
|
||||
'not_typified': ('The analysis "%(analysis)s" is not typified '
|
||||
'for product type "%(product_type)s" and matrix "%(matrix)s"'),
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
Config = Pool().get('lims.configuration')
|
||||
config = Config(1)
|
||||
|
@ -3301,20 +3216,20 @@ class AddFractionMRT(Wizard):
|
|||
|
||||
config = Config(1)
|
||||
if not config.mrt_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_mrt_default_configuration')
|
||||
raise UserError(gettext('lims.msg_no_mrt_default_configuration'))
|
||||
|
||||
if (fraction_type.control_charts and not
|
||||
self.start.concentration_level):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
laboratory = self.start.planification.laboratory
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
|
@ -3359,11 +3274,11 @@ class AddFractionMRT(Wizard):
|
|||
for p_analysis in self.start.planification.analysis:
|
||||
if not Analysis.is_typified(p_analysis,
|
||||
new_sample.product_type, new_sample.matrix):
|
||||
self.raise_user_error('not_typified', {
|
||||
'analysis': p_analysis.rec_name,
|
||||
'product_type': new_sample.product_type.rec_name,
|
||||
'matrix': new_sample.matrix.rec_name,
|
||||
})
|
||||
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
|
||||
|
@ -4542,28 +4457,6 @@ class CreateFractionControl(Wizard):
|
|||
create_ = StateTransition()
|
||||
open_ = StateAction('lims.act_lims_sample_list')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(CreateFractionControl, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'no_entry_control': ('There is no default entry control for this '
|
||||
'work year'),
|
||||
'no_coi_fraction_type': ('There is no COI fraction type '
|
||||
'configured'),
|
||||
'no_mrc_fraction_type': ('There is no MRC fraction type '
|
||||
'configured'),
|
||||
'no_sla_fraction_type': ('There is no SLA fraction type '
|
||||
'configured'),
|
||||
'no_coi_default_configuration': ('Missing default configuration '
|
||||
'for COI fraction type'),
|
||||
'no_mrc_default_configuration': ('Missing default configuration '
|
||||
'for MRC fraction type'),
|
||||
'no_sla_default_configuration': ('Missing default configuration '
|
||||
'for SLA fraction type'),
|
||||
'no_concentration_level': ('Missing concentration level '
|
||||
'for this control type'),
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
defaults = {
|
||||
'laboratory': Transaction().context.get('laboratory', None),
|
||||
|
@ -4589,34 +4482,37 @@ class CreateFractionControl(Wizard):
|
|||
config = Config(1)
|
||||
if self.start.type == 'coi':
|
||||
if not config.coi_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_coi_default_configuration')
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_coi_default_configuration'))
|
||||
elif self.start.type == 'mrc':
|
||||
if not config.mrc_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_mrc_default_configuration')
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_mrc_default_configuration'))
|
||||
elif self.start.type == 'sla':
|
||||
if not config.sla_fraction_type:
|
||||
self.raise_user_error('no_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):
|
||||
self.raise_user_error('no_sla_default_configuration')
|
||||
raise UserError(gettext(
|
||||
'lims.msg_no_sla_default_configuration'))
|
||||
|
||||
if (fraction_type.control_charts and not
|
||||
self.start.concentration_level):
|
||||
self.raise_user_error('no_concentration_level')
|
||||
raise UserError(gettext('lims.msg_no_concentration_level'))
|
||||
|
||||
workyear_id = LabWorkYear.find()
|
||||
workyear = LabWorkYear(workyear_id)
|
||||
if not workyear.default_entry_control:
|
||||
self.raise_user_error('no_entry_control')
|
||||
raise UserError(gettext('lims.msg_no_entry_control'))
|
||||
|
||||
laboratory = self.start.laboratory
|
||||
entry = Entry(workyear.default_entry_control.id)
|
||||
|
@ -5964,7 +5860,8 @@ class PlanificationWorksheetMethodReport(Report):
|
|||
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())
|
||||
fractions = list(
|
||||
objects[k1]['professionals'][k2]['lines'].values())
|
||||
for fraction in fractions:
|
||||
m_key = ()
|
||||
m_names = []
|
||||
|
@ -6150,8 +6047,9 @@ class PlanificationWorksheetReport(Report):
|
|||
'analysis'].keys():
|
||||
objects[k1]['professionals'][k2]['analysis'][k3][
|
||||
'methods'] = {}
|
||||
fractions = list(objects[k1]['professionals'][k2]['analysis'][
|
||||
k3]['lines'].values())
|
||||
fractions = list(
|
||||
objects[k1]['professionals'][k2]['analysis'][
|
||||
k3]['lines'].values())
|
||||
for fraction in fractions:
|
||||
m_key = ()
|
||||
m_names = []
|
||||
|
|
|
@ -302,16 +302,9 @@
|
|||
<!-- Cron -->
|
||||
|
||||
<record model="ir.cron" id="cron_lims_process_waiting_planifications">
|
||||
<field name="name">Lims Process Waiting Planifications</field>
|
||||
<field name="request_user" ref="res.user_admin"/>
|
||||
<field name="user" ref="user_planification_process"/>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="number_calls" eval="-1"/>
|
||||
<field name="repeat_missed" eval="False"/>
|
||||
<field name="model">lims.planification</field>
|
||||
<field name="function">process_waiting_planifications</field>
|
||||
<field name="method">lims.planification|process_waiting_planifications</field>
|
||||
</record>
|
||||
|
||||
<!-- Sequence -->
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# This file is part of lims module for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
from io import StringIO, BytesIO
|
||||
from io import BytesIO
|
||||
from datetime import datetime
|
||||
from PyPDF2 import PdfFileMerger
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
|
@ -13,6 +13,8 @@ from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, And, Or, If
|
|||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.rpc import RPC
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['ResultsReport', 'ResultsReportVersion',
|
||||
'ResultsReportVersionDetail', 'ResultsReportVersionDetailLine',
|
||||
|
@ -76,12 +78,6 @@ class ResultsReport(ModelSQL, ModelView):
|
|||
def __setup__(cls):
|
||||
super(ResultsReport, cls).__setup__()
|
||||
cls._order.insert(0, ('number', 'DESC'))
|
||||
cls._error_messages.update({
|
||||
'no_sequence': ('There is no results report sequence for '
|
||||
'the work year "%s".'),
|
||||
'missing_module': 'Missing PyPDF2 module',
|
||||
'empty_report': 'The report has not details to print',
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_report_grouper():
|
||||
|
@ -97,8 +93,8 @@ class ResultsReport(ModelSQL, ModelView):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('results_report')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext('lims.msg_no_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
@ -369,48 +365,6 @@ class ResultsReportVersionDetail(ModelSQL, ModelView):
|
|||
)),
|
||||
},
|
||||
})
|
||||
cls._error_messages.update({
|
||||
'delete_detail': ('You can not delete a detail that is not in '
|
||||
'draft state'),
|
||||
'multiple_reports': 'Please, select only one report to print',
|
||||
'annulled_report': 'This report is annulled',
|
||||
'empty_report': 'The report has not lines to print',
|
||||
'replace_number': 'Supplants the Results Report N° %s',
|
||||
'quantification_limit': '< LoQ = %s',
|
||||
'detection_limit': '(LoD = %s %s)',
|
||||
'detection_limit_2': '(LoD = %s)',
|
||||
'uncertainty': '(U± %s %s)',
|
||||
'obs_uncert': 'U = Uncertainty.',
|
||||
'neg': 'Negative',
|
||||
'pos': 'Positive',
|
||||
'nd': 'Not detected',
|
||||
'pre': 'Presence',
|
||||
'abs': 'Absence',
|
||||
'enac_all_acredited': ('Uncertainty for the analysis covered '
|
||||
'by the Accreditation is available.'),
|
||||
'enac_acredited': ('The analysis marked with * are not '
|
||||
'covered by the Accreditation. Uncertainty for the '
|
||||
'analysis covered by the Accreditation is available.'),
|
||||
'concentration_label_1': ('(Expressed at the concentration of '
|
||||
'the received sample)'),
|
||||
'concentration_label_2': '(Expressed at %s° Brix)',
|
||||
'concentration_label_3': '(Expressed at %s)',
|
||||
'final_unit_label_1': 'Expressed at %s %% Alcohol',
|
||||
'final_unit_label_2': 'Expressed at %s',
|
||||
'final_unit_label_3': 'Expressed at %s Bx',
|
||||
'final_unit_label_4': 'Expressed at dry matter',
|
||||
'obs_ql': ('LoQ= Limit of Quantitation. If the Detection '
|
||||
'Limit is reported, Result <LoQ '
|
||||
'indicates that the detected value is between LoD '
|
||||
'(Limit of Detection) and LoQ (Limit of Quantitation).'),
|
||||
'obs_dl': 'LoD= Limit of Detection.',
|
||||
'caa_min': 'min: %s',
|
||||
'caa_max': 'max: %s',
|
||||
'obs_rm_c_f': ('Elements results are reported without recovery '
|
||||
'correction.'),
|
||||
'data_not_specified': 'NOT SPECIFIED BY THE CLIENT',
|
||||
'not_done': 'Not done by the company',
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_report_type_forced():
|
||||
|
@ -810,7 +764,7 @@ class ResultsReportVersionDetail(ModelSQL, ModelView):
|
|||
def check_delete(cls, details):
|
||||
for detail in details:
|
||||
if detail.state != 'draft':
|
||||
cls.raise_user_error('delete_detail')
|
||||
raise UserError(gettext('lims.msg_delete_detail_not_draft'))
|
||||
|
||||
@classmethod
|
||||
def get_fraction_comments(cls, details, name):
|
||||
|
@ -1989,7 +1943,7 @@ class PrintResultsReport(Wizard):
|
|||
(format_field, '=', 'pdf'),
|
||||
])
|
||||
if not details:
|
||||
ResultsReport.raise_user_error('empty_report')
|
||||
raise UserError(gettext('lims.msg_empty_report'))
|
||||
|
||||
if results_report.english_report:
|
||||
results_report.report_format_eng = 'pdf'
|
||||
|
@ -2201,11 +2155,11 @@ class ResultReport(Report):
|
|||
def execute(cls, ids, data):
|
||||
ResultsReport = Pool().get('lims.results_report.version.detail')
|
||||
if len(ids) > 1:
|
||||
ResultsReport.raise_user_error('multiple_reports')
|
||||
raise UserError(gettext('lims.msg_multiple_reports'))
|
||||
|
||||
results_report = ResultsReport(ids[0])
|
||||
if results_report.state == 'annulled':
|
||||
ResultsReport.raise_user_error('annulled_report')
|
||||
raise UserError(gettext('lims.msg_annulled_report'))
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
|
@ -2279,8 +2233,7 @@ class ResultReport(Report):
|
|||
prev_number = "%s-%s" % (report.report_version.number,
|
||||
int(report.number) - 1)
|
||||
report_context['replace_number'] = (
|
||||
ResultsReport.raise_user_error('replace_number',
|
||||
(prev_number,), raise_exception=False))
|
||||
gettext('lims.msg_replace_number', report=prev_number))
|
||||
report_context['print_date'] = get_print_date()
|
||||
report_context['party'] = (
|
||||
report.report_version.results_report.party.rec_name)
|
||||
|
@ -2347,7 +2300,7 @@ class ResultReport(Report):
|
|||
('report_version_detail.valid', '=', True)],
|
||||
], order=[('report_version_detail', 'ASC')])
|
||||
if not notebook_lines:
|
||||
ResultsReport.raise_user_error('empty_report')
|
||||
raise UserError(gettext('lims.msg_empty_report'))
|
||||
|
||||
with Transaction().set_context(language=lang_code):
|
||||
reference_sample = Sample(
|
||||
|
@ -2393,8 +2346,7 @@ class ResultReport(Report):
|
|||
if sample.package_state else ''),
|
||||
'producer': (sample.producer.rec_name
|
||||
if sample.producer else
|
||||
ResultsReport.raise_user_error('data_not_specified',
|
||||
raise_exception=False)),
|
||||
gettext('lims.msg_data_not_specified')),
|
||||
'obj_description': (sample.obj_description.description
|
||||
if sample.obj_description else
|
||||
sample.obj_description_manual),
|
||||
|
@ -2426,9 +2378,8 @@ class ResultReport(Report):
|
|||
cls.format_date(sample.sampling_datetime.date(),
|
||||
report_context['user'].language))
|
||||
else:
|
||||
fractions[key]['water_sampling_date'] = (
|
||||
ResultsReport.raise_user_error('not_done',
|
||||
raise_exception=False))
|
||||
fractions[key]['water_sampling_date'] = gettext(
|
||||
'not_done')
|
||||
|
||||
record = {
|
||||
'order': t_line.analysis.order or 9999,
|
||||
|
@ -2541,8 +2492,7 @@ class ResultReport(Report):
|
|||
with Transaction().set_context(language=lang_code):
|
||||
report_context['sample_producer'] = (
|
||||
reference_sample.producer.rec_name if reference_sample.producer
|
||||
else ResultsReport.raise_user_error('data_not_specified',
|
||||
raise_exception=False))
|
||||
else gettext('lims.msg_data_not_specified'))
|
||||
report_context['sample_obj_description'] = (
|
||||
reference_sample.obj_description.description
|
||||
if reference_sample.obj_description
|
||||
|
@ -2599,9 +2549,8 @@ class ResultReport(Report):
|
|||
cls.format_date(reference_sample.sampling_datetime.date(),
|
||||
report_context['user'].language))
|
||||
else:
|
||||
report_context['water_sampling_date'] = (
|
||||
ResultsReport.raise_user_error('not_done',
|
||||
raise_exception=False))
|
||||
report_context['water_sampling_date'] = gettext(
|
||||
'lims.msg_not_done')
|
||||
|
||||
report_context['tas_project'] = 'True' if tas_project else 'False'
|
||||
report_context['stp_project'] = 'True' if stp_project else 'False'
|
||||
|
@ -2646,14 +2595,14 @@ class ResultReport(Report):
|
|||
fraction['concentrations'][conc]['label'] = ''
|
||||
elif conc_is_numeric and numeric_conc < 100:
|
||||
fraction['concentrations'][conc]['label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'concentration_label_2', (conc,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'concentration_label_2', concentration=conc
|
||||
))
|
||||
else:
|
||||
fraction['concentrations'][conc]['label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'concentration_label_3', (conc,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'concentration_label_3', concentration=conc
|
||||
))
|
||||
|
||||
show_unit_label = False
|
||||
for line in sorted_lines:
|
||||
|
@ -2664,29 +2613,27 @@ class ResultReport(Report):
|
|||
if dry_matter:
|
||||
fraction['concentrations'][conc][
|
||||
'unit_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'final_unit_label_4',
|
||||
raise_exception=False))
|
||||
gettext('lims.msg_final_unit_label_4'))
|
||||
else:
|
||||
if conc_is_numeric:
|
||||
if alcohol:
|
||||
fraction['concentrations'][conc][
|
||||
'unit_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'final_unit_label_1', (conc,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'final_unit_label_1',
|
||||
concentration=conc))
|
||||
else:
|
||||
fraction['concentrations'][conc][
|
||||
'unit_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'final_unit_label_3', (conc,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'final_unit_label_3',
|
||||
concentration=conc))
|
||||
else:
|
||||
fraction['concentrations'][conc][
|
||||
'unit_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'final_unit_label_2', (conc,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'final_unit_label_2',
|
||||
concentration=conc))
|
||||
|
||||
report_context['fractions'] = sorted_fractions
|
||||
|
||||
|
@ -2703,12 +2650,10 @@ class ResultReport(Report):
|
|||
with Transaction().set_context(language=lang_code):
|
||||
if enac_all_acredited:
|
||||
report_context['enac_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'enac_all_acredited', raise_exception=False))
|
||||
gettext('lims.msg_enac_all_acredited'))
|
||||
else:
|
||||
report_context['enac_label'] = (
|
||||
ResultsReport.raise_user_error(
|
||||
'enac_acredited', raise_exception=False))
|
||||
report_context['enac_label'] = gettext(
|
||||
'lims.msg_enac_acredited')
|
||||
else:
|
||||
report_context['enac_label'] = ''
|
||||
|
||||
|
@ -2735,23 +2680,17 @@ class ResultReport(Report):
|
|||
with Transaction().set_context(language=lang_code):
|
||||
if report_context['comments']:
|
||||
report_context['comments'] += '\n'
|
||||
report_context['comments'] += (
|
||||
ResultsReport.raise_user_error('obs_ql',
|
||||
raise_exception=False))
|
||||
report_context['comments'] += gettext('lims.msg_obs_ql')
|
||||
if obs_dl and report_context['report_section']:
|
||||
with Transaction().set_context(language=lang_code):
|
||||
if report_context['comments']:
|
||||
report_context['comments'] += '\n'
|
||||
report_context['comments'] += (
|
||||
ResultsReport.raise_user_error('obs_dl',
|
||||
raise_exception=False))
|
||||
report_context['comments'] += gettext('lims.msg_obs_dl')
|
||||
if obs_uncert:
|
||||
with Transaction().set_context(language=lang_code):
|
||||
if report_context['comments']:
|
||||
report_context['comments'] += '\n'
|
||||
report_context['comments'] += (
|
||||
ResultsReport.raise_user_error('obs_uncert',
|
||||
raise_exception=False))
|
||||
report_context['comments'] += gettext('lims.msg_obs_uncert')
|
||||
if obs_result_range and range_type.resultrange_comments:
|
||||
if report_context['comments']:
|
||||
report_context['comments'] += '\n'
|
||||
|
@ -2760,9 +2699,7 @@ class ResultReport(Report):
|
|||
with Transaction().set_context(language=lang_code):
|
||||
if report_context['comments']:
|
||||
report_context['comments'] += '\n'
|
||||
report_context['comments'] += (
|
||||
ResultsReport.raise_user_error('obs_rm_c_f',
|
||||
raise_exception=False))
|
||||
report_context['comments'] += gettext('lims.msg_obs_rm_c_f')
|
||||
|
||||
report_context['annulment_reason'] = ''
|
||||
if report.number != '1':
|
||||
|
@ -2812,9 +2749,6 @@ class ResultReport(Report):
|
|||
|
||||
@classmethod
|
||||
def get_result(cls, report_section, notebook_line, obs_ql, language):
|
||||
pool = Pool()
|
||||
ResultsReport = pool.get('lims.results_report.version.detail')
|
||||
|
||||
literal_result = notebook_line.literal_result
|
||||
result = notebook_line.result
|
||||
decimals = notebook_line.decimals
|
||||
|
@ -2835,27 +2769,20 @@ class ResultReport(Report):
|
|||
if result_modifier == 'eq':
|
||||
res = res
|
||||
elif result_modifier == 'low':
|
||||
res = ResultsReport.raise_user_error(
|
||||
'quantification_limit', (res,),
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_quantification_limit', loq=res)
|
||||
obs_ql = True
|
||||
elif result_modifier == 'nd':
|
||||
res = ResultsReport.raise_user_error('nd',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_nd')
|
||||
elif result_modifier == 'ni':
|
||||
res = ''
|
||||
elif result_modifier == 'pos':
|
||||
res = ResultsReport.raise_user_error('pos',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pos')
|
||||
elif result_modifier == 'neg':
|
||||
res = ResultsReport.raise_user_error('neg',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_neg')
|
||||
elif result_modifier == 'pre':
|
||||
res = ResultsReport.raise_user_error('pre',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pre')
|
||||
elif result_modifier == 'abs':
|
||||
res = ResultsReport.raise_user_error('abs',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_abs')
|
||||
else:
|
||||
res = result_modifier
|
||||
elif report_section == 'mi':
|
||||
|
@ -2873,28 +2800,20 @@ class ResultReport(Report):
|
|||
elif result_modifier == 'low':
|
||||
res = '< %s' % res
|
||||
elif result_modifier == 'nd':
|
||||
res = ResultsReport.raise_user_error('nd',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_nd')
|
||||
elif result_modifier == 'pos':
|
||||
res = ResultsReport.raise_user_error('pos',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pos')
|
||||
elif result_modifier == 'neg':
|
||||
res = ResultsReport.raise_user_error('neg',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_neg')
|
||||
elif result_modifier == 'pre':
|
||||
res = ResultsReport.raise_user_error('pre',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pre')
|
||||
elif result_modifier == 'abs':
|
||||
res = ResultsReport.raise_user_error('abs',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_abs')
|
||||
return res, obs_ql
|
||||
|
||||
@classmethod
|
||||
def get_converted_result(cls, report_section, report_result_type,
|
||||
notebook_line, obs_ql, language):
|
||||
pool = Pool()
|
||||
ResultsReport = pool.get('lims.results_report.version.detail')
|
||||
|
||||
if (report_section in ('for', 'mi', 'rp') or
|
||||
report_result_type not in ('both', 'both_range')):
|
||||
return '', obs_ql
|
||||
|
@ -2909,38 +2828,29 @@ class ResultReport(Report):
|
|||
res = ''
|
||||
if analysis != '0001' and not literal_result:
|
||||
if converted_result_modifier == 'neg':
|
||||
res = ResultsReport.raise_user_error('neg',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_neg')
|
||||
elif converted_result_modifier == 'pos':
|
||||
res = ResultsReport.raise_user_error('pos',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pos')
|
||||
elif converted_result_modifier == 'pre':
|
||||
res = ResultsReport.raise_user_error('pre',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_pre')
|
||||
elif converted_result_modifier == 'abs':
|
||||
res = ResultsReport.raise_user_error('abs',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_abs')
|
||||
elif converted_result_modifier == 'nd':
|
||||
res = ResultsReport.raise_user_error('nd',
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_nd')
|
||||
else:
|
||||
if converted_result and converted_result_modifier != 'ni':
|
||||
res = round(float(converted_result), decimals)
|
||||
if decimals == 0:
|
||||
res = int(res)
|
||||
if converted_result_modifier == 'low':
|
||||
res = ResultsReport.raise_user_error(
|
||||
'quantification_limit', (res,),
|
||||
raise_exception=False)
|
||||
res = gettext(
|
||||
'quantification_limit', loq=res)
|
||||
obs_ql = True
|
||||
return res, obs_ql
|
||||
|
||||
@classmethod
|
||||
def get_initial_unit(cls, report_section, report_result_type,
|
||||
notebook_line, obs_dl, obs_uncert, language):
|
||||
pool = Pool()
|
||||
ResultsReport = pool.get('lims.results_report.version.detail')
|
||||
|
||||
if not notebook_line.initial_unit:
|
||||
return '', obs_dl, obs_uncert
|
||||
|
||||
|
@ -2960,9 +2870,8 @@ class ResultReport(Report):
|
|||
res = round(float(uncertainty), decimals)
|
||||
if decimals == 0:
|
||||
res = int(res)
|
||||
res = ResultsReport.raise_user_error(
|
||||
'uncertainty', (res, ''),
|
||||
raise_exception=False)
|
||||
res = gettext(
|
||||
'lims.msg_uncertainty', res=res, initial_unit='')
|
||||
obs_uncert = True
|
||||
else:
|
||||
res = initial_unit
|
||||
|
@ -2978,9 +2887,9 @@ class ResultReport(Report):
|
|||
'0', '0.0')):
|
||||
res = initial_unit
|
||||
else:
|
||||
res = ResultsReport.raise_user_error(
|
||||
'detection_limit', (detection_limit,
|
||||
initial_unit), raise_exception=False)
|
||||
res = gettext('lims.msg_detection_limit',
|
||||
detection_limit=detection_limit,
|
||||
initial_unit=initial_unit)
|
||||
obs_dl = True
|
||||
else:
|
||||
if (not converted_result and uncertainty and
|
||||
|
@ -2988,18 +2897,14 @@ class ResultReport(Report):
|
|||
res = round(float(uncertainty), decimals)
|
||||
if decimals == 0:
|
||||
res = int(res)
|
||||
res = ResultsReport.raise_user_error(
|
||||
'uncertainty', (res, initial_unit),
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_uncertainty',
|
||||
res=res, initial_unit=initial_unit)
|
||||
obs_uncert = True
|
||||
return res, obs_dl, obs_uncert
|
||||
|
||||
@classmethod
|
||||
def get_final_unit(cls, report_section, report_result_type,
|
||||
notebook_line, obs_dl, obs_uncert, language):
|
||||
pool = Pool()
|
||||
ResultsReport = pool.get('lims.results_report.version.detail')
|
||||
|
||||
if (report_section in ('for', 'mi', 'rp') or
|
||||
report_result_type not in ('both', 'both_range')):
|
||||
return '', obs_dl, obs_uncert
|
||||
|
@ -3026,9 +2931,9 @@ class ResultReport(Report):
|
|||
'0', '0.0')):
|
||||
res = final_unit
|
||||
else:
|
||||
res = ResultsReport.raise_user_error(
|
||||
'detection_limit', (detection_limit,
|
||||
final_unit), raise_exception=False)
|
||||
res = gettext('lims.msg_detection_limit',
|
||||
detection_limit=detection_limit,
|
||||
final_unit=final_unit)
|
||||
obs_dl = True
|
||||
else:
|
||||
if not converted_result:
|
||||
|
@ -3038,17 +2943,14 @@ class ResultReport(Report):
|
|||
res = round(float(uncertainty), decimals)
|
||||
if decimals == 0:
|
||||
res = int(res)
|
||||
res = ResultsReport.raise_user_error(
|
||||
'uncertainty', (res, final_unit),
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_uncertainty',
|
||||
res=res, final_unit=final_unit)
|
||||
obs_uncert = True
|
||||
return res, obs_dl, obs_uncert
|
||||
|
||||
@classmethod
|
||||
def get_detection_limit(cls, report_section, report_result_type,
|
||||
report_type, notebook_line, language):
|
||||
ResultsReport = Pool().get('lims.results_report.version.detail')
|
||||
|
||||
detection_limit = notebook_line.detection_limit
|
||||
literal_result = notebook_line.literal_result
|
||||
result_modifier = notebook_line.result_modifier
|
||||
|
@ -3057,9 +2959,8 @@ class ResultReport(Report):
|
|||
res = ''
|
||||
if report_type == 'polisample' and result_modifier == 'nd':
|
||||
with Transaction().set_context(language=language):
|
||||
res = ResultsReport.raise_user_error(
|
||||
'detection_limit_2', (detection_limit),
|
||||
raise_exception=False)
|
||||
res = gettext('lims.msg_detection_limit_2',
|
||||
detection_limit=detection_limit)
|
||||
else:
|
||||
if (not detection_limit or detection_limit in ('0', '0.0') or
|
||||
literal_result):
|
||||
|
@ -3073,7 +2974,6 @@ class ResultReport(Report):
|
|||
report_section):
|
||||
pool = Pool()
|
||||
Range = pool.get('lims.range')
|
||||
ResultsReport = pool.get('lims.results_report.version.detail')
|
||||
|
||||
with Transaction().set_context(language=language):
|
||||
ranges = Range.search([
|
||||
|
@ -3101,8 +3001,7 @@ class ResultReport(Report):
|
|||
res1 = str(round(range_.min, 2))
|
||||
else:
|
||||
res1 = str(int(range_.min))
|
||||
res = ResultsReport.raise_user_error('caa_min',
|
||||
(res1,), raise_exception=False)
|
||||
res = gettext('lims.msg_caa_min', min=res1)
|
||||
|
||||
if range_.max:
|
||||
if res:
|
||||
|
@ -3115,8 +3014,7 @@ class ResultReport(Report):
|
|||
else:
|
||||
res1 = str(int(range_.max))
|
||||
|
||||
res += ResultsReport.raise_user_error('caa_max',
|
||||
(res1,), raise_exception=False)
|
||||
res += gettext('lims.msg_caa_max', max=res1)
|
||||
return res
|
||||
|
||||
|
||||
|
@ -3128,11 +3026,11 @@ class ResultReportTranscription(ResultReport):
|
|||
def execute(cls, ids, data):
|
||||
ResultsReport = Pool().get('lims.results_report.version.detail')
|
||||
if len(ids) > 1:
|
||||
ResultsReport.raise_user_error('multiple_reports')
|
||||
raise UserError(gettext('lims.msg_multiple_reports'))
|
||||
|
||||
results_report = ResultsReport(ids[0])
|
||||
if results_report.state == 'annulled':
|
||||
ResultsReport.raise_user_error('annulled_report')
|
||||
raise UserError(gettext('lims.msg_annulled_report'))
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
|
|
148
lims/sample.py
148
lims/sample.py
|
@ -14,6 +14,8 @@ from trytond.pool import Pool
|
|||
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, Or
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Zone', 'Variety', 'MatrixVariety', 'PackagingIntegrity',
|
||||
'PackagingType', 'FractionType', 'SampleProducer', 'Service',
|
||||
|
@ -362,14 +364,6 @@ class Service(ModelSQL, ModelView):
|
|||
def __setup__(cls):
|
||||
super(Service, cls).__setup__()
|
||||
cls._order.insert(0, ('number', 'DESC'))
|
||||
cls._error_messages.update({
|
||||
'no_service_sequence': ('There is no service sequence for '
|
||||
'the work year "%s".'),
|
||||
'delete_service': ('You can not delete service "%s" because '
|
||||
'its fraction is confirmed'),
|
||||
'duplicated_analysis': ('The analysis "%(analysis)s" is assigned '
|
||||
'more than once to the fraction "%(fraction)s"'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_urgent():
|
||||
|
@ -411,10 +405,11 @@ class Service(ModelSQL, ModelView):
|
|||
for a_id in new_analysis_ids:
|
||||
if a_id in analysis_ids:
|
||||
analysis = Analysis(a_id)
|
||||
self.raise_user_error('duplicated_analysis', {
|
||||
'analysis': analysis.rec_name,
|
||||
'fraction': self.fraction.rec_name,
|
||||
})
|
||||
raise UserError(gettext(
|
||||
'lims.msg_duplicated_analysis_service',
|
||||
analysis=analysis.rec_name,
|
||||
fraction=self.fraction.rec_name,
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
|
@ -427,8 +422,8 @@ class Service(ModelSQL, ModelView):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('service')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_service_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext('lims.msg_no_service_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
@ -489,7 +484,8 @@ class Service(ModelSQL, ModelView):
|
|||
def check_delete(cls, services):
|
||||
for service in services:
|
||||
if service.fraction and service.fraction.confirmed:
|
||||
cls.raise_user_error('delete_service', (service.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_delete_service', service=service.rec_name))
|
||||
|
||||
@staticmethod
|
||||
def update_analysis_detail(services):
|
||||
|
@ -1318,18 +1314,6 @@ class Fraction(ModelSQL, ModelView):
|
|||
Bool(Eval('services'))),
|
||||
},
|
||||
})
|
||||
cls._error_messages.update({
|
||||
'missing_fraction_product': ('Missing "Fraction product" '
|
||||
'on Lims configuration'),
|
||||
'delete_fraction': ('You can not delete fraction "%s" because '
|
||||
'it is confirmed'),
|
||||
'duplicated_analysis': ('The analysis "%s" is assigned more'
|
||||
' than once'),
|
||||
'not_services': ('You can not confirm fraction "%s" because '
|
||||
'has not services'),
|
||||
'not_divided': ('You can not confirm fraction because '
|
||||
'is not yet divided'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_packages_quantity():
|
||||
|
@ -1419,7 +1403,8 @@ class Fraction(ModelSQL, ModelView):
|
|||
def check_delete(cls, fractions):
|
||||
for fraction in fractions:
|
||||
if fraction.confirmed:
|
||||
cls.raise_user_error('delete_fraction', (fraction.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims.msg_delete_fraction', fraction=fraction.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, fractions):
|
||||
|
@ -1782,7 +1767,7 @@ class Fraction(ModelSQL, ModelView):
|
|||
('service', '=', service.id),
|
||||
('report_grouper', '!=', 0),
|
||||
]) == 0):
|
||||
cls.raise_user_error('not_divided')
|
||||
raise UserError(gettext('lims.msg_not_divided'))
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
|
@ -1855,7 +1840,7 @@ class Fraction(ModelSQL, ModelView):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
self.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
today = Date.today()
|
||||
company = User(Transaction().user).company
|
||||
if self.sample.entry.party.customer_location:
|
||||
|
@ -1921,9 +1906,9 @@ class Fraction(ModelSQL, ModelView):
|
|||
if a_id in analysis_ids:
|
||||
analysis = Analysis(a_id)
|
||||
self.duplicated_analysis_message = (
|
||||
self.raise_user_error('duplicated_analysis',
|
||||
(analysis.rec_name,),
|
||||
raise_exception=False))
|
||||
gettext(
|
||||
'lims.msg_duplicated_analysis_fraction',
|
||||
analysis=analysis.rec_name))
|
||||
return
|
||||
analysis_ids.extend(new_analysis_ids)
|
||||
|
||||
|
@ -2321,14 +2306,6 @@ class Sample(ModelSQL, ModelView):
|
|||
def __setup__(cls):
|
||||
super(Sample, cls).__setup__()
|
||||
cls._order.insert(0, ('number', 'DESC'))
|
||||
cls._error_messages.update({
|
||||
'no_sample_sequence': ('There is no sample sequence for '
|
||||
'the work year "%s".'),
|
||||
'duplicated_label': ('The label "%s" is already present in '
|
||||
'another sample'),
|
||||
'delete_sample': ('You can not delete sample "%s" because '
|
||||
'its entry is not in draft state'),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_date():
|
||||
|
@ -2410,8 +2387,8 @@ class Sample(ModelSQL, ModelView):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('sample')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_sample_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext('lims.msg_no_sample_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
@ -2423,6 +2400,7 @@ class Sample(ModelSQL, ModelView):
|
|||
|
||||
def warn_duplicated_label(self):
|
||||
return # deactivated
|
||||
Warning = Pool().get('res.user.warning')
|
||||
if self.label:
|
||||
duplicated = self.search([
|
||||
('entry', '=', self.entry.id),
|
||||
|
@ -2430,8 +2408,10 @@ class Sample(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if duplicated:
|
||||
self.raise_user_warning('lims_sample_label@%s' %
|
||||
self.number, 'duplicated_label', self.label)
|
||||
key = 'lims_sample_label@%s' % self.number
|
||||
if Warning.check(key):
|
||||
raise UserWarning(gettext(
|
||||
'lims.duplicated_label', label=self.label))
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
|
@ -2584,7 +2564,8 @@ class Sample(ModelSQL, ModelView):
|
|||
def check_delete(cls, samples):
|
||||
for sample in samples:
|
||||
if sample.entry and sample.entry.state != 'draft':
|
||||
cls.raise_user_error('delete_sample', (sample.rec_name,))
|
||||
raise UserError(
|
||||
gettext('lims.msg_delete_sample', sample=sample.rec_name))
|
||||
|
||||
@classmethod
|
||||
def delete(cls, samples):
|
||||
|
@ -2855,14 +2836,6 @@ class ManageServices(Wizard):
|
|||
])
|
||||
ok = StateTransition()
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(ManageServices, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'counter_sample_date':
|
||||
'Reverse counter sample storage to enter the service ',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
Fraction = Pool().get('lims.fraction')
|
||||
|
||||
|
@ -2895,7 +2868,7 @@ class ManageServices(Wizard):
|
|||
if fraction.countersample_date is None:
|
||||
return 'start'
|
||||
else:
|
||||
self.raise_user_error('counter_sample_date')
|
||||
raise UserError(gettext('lims.msg_counter_sample_date'))
|
||||
return 'end'
|
||||
|
||||
def transition_ok(self):
|
||||
|
@ -3249,13 +3222,6 @@ class CountersampleStorage(Wizard):
|
|||
storage = StateTransition()
|
||||
open = StateAction('stock.act_shipment_internal_form')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(CountersampleStorage, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'reference': 'Countersamples Storage',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
res = {}
|
||||
for field in ('report_date_from', 'report_date_to',
|
||||
|
@ -3443,8 +3409,7 @@ class CountersampleStorage(Wizard):
|
|||
|
||||
with Transaction().set_user(0, set_context=True):
|
||||
shipment = ShipmentInternal()
|
||||
shipment.reference = CountersampleStorage.raise_user_error(
|
||||
'reference', raise_exception=False)
|
||||
shipment.reference = gettext('lims.msg_reference')
|
||||
shipment.planned_date = planned_date
|
||||
shipment.planned_start_date = planned_date
|
||||
shipment.company = company
|
||||
|
@ -3456,7 +3421,6 @@ class CountersampleStorage(Wizard):
|
|||
def _get_stock_moves(self, fractions):
|
||||
pool = Pool()
|
||||
Config = pool.get('lims.configuration')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
User = pool.get('res.user')
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
|
@ -3464,7 +3428,7 @@ class CountersampleStorage(Wizard):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
Fraction.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
company = User(Transaction().user).company
|
||||
|
||||
from_location = self.start.location_origin
|
||||
|
@ -3569,13 +3533,6 @@ class CountersampleStorageRevert(Wizard):
|
|||
revert = StateTransition()
|
||||
open = StateAction('stock.act_shipment_internal_form')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(CountersampleStorageRevert, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'reference': 'Countersamples Storage Reversion',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
res = {}
|
||||
for field in ('date_from', 'date_to'):
|
||||
|
@ -3651,8 +3608,7 @@ class CountersampleStorageRevert(Wizard):
|
|||
|
||||
with Transaction().set_user(0, set_context=True):
|
||||
shipment = ShipmentInternal()
|
||||
shipment.reference = CountersampleStorageRevert.raise_user_error(
|
||||
'reference', raise_exception=False)
|
||||
shipment.reference = gettext('lims.msg_reference_reversion')
|
||||
shipment.planned_date = today
|
||||
shipment.planned_start_date = today
|
||||
shipment.company = company
|
||||
|
@ -3664,7 +3620,6 @@ class CountersampleStorageRevert(Wizard):
|
|||
def _get_stock_moves(self, fractions):
|
||||
pool = Pool()
|
||||
Config = pool.get('lims.configuration')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
User = pool.get('res.user')
|
||||
Date = pool.get('ir.date')
|
||||
Move = pool.get('stock.move')
|
||||
|
@ -3673,7 +3628,7 @@ class CountersampleStorageRevert(Wizard):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
Fraction.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
company = User(Transaction().user).company
|
||||
|
||||
from_location = self.start.location_origin
|
||||
|
@ -3764,13 +3719,6 @@ class CountersampleDischarge(Wizard):
|
|||
discharge = StateTransition()
|
||||
open = StateAction('stock.act_shipment_internal_form')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(CountersampleDischarge, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'reference': 'Countersamples Discharge',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
res = {}
|
||||
for field in ('expiry_date_from', 'expiry_date_to',
|
||||
|
@ -3848,8 +3796,7 @@ class CountersampleDischarge(Wizard):
|
|||
|
||||
with Transaction().set_user(0, set_context=True):
|
||||
shipment = ShipmentInternal()
|
||||
shipment.reference = CountersampleDischarge.raise_user_error(
|
||||
'reference', raise_exception=False)
|
||||
shipment.reference = gettext('lims.msg_reference_discharge')
|
||||
shipment.planned_date = planned_date
|
||||
shipment.planned_start_date = planned_date
|
||||
shipment.company = company
|
||||
|
@ -3861,7 +3808,6 @@ class CountersampleDischarge(Wizard):
|
|||
def _get_stock_moves(self, fractions):
|
||||
pool = Pool()
|
||||
Config = pool.get('lims.configuration')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
User = pool.get('res.user')
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
|
@ -3869,7 +3815,7 @@ class CountersampleDischarge(Wizard):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
Fraction.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
company = User(Transaction().user).company
|
||||
|
||||
from_location = self.start.location_origin
|
||||
|
@ -3959,13 +3905,6 @@ class FractionDischarge(Wizard):
|
|||
discharge = StateTransition()
|
||||
open = StateAction('stock.act_shipment_internal_form')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(FractionDischarge, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'reference': 'Fractions Discharge',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
res = {}
|
||||
for field in ('date_from', 'date_to'):
|
||||
|
@ -4049,8 +3988,7 @@ class FractionDischarge(Wizard):
|
|||
|
||||
with Transaction().set_user(0, set_context=True):
|
||||
shipment = ShipmentInternal()
|
||||
shipment.reference = FractionDischarge.raise_user_error(
|
||||
'reference', raise_exception=False)
|
||||
shipment.reference = gettext('lims.msg_reference_fractions_discharge')
|
||||
shipment.planned_date = planned_date
|
||||
shipment.planned_start_date = planned_date
|
||||
shipment.company = company
|
||||
|
@ -4062,7 +4000,6 @@ class FractionDischarge(Wizard):
|
|||
def _get_stock_moves(self, fractions):
|
||||
pool = Pool()
|
||||
Config = pool.get('lims.configuration')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
User = pool.get('res.user')
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
|
@ -4070,7 +4007,7 @@ class FractionDischarge(Wizard):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
Fraction.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
company = User(Transaction().user).company
|
||||
|
||||
from_location = self.start.location_origin
|
||||
|
@ -4158,13 +4095,6 @@ class FractionDischargeRevert(Wizard):
|
|||
revert = StateTransition()
|
||||
open = StateAction('stock.act_shipment_internal_form')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(FractionDischargeRevert, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'reference': 'Fractions Discharge Reversion',
|
||||
})
|
||||
|
||||
def default_start(self, fields):
|
||||
res = {}
|
||||
for field in ('date_from', 'date_to'):
|
||||
|
@ -4238,8 +4168,7 @@ class FractionDischargeRevert(Wizard):
|
|||
|
||||
with Transaction().set_user(0, set_context=True):
|
||||
shipment = ShipmentInternal()
|
||||
shipment.reference = FractionDischargeRevert.raise_user_error(
|
||||
'reference', raise_exception=False)
|
||||
shipment.reference = gettext('lims.msg_reference')
|
||||
shipment.planned_date = today
|
||||
shipment.planned_start_date = today
|
||||
shipment.company = company
|
||||
|
@ -4251,7 +4180,6 @@ class FractionDischargeRevert(Wizard):
|
|||
def _get_stock_moves(self, fractions):
|
||||
pool = Pool()
|
||||
Config = pool.get('lims.configuration')
|
||||
Fraction = pool.get('lims.fraction')
|
||||
User = pool.get('res.user')
|
||||
Date = pool.get('ir.date')
|
||||
Move = pool.get('stock.move')
|
||||
|
@ -4260,7 +4188,7 @@ class FractionDischargeRevert(Wizard):
|
|||
if config_.fraction_product:
|
||||
product = config_.fraction_product
|
||||
else:
|
||||
Fraction.raise_user_error('missing_fraction_product')
|
||||
raise UserError(gettext('lims.msg_missing_fraction_product'))
|
||||
company = User(Transaction().user).company
|
||||
|
||||
from_location = self.start.location_origin
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
carrier
|
||||
party_relationship
|
||||
|
@ -21,3 +21,4 @@ xml:
|
|||
fraction.xml
|
||||
stock.xml
|
||||
party.xml
|
||||
message.xml
|
||||
|
|
|
@ -178,30 +178,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="group" ref="group_lims_control"/>
|
||||
</record>
|
||||
|
||||
<!-- Users -->
|
||||
|
||||
<record model="res.user" id="user_acknowledgment_of_receipt">
|
||||
<field name="login">user_cron_acknowledgment_of_receipt</field>
|
||||
<field name="name">Cron Lims Acknowledgment Of Receipt</field>
|
||||
<field name="signature"></field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_acknowledgment_of_receipt_group_entry">
|
||||
<field name="user" ref="user_acknowledgment_of_receipt"/>
|
||||
<field name="group" ref="group_lims_entry"/>
|
||||
</record>
|
||||
|
||||
<record model="res.user" id="user_entry_confirm">
|
||||
<field name="login">user_cron_entry_confirm</field>
|
||||
<field name="name">Cron Lims Entry Confirm</field>
|
||||
<field name="signature"></field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_entry_confirm_group_entry">
|
||||
<field name="user" ref="user_entry_confirm"/>
|
||||
<field name="group" ref="group_lims_entry"/>
|
||||
</record>
|
||||
|
||||
<!-- Groups -->
|
||||
|
||||
<record model="res.group" id="group_lims_blind_sample_readonly">
|
||||
|
@ -226,19 +202,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="group" ref="group_lims_planification"/>
|
||||
</record>
|
||||
|
||||
<!-- Users -->
|
||||
|
||||
<record model="res.user" id="user_planification_process">
|
||||
<field name="login">user_cron_planification_process</field>
|
||||
<field name="name">Cron Lims Planification Process</field>
|
||||
<field name="signature"></field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_planification_process_group_planification">
|
||||
<field name="user" ref="user_planification_process"/>
|
||||
<field name="group" ref="group_lims_planification"/>
|
||||
</record>
|
||||
|
||||
<menuitem parent="res.menu_res" action="act_lims_user_role_list"
|
||||
id="lims_user_role_menu" sequence="5"/>
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from . import lims
|
|||
def register():
|
||||
Pool.register(
|
||||
configuration.Configuration,
|
||||
configuration.Cron,
|
||||
party.Party,
|
||||
party.Address,
|
||||
invoice.InvoiceContact,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['Configuration']
|
||||
__all__ = ['Configuration', 'Cron']
|
||||
|
||||
|
||||
class Configuration(metaclass=PoolMeta):
|
||||
|
@ -15,3 +15,14 @@ class Configuration(metaclass=PoolMeta):
|
|||
mail_send_invoice_subject = fields.Char('Email subject of Invoice report',
|
||||
help="In the text will be added suffix with the invoice report number")
|
||||
mail_send_invoice_body = fields.Text('Email body of Invoice report')
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.extend([
|
||||
('account.invoice|cron_send_invoice', "Cron Send Of Invoice"),
|
||||
])
|
||||
|
|
|
@ -17,6 +17,8 @@ from trytond.pyson import Eval, Bool, Or
|
|||
from trytond.transaction import Transaction
|
||||
from trytond.tools import get_smtp_server
|
||||
from trytond.config import config
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Invoice', 'InvoiceContact', 'InvoiceLine', 'CreditInvoice',
|
||||
'PopulateInvoiceContactsStart', 'PopulateInvoiceContacts', 'SendOfInvoice']
|
||||
|
@ -50,9 +52,6 @@ class Invoice(metaclass=PoolMeta):
|
|||
super(Invoice, cls).__setup__()
|
||||
cls._check_modify_exclude.extend(['sent', 'sent_date',
|
||||
'invoice_contacts', 'no_send_invoice'])
|
||||
cls._error_messages.update({
|
||||
'not_invoice_contacts': 'Invoice Contacts field must have a value',
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
|
@ -90,7 +89,8 @@ class Invoice(metaclass=PoolMeta):
|
|||
if invoice.type == 'out':
|
||||
if (not invoice.no_send_invoice and not
|
||||
invoice.invoice_contacts):
|
||||
cls.raise_user_error('not_invoice_contacts')
|
||||
raise UserError(gettext(
|
||||
'lims_account_invoice.msg_not_invoice_contacts'))
|
||||
|
||||
@classmethod
|
||||
def cron_send_invoice(cls):
|
||||
|
@ -273,10 +273,6 @@ class InvoiceLine(metaclass=PoolMeta):
|
|||
def __setup__(cls):
|
||||
super(InvoiceLine, cls).__setup__()
|
||||
cls.origin.states['readonly'] = True
|
||||
cls._error_messages.update({
|
||||
'delete_service_invoice': ('You can not delete an invoice line '
|
||||
'related to a service ("%s")'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def delete(cls, lines):
|
||||
|
@ -289,8 +285,9 @@ class InvoiceLine(metaclass=PoolMeta):
|
|||
for line in lines:
|
||||
if (line.origin and line.origin.__name__ == 'lims.service' and not
|
||||
line.economic_offer):
|
||||
cls.raise_user_error('delete_service_invoice',
|
||||
(line.origin.rec_name,))
|
||||
raise UserError(
|
||||
gettext('lims_account_invoice.msg_delete_service_invoice',
|
||||
service=line.origin.rec_name))
|
||||
|
||||
@classmethod
|
||||
def get_fraction_field(cls, lines, names):
|
||||
|
|
|
@ -69,32 +69,12 @@
|
|||
<field name="group" ref="group_lims_account_invoice"/>
|
||||
</record>
|
||||
|
||||
<!-- Users -->
|
||||
|
||||
<record model="res.user" id="user_send_invoice">
|
||||
<field name="login">user_cron_send_invoice</field>
|
||||
<field name="name">Cron Send Of Invoice</field>
|
||||
<field name="signature"></field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_send_invoice_group_lims_account_invoice">
|
||||
<field name="user" ref="user_send_invoice"/>
|
||||
<field name="group" ref="group_lims_account_invoice"/>
|
||||
</record>
|
||||
|
||||
<!-- Cron Send Of Invoice -->
|
||||
|
||||
<record model="ir.cron" id="cron_account_invoice_send_invoice">
|
||||
<field name="name">Send Invoice</field>
|
||||
<field name="request_user" ref="res.user_admin"/>
|
||||
<field name="user" ref="user_send_invoice"/>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="number_calls" eval="-1"/>
|
||||
<field name="repeat_missed" eval="False"/>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="function">cron_send_invoice</field>
|
||||
<field name="method">account.invoice|cron_send_invoice</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wiz_send_invoice">
|
||||
|
|
|
@ -7,6 +7,8 @@ from decimal import Decimal
|
|||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['FractionType', 'Entry', 'Fraction', 'Service', 'ManageServices']
|
||||
|
||||
|
@ -67,16 +69,6 @@ class Fraction(metaclass=PoolMeta):
|
|||
class Service(metaclass=PoolMeta):
|
||||
__name__ = 'lims.service'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Service, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'missing_account_revenue': ('Analysis product \"%(product)s\" in '
|
||||
'Service "%(service)s" misses an "account revenue".'),
|
||||
'delete_service_invoice': ('You can not delete a service '
|
||||
'related to an invoice'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
services = super(Service, cls).create(vlist)
|
||||
|
@ -108,10 +100,10 @@ class Service(metaclass=PoolMeta):
|
|||
return
|
||||
account_revenue = product.account_revenue_used
|
||||
if not account_revenue:
|
||||
self.raise_user_error('missing_account_revenue', {
|
||||
'product': self.analysis.product.rec_name,
|
||||
'service': self.rec_name,
|
||||
})
|
||||
raise UserError(
|
||||
gettext('lims_account_invoice.msg_missing_account_revenue',
|
||||
product=self.analysis.product.rec_name,
|
||||
service=self.rec_name))
|
||||
|
||||
party = self.entry.invoice_party
|
||||
taxes = []
|
||||
|
@ -167,7 +159,8 @@ class Service(metaclass=PoolMeta):
|
|||
if lines_to_delete:
|
||||
for line in lines_to_delete:
|
||||
if line.invoice:
|
||||
cls.raise_user_error('delete_service_invoice')
|
||||
raise UserError(gettext(
|
||||
'lims_account_invoice.msg_delete_service_invoice'))
|
||||
with Transaction().set_context(_check_access=False,
|
||||
delete_service=True):
|
||||
InvoiceLine.delete(lines_to_delete)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_not_invoice_contacts">
|
||||
<field name="text">Invoice Contacts field must have a value</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_delete_service_invoice">
|
||||
<field name="text">You can not delete an invoice line related to a service ("%(service)s")</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_missing_account_revenue">
|
||||
<field name="text">Analysis product \"%(product)s\" in Service "%(service)s" misses an "account revenue".</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Party', 'Address']
|
||||
|
||||
|
@ -40,4 +42,5 @@ class Address(metaclass=PoolMeta):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if addresses:
|
||||
self.raise_user_error('invoice_address')
|
||||
raise UserError(
|
||||
gettext('lims_account_invoice.msg_invoice_address'))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
account_invoice
|
||||
|
@ -9,3 +9,4 @@ xml:
|
|||
invoice.xml
|
||||
lims.xml
|
||||
stock.xml
|
||||
message.xml
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
analytic_account
|
||||
|
|
|
@ -11,6 +11,7 @@ from . import digital_sign
|
|||
def register():
|
||||
Pool.register(
|
||||
configuration.Configuration,
|
||||
configuration.Cron,
|
||||
lims.ResultsReportVersionDetail,
|
||||
lims.ResultsReport,
|
||||
digital_sign.DigitalSignStart,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['Configuration']
|
||||
__all__ = ['Configuration', 'Cron']
|
||||
|
||||
|
||||
class Configuration(metaclass=PoolMeta):
|
||||
|
@ -18,3 +18,15 @@ class Configuration(metaclass=PoolMeta):
|
|||
mail_ack_report_body = fields.Text('Email body of Acknowledgment of'
|
||||
' results report',
|
||||
help='<SAMPLES> will be replaced by the list of sample\'s labels')
|
||||
|
||||
|
||||
class Cron(metaclass=PoolMeta):
|
||||
__name__ = 'ir.cron'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.method.selection.extend([
|
||||
('lims.results_repor|cron_digital_signs',
|
||||
"Cron Lims Digital Sign"),
|
||||
])
|
||||
|
|
|
@ -12,32 +12,12 @@
|
|||
<field name="group" ref="group_lims_digital_sign"/>
|
||||
</record>
|
||||
|
||||
<!-- Users -->
|
||||
|
||||
<record model="res.user" id="user_digital_sign">
|
||||
<field name="login">user_cron_digital_sign</field>
|
||||
<field name="name">Cron Lims Digital Sign</field>
|
||||
<field name="signature"></field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="res.user-res.group" id="user_digital_sign_group_digital_sign">
|
||||
<field name="user" ref="user_digital_sign"/>
|
||||
<field name="group" ref="group_lims_digital_sign"/>
|
||||
</record>
|
||||
|
||||
<!-- Cron -->
|
||||
|
||||
<record model="ir.cron" id="cron_lims_digital_sign_results_reports">
|
||||
<field name="name">Lims Digital Sign Results Reports</field>
|
||||
<field name="request_user" ref="res.user_admin"/>
|
||||
<field name="user" ref="user_digital_sign"/>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number" eval="1"/>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="number_calls" eval="-1"/>
|
||||
<field name="repeat_missed" eval="False"/>
|
||||
<field name="model">lims.results_report</field>
|
||||
<field name="function">cron_digital_signs</field>
|
||||
<field name="method">lims.results_repor|cron_digital_signs</field>
|
||||
</record>
|
||||
|
||||
<!-- Wizard Digital Sign -->
|
||||
|
|
|
@ -16,6 +16,7 @@ from trytond.pool import Pool, PoolMeta
|
|||
from trytond.transaction import Transaction
|
||||
from trytond.tools import get_smtp_server
|
||||
from trytond.config import config as tconfig
|
||||
from trytond.i18n import gettext
|
||||
from .tokenclient import GetToken
|
||||
|
||||
__all__ = ['ResultsReportVersionDetail', 'ResultsReport',
|
||||
|
@ -61,13 +62,6 @@ class ResultsReport(metaclass=PoolMeta):
|
|||
sent = fields.Boolean('Sent', readonly=True)
|
||||
sent_date = fields.DateTime('Sent date', readonly=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(ResultsReport, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'polisample': 'Polisample',
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _get_modified_fields(cls):
|
||||
fields = super(ResultsReport, cls)._get_modified_fields()
|
||||
|
@ -263,8 +257,7 @@ class ResultsReport(metaclass=PoolMeta):
|
|||
if len(sample_list) == 1:
|
||||
label = '%s' % sample_list[0]
|
||||
else:
|
||||
label = self.raise_user_error('polisample',
|
||||
raise_exception=False)
|
||||
label = gettext('lims_digital_sign.msg_polisample')
|
||||
subject = str('%s %s (%s)' % (
|
||||
config.mail_ack_report_subject,
|
||||
self.number, label)).strip()
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_polisample">
|
||||
<field name="text">Polisample</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -1,7 +1,8 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
xml:
|
||||
digital_sign.xml
|
||||
configuration.xml
|
||||
message.xml
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_not_module">
|
||||
<field name="text">No module for importer type "%(module)s"</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_implemented">
|
||||
<field name="text">The function "%(function)s" is not implemented for this importer</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invalid_alias">
|
||||
<field name="text">Invalid symbol "%(symbol)s" in formula "%(name)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invalid_store">
|
||||
<field name="text">Formula "%(formula)s" cannot be stored because type is not set.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_end_date">
|
||||
<field name="text">End date cannot be empty</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_end_date_start_date">
|
||||
<field name="text">End date cannot be lower than Start date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inj_date_start_date">
|
||||
<field name="text">Injection date cannot be lower than Start date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_inj_date_end_date">
|
||||
<field name="text">Injection date cannot be upper than End date</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_professionals">
|
||||
<field name="text">Professional(s) with code %(code)s not identified</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -2,10 +2,7 @@
|
|||
# This file is part of lims_instrument module for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
try:
|
||||
import io as StringIO
|
||||
except ImportError:
|
||||
import io
|
||||
import io
|
||||
import traceback
|
||||
import xlrd
|
||||
from xlutils.copy import copy
|
||||
|
@ -15,6 +12,8 @@ from trytond.model import ModelView, ModelSQL, fields, Unique
|
|||
from trytond.wizard import Wizard, StateView, StateTransition, Button
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
|
||||
__all__ = ['NotebookLine', 'ResultsImport', 'NotebookLoadResultsFileStart',
|
||||
|
@ -60,11 +59,6 @@ class ResultsImport(ModelSQL, ModelView):
|
|||
('name_uniq', Unique(t, t.name),
|
||||
'The results importer name must be unique'),
|
||||
]
|
||||
cls._error_messages.update({
|
||||
'not_module': 'No module for importer type "%s"',
|
||||
'not_implemented': ('The function "%s" is not implemented for'
|
||||
' this importer'),
|
||||
})
|
||||
|
||||
@fields.depends('name')
|
||||
def on_change_with_description(self, name=None):
|
||||
|
@ -75,12 +69,12 @@ class ResultsImport(ModelSQL, ModelView):
|
|||
try:
|
||||
description = self.controller.getControllerName()
|
||||
except AttributeError:
|
||||
self.raise_user_error('not_implemented',
|
||||
('getControllerName',))
|
||||
raise UserError(gettext('lims_instrument.msg_not_implemented',
|
||||
function='getControllerName'))
|
||||
return description
|
||||
|
||||
def loadController(self):
|
||||
self.raise_user_error('not_module', (self.name,))
|
||||
raise UserError(gettext('lims_instrument.msg_not_module', module=self.name))
|
||||
|
||||
def getInputFile(self):
|
||||
return self._infile
|
||||
|
@ -95,7 +89,7 @@ class ResultsImport(ModelSQL, ModelView):
|
|||
return self.controller.parse(self, infile)
|
||||
except AttributeError:
|
||||
traceback.print_exc()
|
||||
self.raise_user_error('not_implemented', ('parse',))
|
||||
raise UserError(gettext('lims_instrument.msg_not_implemented', function='parse'))
|
||||
|
||||
def exportResults(self):
|
||||
'''
|
||||
|
@ -298,19 +292,6 @@ class NotebookLoadResultsFile(Wizard):
|
|||
Button('Done', 'end', 'tryton-close', default=True),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(NotebookLoadResultsFile, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'end_date': 'End date cannot be empty',
|
||||
'end_date_start_date': 'End date cannot be lower than Start date',
|
||||
'inj_date_start_date': ('Injection date cannot be lower than '
|
||||
'Start date'),
|
||||
'inj_date_end_date': ('Injection date cannot be upper than '
|
||||
'End date'),
|
||||
'professionals': 'Professional(s) with code %s not identified',
|
||||
})
|
||||
|
||||
def transition_collect(self):
|
||||
cursor = Transaction().connection.cursor()
|
||||
pool = Pool()
|
||||
|
@ -488,23 +469,19 @@ class NotebookLoadResultsFile(Wizard):
|
|||
if line.imported_result != '-1000.0':
|
||||
if not line.imported_end_date:
|
||||
prevent_line = True
|
||||
outcome = self.raise_user_error('end_date',
|
||||
raise_exception=False)
|
||||
outcome = gettext('lims_instrument.msg_end_date')
|
||||
elif (line.imported_end_date and line.start_date and
|
||||
line.start_date > line.imported_end_date):
|
||||
prevent_line = True
|
||||
outcome = self.raise_user_error('end_date_start_date',
|
||||
raise_exception=False)
|
||||
outcome = gettext('lims_instrument.msg_end_date_start_date')
|
||||
elif (line.imported_inj_date and line.start_date and
|
||||
line.start_date > line.imported_inj_date):
|
||||
prevent_line = True
|
||||
outcome = self.raise_user_error('inj_date_start_date',
|
||||
raise_exception=False)
|
||||
outcome = gettext('lims_instrument.msg_inj_date_start_date')
|
||||
elif (line.imported_end_date and line.imported_inj_date and
|
||||
line.imported_inj_date > line.imported_end_date):
|
||||
prevent_line = True
|
||||
outcome = self.raise_user_error('inj_date_end_date',
|
||||
raise_exception=False)
|
||||
outcome = gettext('lims_instrument.msg_inj_date_end_date')
|
||||
else:
|
||||
line.result = line.imported_result
|
||||
line.end_date = line.imported_end_date
|
||||
|
@ -543,9 +520,8 @@ class NotebookLoadResultsFile(Wizard):
|
|||
outcome = msg
|
||||
else:
|
||||
prevent_line = True
|
||||
outcome = self.raise_user_error('professionals',
|
||||
(str(line.imported_professionals),),
|
||||
raise_exception=False)
|
||||
outcome = gettext('lims_instrument.msg_professionals',
|
||||
code=str(line.imported_professionals))
|
||||
|
||||
if prevent_line:
|
||||
warnings = True
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
xml:
|
||||
resultsimport.xml
|
||||
resultsimport.xml
|
||||
message.xml
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_instrument
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_instrument
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_instrument
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_quantity_multiple_required">
|
||||
<field name="text">Quantity multiple of output bom required.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invalid_product_uom_category">
|
||||
<field name="text">The UoM\'s Category of each Product should be the same as the UoM\'s Category of Family/Equivalent.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -9,6 +9,8 @@ from trytond.pyson import Eval, Bool
|
|||
from trytond.pool import PoolMeta, Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['BOM', 'Production', 'FamilyEquivalentReport']
|
||||
|
||||
|
@ -50,14 +52,6 @@ class Production(metaclass=PoolMeta):
|
|||
'on_change_with_salable_product')
|
||||
comments = fields.Text('Comments')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Production, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'quantity_multiple_required': ('Quantity multiple of output bom '
|
||||
'required.'),
|
||||
})
|
||||
|
||||
@fields.depends('product')
|
||||
def on_change_with_salable_product(self, name=None):
|
||||
if self.product:
|
||||
|
@ -96,7 +90,8 @@ class Production(metaclass=PoolMeta):
|
|||
for output in self.bom.outputs:
|
||||
quantity += output.quantity
|
||||
if not (self.quantity % quantity == 0):
|
||||
self.raise_user_error('quantity_multiple_required')
|
||||
raise UserError(
|
||||
gettext('lims_production.msg_quantity_multiple_required'))
|
||||
|
||||
outputs = []
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ from trytond.transaction import Transaction
|
|||
from trytond.pool import PoolMeta, Pool
|
||||
from trytond.wizard import Wizard, StateAction
|
||||
from trytond.modules.product import price_digits
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['PurityDegree', 'Brand', 'FamilyEquivalent', 'Template', 'Product',
|
||||
'LotCategory', 'Lot', 'Move', 'ShipmentIn', 'MoveProductionRelated']
|
||||
|
@ -46,15 +48,6 @@ class FamilyEquivalent(ModelSQL, ModelView):
|
|||
products = fields.One2Many('product.template', 'family_equivalent',
|
||||
'Products', readonly=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(FamilyEquivalent, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'invalid_product_uom_category': ('The UoM\'s Category '
|
||||
'of each Product should be the same as the UoM\'s '
|
||||
'Category of Family/Equivalent.'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def validate(cls, family_equivalents):
|
||||
super(FamilyEquivalent, cls).validate(family_equivalents)
|
||||
|
@ -66,7 +59,8 @@ class FamilyEquivalent(ModelSQL, ModelView):
|
|||
main_category = self.uom.category
|
||||
for product in self.products:
|
||||
if main_category != product.default_uom.category:
|
||||
self.raise_user_error('invalid_product_uom_category')
|
||||
raise UserError(gettext(
|
||||
'lims_production.msg_invalid_product_uom_category'))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, family_equivalents, default=None):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
production
|
||||
|
@ -9,4 +9,5 @@ depends:
|
|||
xml:
|
||||
stock.xml
|
||||
production.xml
|
||||
configuration.xml
|
||||
configuration.xml
|
||||
message.xml
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
xml:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_project
|
||||
xml:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_no_project_study_plan_sequence">
|
||||
<field name="text">There is no sequence for Study plan Projects for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_glp">
|
||||
<field name="text">Please, select a "Study plan" Project to print this report</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_analytical_phase">
|
||||
<field name="text">Please, select a "Analytical Phase Project" to print this report</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_not_study_plan">
|
||||
<field name="text">Please, select a "Study Plan Phase Project" to print this report</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_existing_role_study_director">
|
||||
<field name="text">There is already a Study director for this project</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_existing_role_facility_director">
|
||||
<field name="text">There is already a Facility director for this project</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_existing_role_quality_unit">
|
||||
<field name="text">There is already a Quality unit for this project</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -11,6 +11,8 @@ from trytond.transaction import Transaction
|
|||
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
|
||||
Button
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Project', 'Entry', 'ProjectReferenceElement',
|
||||
'ProjectSolventAndReagent', 'ProjectSampleInCustody',
|
||||
|
@ -226,16 +228,6 @@ class Project(metaclass=PoolMeta):
|
|||
'invisible': (Eval('stp_state') != 'finalized'),
|
||||
},
|
||||
})
|
||||
cls._error_messages.update({
|
||||
'no_project_study_plan_sequence': ('There is no sequence for '
|
||||
'Study plan Projects for the work year "%s".'),
|
||||
'not_glp': ('Please, select a "Study plan" Project to print this '
|
||||
'report'),
|
||||
'not_analytical_phase': ('Please, select a "Analytical Phase '
|
||||
'Project" to print this report'),
|
||||
'not_study_plan': ('Please, select a "Study Plan Phase '
|
||||
'Project" to print this report'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
|
@ -254,8 +246,9 @@ class Project(metaclass=PoolMeta):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('project_study_plan')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_project_study_plan_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims_project_study_plan.msg_no_project_study_plan_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
@ -297,7 +290,8 @@ class Project(metaclass=PoolMeta):
|
|||
if line.device.id not in devices:
|
||||
devices[line.device.id] = line.device.rec_name
|
||||
if devices:
|
||||
stp_test_system = '\n'.join([d for d in list(devices.values())])
|
||||
stp_test_system = '\n'.join(
|
||||
[d for d in list(devices.values())])
|
||||
self.stp_test_system = stp_test_system
|
||||
|
||||
@ModelView.button_change('stp_test_method')
|
||||
|
@ -315,7 +309,8 @@ class Project(metaclass=PoolMeta):
|
|||
if line.method.id not in methods:
|
||||
methods[line.method.id] = line.method.rec_name
|
||||
if methods:
|
||||
stp_test_method = '\n'.join([m for m in list(methods.values())])
|
||||
stp_test_method = '\n'.join(
|
||||
[m for m in list(methods.values())])
|
||||
self.stp_test_method = stp_test_method
|
||||
|
||||
@classmethod
|
||||
|
@ -413,18 +408,6 @@ class ProjectLaboratoryProfessional(ModelSQL, ModelView):
|
|||
role_other = fields.Boolean('Other')
|
||||
approval_date = fields.Date('Approval date')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(ProjectLaboratoryProfessional, cls).__setup__()
|
||||
cls._error_messages.update({
|
||||
'existing_role_study_director': ('There is already a '
|
||||
'Study director for this project'),
|
||||
'existing_role_facility_director': ('There is already a '
|
||||
'Facility director for this project'),
|
||||
'existing_role_quality_unit': ('There is already a '
|
||||
'Quality unit for this project'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def validate(cls, professionals):
|
||||
super(ProjectLaboratoryProfessional, cls).validate(professionals)
|
||||
|
@ -441,7 +424,8 @@ class ProjectLaboratoryProfessional(ModelSQL, ModelView):
|
|||
('id', '!=', self.id),
|
||||
])
|
||||
if existing_roles:
|
||||
self.raise_user_error('existing_' + field)
|
||||
raise UserError(gettext(
|
||||
'lims_project_study_plan.msg_existing_' + field))
|
||||
|
||||
@fields.depends('role_study_director', 'role_facility_director',
|
||||
'role_quality_unit', 'role_other')
|
||||
|
@ -970,11 +954,11 @@ class ProjectGLPReport01(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport01, cls).execute(ids, data)
|
||||
|
||||
|
@ -1033,11 +1017,11 @@ class ProjectGLPReport02(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport02, cls).execute(ids, data)
|
||||
|
||||
|
@ -1135,7 +1119,7 @@ class ProjectGLPReport03(Report):
|
|||
|
||||
project = Project(data['id'])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport03, cls).execute(ids, data)
|
||||
|
||||
|
@ -1192,11 +1176,11 @@ class ProjectGLPReport04(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport04, cls).execute(ids, data)
|
||||
|
||||
|
@ -1299,7 +1283,7 @@ class ProjectGLPReport05(Report):
|
|||
|
||||
project = Project(data['id'])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport05, cls).execute(ids, data)
|
||||
|
||||
|
@ -1354,11 +1338,11 @@ class ProjectGLPReport06(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport06, cls).execute(ids, data)
|
||||
|
||||
|
@ -1411,11 +1395,11 @@ class ProjectGLPReport07(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport07, cls).execute(ids, data)
|
||||
|
||||
|
@ -1476,11 +1460,11 @@ class ProjectGLPReport08(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport08, cls).execute(ids, data)
|
||||
|
||||
|
@ -1538,11 +1522,11 @@ class ProjectGLPReport09(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport09, cls).execute(ids, data)
|
||||
|
||||
|
@ -1608,7 +1592,7 @@ class ProjectGLPReport09(Report):
|
|||
re = None
|
||||
analysis = None
|
||||
if report_id[1] == 'eq':
|
||||
re = report_id[2]
|
||||
re = report_id[2]
|
||||
else:
|
||||
if report_id[1] == 'low':
|
||||
re = '< ' + report_id[2]
|
||||
|
@ -1754,11 +1738,11 @@ class ProjectGLPReport11(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport11, cls).execute(ids, data)
|
||||
|
||||
|
@ -1913,11 +1897,11 @@ class ProjectGLPReportStudyPlan(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReportStudyPlan, cls).execute(ids, data)
|
||||
|
||||
|
@ -2007,14 +1991,15 @@ class ProjectGLPReportFinalRP(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
else:
|
||||
if project.stp_phase != 'study_plan':
|
||||
Project.raise_user_error('not_study_plan')
|
||||
raise UserError(gettext(
|
||||
'lims_project_study_plan.msg_not_study_plan'))
|
||||
return super(ProjectGLPReportFinalRP, cls).execute(ids, data)
|
||||
|
||||
@classmethod
|
||||
|
@ -2248,14 +2233,15 @@ class ProjectGLPReportFinalFOR(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
else:
|
||||
if project.stp_phase != 'study_plan':
|
||||
Project.raise_user_error('not_study_plan')
|
||||
raise UserError(gettext(
|
||||
'lims_project_study_plan.msg_not_study_plan'))
|
||||
return super(ProjectGLPReportFinalFOR, cls).execute(ids, data)
|
||||
|
||||
@classmethod
|
||||
|
@ -2494,14 +2480,15 @@ class ProjectGLPReportAnalyticalPhase(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
else:
|
||||
if project.stp_phase != 'analytical_phase':
|
||||
Project.raise_user_error('not_analytical_phase')
|
||||
raise UserError(gettext(
|
||||
'lims_project_study_plan.msg_not_analytical_phase'))
|
||||
return super(ProjectGLPReportAnalyticalPhase,
|
||||
cls).execute(ids, data)
|
||||
|
||||
|
@ -2736,11 +2723,11 @@ class ProjectGLPReport13(Report):
|
|||
def execute(cls, ids, data):
|
||||
Project = Pool().get('lims.project')
|
||||
if len(ids) > 1:
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
project = Project(ids[0])
|
||||
if project.type != 'study_plan':
|
||||
Project.raise_user_error('not_glp')
|
||||
raise UserError(gettext('lims_project_study_plan.msg_not_glp'))
|
||||
|
||||
return super(ProjectGLPReport13, cls).execute(ids, data)
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_project
|
||||
lims_production
|
||||
xml:
|
||||
project.xml
|
||||
configuration.xml
|
||||
message.xml
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_no_project_tas_sequence">
|
||||
<field name="text">There is no sequence for TAS Projects for the work year "%(work_year)s".</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -6,6 +6,8 @@
|
|||
from trytond.model import ModelView, ModelSQL, fields, Unique
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, Equal, Bool, Not
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['TasType', 'Project', 'Entry']
|
||||
|
||||
|
@ -44,10 +46,6 @@ class Project(metaclass=PoolMeta):
|
|||
cls.type.selection.append(project_type)
|
||||
cls.client.states = STATES
|
||||
cls.client.depends = DEPENDS
|
||||
cls._error_messages.update({
|
||||
'no_project_tas_sequence': ('There is no sequence for '
|
||||
'TAS Projects for the work year "%s".'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
|
@ -115,8 +113,9 @@ class Project(metaclass=PoolMeta):
|
|||
workyear = LabWorkYear(workyear_id)
|
||||
sequence = workyear.get_sequence('project_tas')
|
||||
if not sequence:
|
||||
cls.raise_user_error('no_project_tas_sequence',
|
||||
(workyear.rec_name,))
|
||||
raise UserError(gettext(
|
||||
'lims_project_tas.msg_no_project_tas_sequence',
|
||||
work_year=workyear.rec_name))
|
||||
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for values in vlist:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_project
|
||||
extras_depend:
|
||||
|
@ -8,3 +8,4 @@ xml:
|
|||
project.xml
|
||||
invoice.xml
|
||||
configuration.xml
|
||||
message.xml
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<tryton>
|
||||
<data group="1">
|
||||
<record model="ir.message" id="msg_not_water">
|
||||
<field name="text">Please, select a "Water sampling" Project to print this report</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -8,6 +8,8 @@ from trytond.pool import Pool, PoolMeta
|
|||
from trytond.pyson import Eval, Equal, Bool, Not
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.report import Report
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.i18n import gettext
|
||||
|
||||
__all__ = ['Project', 'Entry', 'Sample', 'CreateSampleStart', 'CreateSample']
|
||||
|
||||
|
@ -29,10 +31,6 @@ class Project(metaclass=PoolMeta):
|
|||
project_type = PROJECT_TYPE
|
||||
if project_type not in cls.type.selection:
|
||||
cls.type.selection.append(project_type)
|
||||
cls._error_messages.update({
|
||||
'not_water': ('Please, select a "Water sampling" Project to print '
|
||||
'this report'),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
|
@ -140,7 +138,7 @@ class ProjectWaterSampling(Report):
|
|||
|
||||
project = Project(data['id'])
|
||||
if project.type != 'water':
|
||||
Project.raise_user_error('not_water')
|
||||
raise UserError(gettext('lims_project_water.msg_not_water'))
|
||||
|
||||
return super(ProjectWaterSampling, cls).execute(ids, data)
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims_project
|
||||
xml:
|
||||
project.xml
|
||||
message.xml
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=5.0.0
|
||||
version=5.2.0
|
||||
depends:
|
||||
lims
|
||||
sale
|
||||
|
|
4
setup.py
4
setup.py
|
@ -35,12 +35,12 @@ def get_require_version(name):
|
|||
return require
|
||||
|
||||
|
||||
version = '5.0.0'
|
||||
version = '5.2.0'
|
||||
major_version, minor_version, _ = version.split('.', 2)
|
||||
major_version = int(major_version)
|
||||
minor_version = int(minor_version)
|
||||
|
||||
requires = ['pytz', 'xlrd', 'xlutils', 'PyPDF2']
|
||||
requires = ['pytz', 'xlrd', 'xlutils', 'PyPDF2', 'unidecode']
|
||||
packages = []
|
||||
package_dir = {}
|
||||
package_data = {}
|
||||
|
|
Loading…
Reference in New Issue