kalenislims/lims_industry/industry.py

666 lines
23 KiB
Python
Raw Permalink Normal View History

2019-08-29 18:47:58 +02:00
# This file is part of lims_industry module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, DeactivableMixin, fields, Unique
2019-09-04 23:06:00 +02:00
from trytond.pool import Pool
from trytond.pyson import Eval, If
from trytond.transaction import Transaction
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond import backend
2019-08-29 18:47:58 +02:00
class Plant(ModelSQL, ModelView):
'Plant'
__name__ = 'lims.plant'
party = fields.Many2One('party.party', 'Party', required=True,
ondelete='CASCADE', select=True)
2019-08-29 18:47:58 +02:00
name = fields.Char('Name', required=True)
street = fields.Char('Street', required=True)
2021-09-23 01:11:28 +02:00
postal_code = fields.Char('Postal Code', required=True)
2019-08-29 18:47:58 +02:00
city = fields.Char('City', required=True)
subdivision = fields.Many2One('country.subdivision',
'Subdivision', required=True, domain=[
('country', '=', Eval('country', -1)),
('parent', '=', None),
],
depends=['country'])
country = fields.Many2One('country.country', 'Country',
required=True)
equipments = fields.One2Many('lims.equipment', 'plant',
'Equipments')
contacts = fields.One2Many('party.address', 'plant',
'Contacts', domain=[('party', '=', Eval('party'))],
depends=['party'])
invoice_party = fields.Many2One('party.party', 'Invoice Party')
latitude = fields.Numeric('Latitude', digits=(3, 14))
longitude = fields.Numeric('Longitude', digits=(4, 14))
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
2019-08-29 18:47:58 +02:00
cls._order.insert(0, ('party', 'ASC'))
cls._order.insert(1, ('name', 'ASC'))
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.party, t.name),
'lims_industry.msg_plant_name_unique'),
]
2019-08-29 18:47:58 +02:00
2021-09-23 01:11:28 +02:00
@classmethod
def __register__(cls, module_name):
table = cls.__table_handler__(module_name)
table.column_rename('zip', 'postal_code')
super().__register__(module_name)
@staticmethod
def default_country():
Company = Pool().get('company.company')
company_id = Transaction().context.get('company')
if company_id:
address = Company(company_id).party.address_get()
if address and address.country:
return address.country.id
def get_rec_name(self, name):
res = '%s [%s]' % (self.name, self.party.name)
return res
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
current_default['equipments'] = None
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class EquipmentType(ModelSQL, ModelView):
'Equipment Type'
__name__ = 'lims.equipment.type'
name = fields.Char('Name', required=True)
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.name),
'lims_industry.msg_equipment_type_name_unique'),
]
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class Brand(ModelSQL, ModelView):
'Brand'
__name__ = 'lims.brand'
name = fields.Char('Name', required=True)
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.name),
'lims_industry.msg_brand_name_unique'),
]
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class ComponentKind(ModelSQL, ModelView):
'Component Kind'
__name__ = 'lims.component.kind'
name = fields.Char('Name', required=True)
product_type = fields.Many2One('lims.product.type', 'Product type',
required=True)
@classmethod
def __setup__(cls):
super().__setup__()
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.name),
'lims_industry.msg_component_kind_name_unique'),
]
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
class ComponentLocation(ModelSQL, ModelView):
'Component Location'
__name__ = 'lims.component.location'
name = fields.Char('Name', required=True)
2019-08-29 18:47:58 +02:00
class ComponentType(ModelSQL, ModelView):
'Component Type'
__name__ = 'lims.component.type'
name = fields.Char('Name', required=True)
product_type = fields.Many2One('lims.product.type', 'Product type',
required=True)
kind = fields.Many2One('lims.component.kind', 'Kind')
location = fields.Many2One('lims.component.location', 'Location')
2019-08-29 18:47:58 +02:00
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.name),
'lims_industry.msg_component_type_name_unique'),
]
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class EquipmentTemplate(ModelSQL, ModelView):
'Equipment Template'
__name__ = 'lims.equipment.template'
type = fields.Many2One('lims.equipment.type', 'Type', required=True)
brand = fields.Many2One('lims.brand', 'Brand', required=True)
2019-09-04 23:06:00 +02:00
model = fields.Char('Model')
2019-08-29 18:47:58 +02:00
power = fields.Char('Power')
component_kinds = fields.One2Many(
'lims.equipment.template-component.kind',
'template', 'Component kinds')
2019-08-29 18:47:58 +02:00
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
2019-08-29 18:47:58 +02:00
cls._order.insert(0, ('type', 'ASC'))
cls._order.insert(1, ('brand', 'ASC'))
cls._order.insert(2, ('model', 'ASC'))
t = cls.__table__()
cls._sql_constraints = [
('type_brand_model_unique', Unique(t, t.type, t.brand, t.model),
'lims_industry.msg_equipment_template_unique'),
]
2019-08-29 18:47:58 +02:00
def get_rec_name(self, name):
2019-09-04 23:06:00 +02:00
res = '%s - %s' % (self.type.rec_name, self.brand.rec_name)
if self.model:
res += ' - ' + self.model
return res
2019-08-29 18:47:58 +02:00
@classmethod
def search_rec_name(cls, name, clause):
return ['OR',
('type.name',) + tuple(clause[1:]),
('brand.name',) + tuple(clause[1:]),
('model',) + tuple(clause[1:]),
]
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['model'] = '%s (copy)' % record.model
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class EquipmentTemplateComponentKind(ModelSQL, ModelView):
'Equipment Template - Component Kind'
__name__ = 'lims.equipment.template-component.kind'
_table = 'lims_equipment_template_component_kind'
2019-08-29 18:47:58 +02:00
template = fields.Many2One('lims.equipment.template', 'Template',
required=True, ondelete='CASCADE', select=True)
kind = fields.Many2One('lims.component.kind', 'Kind',
2019-08-29 18:47:58 +02:00
required=True, ondelete='CASCADE', select=True)
location = fields.Many2One('lims.component.location', 'Location')
2019-08-29 18:47:58 +02:00
@classmethod
def __register__(cls, module_name):
cursor = Transaction().connection.cursor()
TableHandler = backend.TableHandler
sql_table = cls.__table__()
super().__register__(module_name)
old_table_name = 'lims_equipment_template_component_type'
if TableHandler.table_exist(old_table_name):
cursor.execute('SELECT etct.template, ct.kind, ct.location '
'FROM lims_equipment_template_component_type etct '
'INNER JOIN lims_component_type ct '
'ON ct.id = etct.type '
'WHERE ct.kind IS NOT NULL')
res = cursor.fetchall()
if res:
cursor.execute(*sql_table.insert(
columns=[sql_table.template, sql_table.kind,
sql_table.location],
values=[[x[0], x[1], x[2]] for x in res]))
TableHandler.drop_table('', old_table_name)
2019-08-29 18:47:58 +02:00
class Equipment(DeactivableMixin, ModelSQL, ModelView):
2019-08-29 18:47:58 +02:00
'Equipment'
__name__ = 'lims.equipment'
template = fields.Many2One('lims.equipment.template', 'Template',
required=True)
name = fields.Char('Name', required=True)
2020-07-17 02:58:32 +02:00
type = fields.Function(fields.Many2One('lims.equipment.type', 'Type'),
'get_type', searcher='search_type')
brand = fields.Function(fields.Many2One('lims.brand', 'Brand'),
'get_brand', searcher='search_brand')
2019-09-04 23:06:00 +02:00
model = fields.Char('Model', required=True)
power = fields.Char('Power')
2021-07-15 01:07:56 +02:00
voltage = fields.Char('Primary Voltage')
voltage_secondary = fields.Char('Secondary Voltage')
amperage = fields.Char('Secondary Amperage')
2019-08-29 18:47:58 +02:00
serial_number = fields.Char('Serial number')
internal_id = fields.Char('Internal ID Code')
latitude = fields.Numeric('Latitude', digits=(3, 14))
longitude = fields.Numeric('Longitude', digits=(4, 14))
plant = fields.Many2One('lims.plant', 'Plant',
required=True, select=True,
domain=[If(Eval('context', {}).contains('party'),
('party', '=', Eval('context', {}).get('party', -1)),
())])
2019-08-29 18:47:58 +02:00
components = fields.One2Many('lims.component', 'equipment',
'Components')
year_manufacturing = fields.Integer('Year of manufacturing')
2021-07-15 01:07:56 +02:00
year_service_start = fields.Integer('Year of service start')
2019-08-29 18:47:58 +02:00
internal_location = fields.Char('Internal location')
contacts = fields.One2Many('party.address', 'equipment',
'Contacts', domain=[('party', '=', Eval('party'))],
context={'plant': Eval('plant')},
depends=['party', 'plant'])
2019-08-29 18:47:58 +02:00
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_party', searcher='search_party')
2019-09-04 23:06:00 +02:00
missing_data = fields.Boolean('Missing data')
2019-08-29 18:47:58 +02:00
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
2019-08-29 18:47:58 +02:00
cls._order.insert(0, ('template', 'ASC'))
cls._order.insert(1, ('name', 'ASC'))
t = cls.__table__()
cls._sql_constraints = [
('name_unique', Unique(t, t.plant, t.name),
'lims_industry.msg_equipment_name_unique'),
]
2019-08-29 18:47:58 +02:00
@classmethod
def create(cls, vlist):
TaskTemplate = Pool().get('lims.administrative.task.template')
2020-08-06 19:52:36 +02:00
equipments = super().create(vlist)
TaskTemplate.create_tasks('equipment_missing_data',
cls._for_task_missing_data(equipments))
return equipments
@classmethod
def _for_task_missing_data(cls, equipments):
AdministrativeTask = Pool().get('lims.administrative.task')
res = []
for equipment in equipments:
if not equipment.missing_data:
continue
if AdministrativeTask.search([
('type', '=', 'equipment_missing_data'),
('origin', '=', '%s,%s' % (cls.__name__, equipment.id)),
('state', 'not in', ('done', 'discarded')),
]):
continue
res.append(equipment)
return res
2020-06-26 02:33:43 +02:00
def get_rec_name(self, name):
2020-06-26 02:26:54 +02:00
res = '%s [%s]' % (self.name, self.plant.name)
return res
@classmethod
def search_rec_name(cls, name, clause):
return ['OR',
('name',) + tuple(clause[1:]),
('serial_number',) + tuple(clause[1:]),
('brand.name',) + tuple(clause[1:]),
('plant.name',) + tuple(clause[1:]),
('components.customer_description',) + tuple(clause[1:]),
]
2020-03-05 05:24:51 +01:00
@fields.depends('plant', '_parent_plant.party')
def on_change_with_party(self, name=None):
return self.get_party([self], name)[self.id]
@classmethod
def get_party(cls, equipments, name):
result = {}
for e in equipments:
result[e.id] = e.plant and e.plant.party.id or None
return result
2019-08-29 18:47:58 +02:00
@classmethod
def search_party(cls, name, clause):
return [('plant.party',) + tuple(clause[1:])]
2020-07-17 02:58:32 +02:00
@fields.depends('template', '_parent_template.type')
def on_change_with_type(self, name=None):
return self.get_type([self], name)[self.id]
@classmethod
def get_type(cls, equipments, name):
result = {}
for e in equipments:
result[e.id] = e.template and e.template.type.id or None
return result
@classmethod
def search_type(cls, name, clause):
return [('template.type',) + tuple(clause[1:])]
2020-03-05 05:24:51 +01:00
@fields.depends('template', '_parent_template.brand')
def on_change_with_brand(self, name=None):
return self.get_brand([self], name)[self.id]
@classmethod
def get_brand(cls, equipments, name):
result = {}
for e in equipments:
result[e.id] = e.template and e.template.brand.id or None
return result
@classmethod
def search_brand(cls, name, clause):
return [('template.brand',) + tuple(clause[1:])]
@fields.depends('template', 'components')
2019-09-04 23:06:00 +02:00
def on_change_template(self):
pool = Pool()
Component = pool.get('lims.component')
if not self.template:
return
current_components_ids = [(component.kind.id,
component.location and component.location.id or None)
for component in self.components]
components = list(self.components)
for record in self.template.component_kinds:
kind_id = record.kind.id
location_id = record.location and record.location.id or None
if (kind_id, location_id) in current_components_ids:
continue
value = Component(**Component.default_get(
list(Component._fields.keys()), with_rec_name=False))
value.kind = kind_id
value.location = location_id
components.append(value)
self.model = self.template.model
self.power = self.template.power
2019-09-04 23:06:00 +02:00
self.components = components
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['name'] = '%s (copy)' % record.name
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
class Component(ModelSQL, ModelView):
'Component'
__name__ = 'lims.component'
equipment = fields.Many2One('lims.equipment', 'Equipment',
required=True, ondelete='CASCADE', select=True)
kind = fields.Many2One('lims.component.kind', 'Kind', required=True)
location = fields.Many2One('lims.component.location', 'Location')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_product_type')
comercial_product = fields.Many2One('lims.comercial.product',
'Comercial product')
2019-08-29 18:47:58 +02:00
capacity = fields.Char('Capacity (lts)')
serial_number = fields.Char('Serial number')
model = fields.Char('Model')
power = fields.Char('Power')
brand = fields.Many2One('lims.brand', 'Brand')
internal_id = fields.Char('Internal ID Code')
customer_description = fields.Char('Customer description')
year_manufacturing = fields.Integer('Year of manufacturing')
plant = fields.Function(fields.Many2One('lims.plant', 'Plant'),
'get_plant', searcher='search_plant')
2019-08-29 18:47:58 +02:00
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_party', searcher='search_party')
missing_data = fields.Boolean('Missing data')
2019-08-29 18:47:58 +02:00
@classmethod
def __setup__(cls):
2020-08-06 19:52:36 +02:00
super().__setup__()
2019-08-29 18:47:58 +02:00
cls._order.insert(0, ('equipment', 'ASC'))
cls._order.insert(1, ('kind', 'ASC'))
t = cls.__table__()
cls._sql_constraints = [
('kind_location_description_unique', Unique(t, t.equipment,
t.kind, t.location, t.customer_description),
'lims_industry.msg_component_unique'),
]
2019-08-29 18:47:58 +02:00
@classmethod
def __register__(cls, module_name):
table_h = cls.__table_handler__(module_name)
type_exist = table_h.column_exist('type')
super().__register__(module_name)
if type_exist:
cursor = Transaction().connection.cursor()
ComponentType = Pool().get('lims.component.type')
cursor.execute('UPDATE "' + cls._table + '" c '
'SET kind = ct.kind, location = ct.location '
'FROM "' + ComponentType._table + '" ct '
'WHERE ct.id = c.type')
table_h.drop_constraint('type_unique')
table_h.drop_constraint('type_description_unique')
table_h.drop_column('type')
@classmethod
def create(cls, vlist):
TaskTemplate = Pool().get('lims.administrative.task.template')
2020-08-06 19:52:36 +02:00
components = super().create(vlist)
TaskTemplate.create_tasks('component_missing_data',
cls._for_task_missing_data(components))
return components
@classmethod
def _for_task_missing_data(cls, components):
AdministrativeTask = Pool().get('lims.administrative.task')
res = []
for component in components:
if not component.missing_data:
continue
if AdministrativeTask.search([
('type', '=', 'component_missing_data'),
('origin', '=', '%s,%s' % (cls.__name__, component.id)),
('state', 'not in', ('done', 'discarded')),
]):
continue
res.append(component)
return res
@classmethod
def delete(cls, components):
cls.check_delete(components)
super().delete(components)
@classmethod
def check_delete(cls, components):
Sample = Pool().get('lims.sample')
for component in components:
samples = Sample.search_count([
('component', '=', component.id),
])
if samples != 0:
raise UserError(gettext('lims_industry.msg_delete_component',
component=component.get_rec_name(None)))
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
current_default = default.copy()
new_records = []
for record in records:
current_default['customer_description'] = '%s (copy)' % (
record.customer_description)
new_record, = super().copy([record], default=current_default)
new_records.append(new_record)
return new_records
2019-08-29 18:47:58 +02:00
def get_rec_name(self, name):
res = self.kind.rec_name
if self.location:
res += ' ' + self.location.name
2019-08-29 18:47:58 +02:00
if self.brand:
res += ' - ' + self.brand.rec_name
if self.model:
res += ' - ' + self.model
2020-06-26 02:26:54 +02:00
if self.customer_description:
res += ' [' + self.customer_description + ']'
2019-08-29 18:47:58 +02:00
return res
@classmethod
def search_rec_name(cls, name, clause):
return ['OR',
('kind.name',) + tuple(clause[1:]),
('location.name',) + tuple(clause[1:]),
('brand.name',) + tuple(clause[1:]),
('model',) + tuple(clause[1:]),
('customer_description',) + tuple(clause[1:]),
]
@classmethod
def get_plant(cls, component, name):
result = {}
for c in component:
result[c.id] = c.equipment and c.equipment.plant.id or None
return result
@classmethod
def search_plant(cls, name, clause):
return [('equipment.plant',) + tuple(clause[1:])]
@classmethod
def get_party(cls, component, name):
result = {}
for c in component:
result[c.id] = c.equipment and c.equipment.plant.party.id or None
return result
2019-08-29 18:47:58 +02:00
@classmethod
def search_party(cls, name, clause):
return [('equipment.plant.party',) + tuple(clause[1:])]
@fields.depends('kind', '_parent_kind.product_type')
def on_change_with_product_type(self, name=None):
return self.get_product_type([self], name)[self.id]
@classmethod
def get_product_type(cls, components, name):
result = {}
for c in components:
result[c.id] = c.kind and c.kind.product_type.id or None
return result
class ComercialProductBrand(ModelSQL, ModelView):
'Comercial Product Brand'
__name__ = 'lims.comercial.product.brand'
name = fields.Char('Name', required=True)
class ComercialProduct(ModelSQL, ModelView):
'Comercial Product'
__name__ = 'lims.comercial.product'
name = fields.Char('Name', required=True)
brand = fields.Many2One('lims.comercial.product.brand', 'Brand',
required=True)
matrix = fields.Many2One('lims.matrix', 'Base/Matrix', required=True)
dangerous = fields.Boolean('Dangerous')
@staticmethod
def default_dangerous():
return False
2020-06-26 02:26:54 +02:00
def get_rec_name(self, name):
res = '%s %s' % (self.brand.name, self.name)
return res
@classmethod
def search_rec_name(cls, name, clause):
return ['OR',
('name',) + tuple(clause[1:]),
('brand.name',) + tuple(clause[1:]),
]