840 lines
31 KiB
Python
840 lines
31 KiB
Python
# The COPYRIGHT file at the top level of this repository contains the full
|
|
# copyright notices and license terms.
|
|
import datetime
|
|
from itertools import chain
|
|
from sql import Table
|
|
|
|
from trytond.model import ModelView, ModelSQL, Workflow, fields
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.transaction import Transaction
|
|
from trytond.wizard import Wizard, StateView, StateAction, Button
|
|
from trytond.pyson import Bool, Date, Equal, Eval, If, Or
|
|
from trytond import backend
|
|
from trytond.exceptions import UserError, UserWarning
|
|
from trytond.i18n import gettext
|
|
|
|
_STATES = {
|
|
'readonly': Eval('state') != 'draft',
|
|
}
|
|
_STATES_REQUIRED = {
|
|
'required': Eval('state') != 'draft',
|
|
'readonly': Eval('state') != 'draft',
|
|
}
|
|
_DEPENDS = ['state']
|
|
|
|
|
|
class Party(metaclass=PoolMeta):
|
|
__name__ = 'party.party'
|
|
|
|
veterinarian = fields.Boolean('Veterinarian')
|
|
collegiate_number = fields.Char('Collegiate Number', states={
|
|
'required': Eval('veterinarian', False),
|
|
'invisible': ~Eval('veterinarian', False),
|
|
}, depends=['veterinarian'])
|
|
|
|
|
|
class ProductTemplate(metaclass=PoolMeta):
|
|
__name__ = 'product.template'
|
|
|
|
prescription_required = fields.Boolean('Prescription required')
|
|
|
|
|
|
class Product(metaclass=PoolMeta):
|
|
__name__ = 'product.product'
|
|
|
|
prescription_required = fields.Function(fields.Boolean(
|
|
'Prescription required'),
|
|
'on_change_with_prescription_required',
|
|
searcher='search_prescription_required')
|
|
prescription_template = fields.Many2One('farm.prescription.template',
|
|
'Prescription Template',
|
|
domain=[
|
|
('product', '=', Eval('id')),
|
|
],
|
|
states={
|
|
'invisible': ~Eval('prescription_required', False),
|
|
},
|
|
depends=['id', 'prescription_required'])
|
|
|
|
@fields.depends('_parent_template.prescription_required' , 'template')
|
|
def on_change_with_prescription_required(self, name=None):
|
|
if self.template:
|
|
return self.template.prescription_required
|
|
return False
|
|
|
|
@classmethod
|
|
def search_prescription_required(cls, name, clause):
|
|
return [tuple(('template.%s' % name, )) + tuple(clause[1:])]
|
|
|
|
|
|
class PrescriptionMixin:
|
|
'''
|
|
Mixin class with the shared fields and methods by Prescription and Template
|
|
'''
|
|
__slots__ = ()
|
|
specie = fields.Many2One('farm.specie', 'Specie', domain=[
|
|
('prescription_enabled', '=', True),
|
|
], required=True, readonly=True)
|
|
type = fields.Selection([
|
|
('feed', 'Feed'),
|
|
('medical', 'Medical'),
|
|
], 'Type', required=True, readonly=True)
|
|
product = fields.Many2One('product.product', 'Product', domain=[
|
|
('prescription_required', '=', True),
|
|
], required=True,
|
|
help='The product for which this recipe is made. This can be a drug '
|
|
'or a feed to which are added the additives or medications defined in '
|
|
'the lines of this recipe.')
|
|
unit = fields.Function(fields.Many2One('product.uom', 'Unit'),
|
|
'on_change_with_unit')
|
|
unit_digits = fields.Function(fields.Integer('Unit Digits'),
|
|
'on_change_with_unit_digits')
|
|
quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)),
|
|
required=True, depends=['unit_digits'])
|
|
drug_quantity = fields.Function(fields.Float('Quantity',
|
|
digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']),
|
|
'get_drug_quantity')
|
|
afection = fields.Char('Afection')
|
|
dosage = fields.Char('Dosage')
|
|
waiting_period = fields.Integer('Waiting Period', states={
|
|
'readonly': Eval('n_lines', 0) > 1,
|
|
}, depends=['n_lines'],
|
|
help='The number of days that must pass since the produced feed is '
|
|
'given to animals and they are slaughtered.')
|
|
expiry_period = fields.Integer('Expiry Period')
|
|
n_lines = fields.Function(fields.Integer('Num. of lines'),
|
|
'on_change_with_n_lines')
|
|
note = fields.Text('Note')
|
|
|
|
@staticmethod
|
|
def default_specie():
|
|
pool = Pool()
|
|
Specie = pool.get('farm.specie')
|
|
specie = Transaction().context.get('specie')
|
|
if not specie:
|
|
species = Specie.search([], limit=2)
|
|
if len(species) == 1:
|
|
specie, = species
|
|
return specie
|
|
|
|
@staticmethod
|
|
def default_type():
|
|
return Transaction().context.get('type', 'feed')
|
|
|
|
@staticmethod
|
|
def default_n_lines():
|
|
return 0
|
|
|
|
@fields.depends('product')
|
|
def on_change_with_unit(self, name=None):
|
|
if self.product:
|
|
return self.product.default_uom.id
|
|
return None
|
|
|
|
@fields.depends('product')
|
|
def on_change_with_unit_digits(self, name=None):
|
|
if self.product:
|
|
return self.product.default_uom.digits
|
|
return 2
|
|
|
|
@fields.depends('lines', 'waiting_period')
|
|
def on_change_with_waiting_period(self):
|
|
if self.lines and len(self.lines) > 1:
|
|
return 28
|
|
return self.waiting_period
|
|
|
|
@fields.depends('lines')
|
|
def on_change_with_n_lines(self, name=None):
|
|
return len(self.lines) if self.lines else 0
|
|
|
|
def get_drug_quantity(self, name):
|
|
Uom = Pool().get('product.uom')
|
|
quantity = 0
|
|
for line in self.lines:
|
|
quantity += Uom.compute_qty(line.unit, line.quantity, self.unit)
|
|
return quantity
|
|
|
|
def get_factor_change_quantity_unit(self, new_quantity, new_uom):
|
|
Uom = Pool().get('product.uom')
|
|
if not new_quantity:
|
|
new_quantity = 0
|
|
if new_uom != self.unit:
|
|
new_quantity = Uom.compute_qty(new_uom, new_quantity,
|
|
self.unit)
|
|
if new_quantity != self.quantity:
|
|
# quantity have chaned
|
|
return new_quantity / self.quantity
|
|
return None
|
|
|
|
|
|
class PrescriptionLineMixin:
|
|
'''
|
|
Mixin class with the shared fields and methods by Prescription Line and
|
|
Template Line
|
|
'''
|
|
__slots__ = ()
|
|
product = fields.Many2One('product.product', 'Product', domain=[
|
|
('prescription_required', '=', True),
|
|
], required=True)
|
|
product_uom_category = fields.Function(
|
|
fields.Many2One('product.uom.category', 'Product Uom Category'),
|
|
'on_change_with_product_uom_category')
|
|
unit = fields.Many2One('product.uom', 'Unit', required=True,
|
|
domain=[
|
|
If(Bool(Eval('product_uom_category')),
|
|
('category', '=', Eval('product_uom_category')),
|
|
()),
|
|
],
|
|
depends=['product_uom_category'])
|
|
unit_digits = fields.Function(fields.Integer('Unit Digits'),
|
|
'on_change_with_unit_digits')
|
|
quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)),
|
|
depends=['unit_digits'], required=True)
|
|
|
|
@fields.depends('product', 'unit', 'unit_digits')
|
|
def on_change_product(self):
|
|
if self.product:
|
|
category = self.product.default_uom.category
|
|
if not self.unit or self.unit not in category.uoms:
|
|
self.unit = self.product.default_uom
|
|
self.unit_digits = self.product.default_uom.digits
|
|
|
|
@fields.depends('product')
|
|
def on_change_with_product_uom_category(self, name=None):
|
|
if self.product:
|
|
return self.product.default_uom_category.id
|
|
|
|
@fields.depends('unit')
|
|
def on_change_with_unit_digits(self, name=None):
|
|
if self.unit:
|
|
return self.unit.digits
|
|
return 2
|
|
|
|
|
|
class Template(ModelSQL, ModelView, PrescriptionMixin):
|
|
'''Prescription Template'''
|
|
__name__ = 'farm.prescription.template'
|
|
name = fields.Char('Name', required=True)
|
|
lines = fields.One2Many('farm.prescription.template.line', 'prescription',
|
|
'Lines',
|
|
states={
|
|
'invisible': Eval('type') == 'medical',
|
|
},
|
|
depends=['type'])
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
cursor = Transaction().connection.cursor()
|
|
table = backend.TableHandler(cls, module_name)
|
|
sql_table = cls.__table__()
|
|
product = Table('product_product')
|
|
template = Table('product_template')
|
|
|
|
# Upgrade from 3.4: copy name from product rec_name
|
|
upgrade_name = False
|
|
if not table.column_exist('name'):
|
|
upgrade_name = True
|
|
|
|
super(Template, cls).__register__(module_name)
|
|
|
|
if upgrade_name:
|
|
# copy product name to prescription name
|
|
query = sql_table.join(product,
|
|
condition=(product.id == sql_table.product)).join(template,
|
|
condition=(product.template == template.id)).select(
|
|
product.id, template.name)
|
|
cursor.execute(*query)
|
|
for id_, name in cursor.fetchall():
|
|
cursor.execute(*sql_table.update(
|
|
columns=[sql_table.name],
|
|
values=[name],
|
|
where=sql_table.product == id_))
|
|
|
|
@fields.depends('product', 'name')
|
|
def on_change_product(self):
|
|
if self.product and not self.name:
|
|
self.name = self.product.rec_name
|
|
|
|
@classmethod
|
|
def write(cls, *args):
|
|
pool = Pool()
|
|
Prescription = pool.get('farm.prescription')
|
|
Product = pool.get('product.product')
|
|
|
|
actions = iter(args)
|
|
for templates, values in zip(actions, actions):
|
|
if values.get('product'):
|
|
for template in templates:
|
|
n_template_products = Product.search_count([
|
|
('prescription_template', '=', template.id),
|
|
])
|
|
n_template_prescriptions = Prescription.search_count([
|
|
('template', '=', template.id),
|
|
])
|
|
if n_template_products or n_template_prescriptions:
|
|
raise UserError(gettext('farm.prescription.msg_'
|
|
'template_related_to_product_or_prescription',
|
|
template=template.rec_name))
|
|
super(Template, cls).write(*args)
|
|
|
|
|
|
class TemplateLine(ModelSQL, ModelView, PrescriptionLineMixin):
|
|
'Prescription Template Line'
|
|
__name__ = 'farm.prescription.template.line'
|
|
|
|
prescription = fields.Many2One('farm.prescription.template',
|
|
'Prescription', required=True, ondelete='CASCADE')
|
|
|
|
|
|
class Prescription(Workflow, ModelSQL, ModelView, PrescriptionMixin):
|
|
'Prescription'
|
|
__name__ = 'farm.prescription'
|
|
_rec_name = 'reference'
|
|
|
|
template = fields.Many2One('farm.prescription.template', 'Template',
|
|
domain=[
|
|
('specie', '=', Eval('specie')),
|
|
],
|
|
states=_STATES, depends=_DEPENDS + ['specie', 'product'])
|
|
reference = fields.Char('Reference', states=_STATES_REQUIRED,
|
|
depends=_DEPENDS,
|
|
help='If there is a real prescription; put its reference here. '
|
|
'Otherwise, leave it blank and it will be computed automatically with '
|
|
'the configured sequence.')
|
|
date = fields.Date('Date', required=True, states=_STATES, depends=_DEPENDS)
|
|
farm = fields.Many2One('stock.location', 'Farm', required=True,
|
|
states=_STATES, depends=_DEPENDS, domain=[
|
|
('type', '=', 'warehouse'),
|
|
('id', 'in', Eval('context', {}).get('farms', [])),
|
|
],
|
|
context={
|
|
'restrict_by_specie_animal_type': True,
|
|
})
|
|
delivery_date = fields.Date('Delivery date', required=True, domain=[
|
|
['OR',
|
|
('delivery_date', '=', None),
|
|
('delivery_date', '>=', Eval('date', Date()))],
|
|
],
|
|
states=_STATES, depends=_DEPENDS+['date'])
|
|
veterinarian = fields.Many2One('party.party', 'Veterinarian', domain=[
|
|
('veterinarian', '=', True),
|
|
],
|
|
states=_STATES_REQUIRED, depends=_DEPENDS)
|
|
lot = fields.Many2One('stock.lot', 'Lot', domain=[
|
|
('product', '=', Eval('product')),
|
|
],
|
|
states={
|
|
'required': Eval('state') == 'done',
|
|
'readonly': Eval('state') == 'done',
|
|
}, depends=_DEPENDS + ['product'])
|
|
number_of_animals = fields.Integer('Number of animals',
|
|
states={
|
|
'readonly': Eval('state') != 'draft',
|
|
'required': ((Eval('state') != 'draft')
|
|
& ~Bool(Eval('animals'))
|
|
& ~Bool(Eval('animal_groups'))),
|
|
},
|
|
depends=['state', 'animals', 'animal_groups'])
|
|
animals = fields.Many2Many('farm.prescription-animal', 'prescription',
|
|
'animal', 'Animals', domain=[
|
|
('specie', '=', Eval('specie')),
|
|
If(Equal(Eval('state'), 'draft'),
|
|
('farm', '=', Eval('farm')),
|
|
()),
|
|
],
|
|
states={
|
|
'readonly': Eval('state') != 'draft',
|
|
'required': ((Eval('state') != 'draft')
|
|
& ~Bool(Eval('number_of_animals'))
|
|
& ~Bool(Eval('animal_groups'))),
|
|
},
|
|
depends=['specie', 'farm', 'state', 'number_of_animals',
|
|
'animal_groups'])
|
|
animal_groups = fields.Many2Many('farm.prescription-animal.group',
|
|
'prescription', 'group', 'Animal Groups', domain=[
|
|
('specie', '=', Eval('specie')),
|
|
If(Equal(Eval('state'), 'draft'),
|
|
('farms', 'in', [Eval('farm')]),
|
|
()),
|
|
],
|
|
states={
|
|
'readonly': Eval('state') != 'draft',
|
|
'required': ((Eval('state') != 'draft')
|
|
& ~Bool(Eval('number_of_animals'))
|
|
& ~Bool(Eval('animals'))),
|
|
},
|
|
depends=['specie', 'farm', 'state', 'number_of_animals',
|
|
'animals'])
|
|
animal_lots = fields.Function(fields.Many2Many('stock.lot', None, None,
|
|
'Animals Lots'), 'get_animal_lots')
|
|
animals_description = fields.Function(fields.Char('Animals Description'),
|
|
'get_animals_description')
|
|
specie_description = fields.Function(fields.Char('Specie Description'),
|
|
'get_specie_description')
|
|
lines = fields.One2Many('farm.prescription.line', 'prescription', 'Lines',
|
|
states={
|
|
'invisible': Eval('type') == 'medical',
|
|
'readonly': Eval('state') != 'draft',
|
|
'required': ((Eval('type') != 'medical')
|
|
& (Eval('state') != 'draft')),
|
|
},
|
|
depends=['type', 'state'])
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('confirmed', 'Confirmed'),
|
|
('done', 'Done'),
|
|
], 'State', readonly=True, required=True)
|
|
origin = fields.Reference('Origin', selection='get_origin',
|
|
states=_STATES, depends=_DEPENDS)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
pool = Pool()
|
|
Lot = pool.get('stock.lot')
|
|
|
|
super(Prescription, cls).__setup__()
|
|
for fname in ('product', 'quantity', 'dosage', 'expiry_period'):
|
|
field = getattr(cls, fname)
|
|
field.states = _STATES
|
|
field.depends.update(set(_DEPENDS))
|
|
cls.afection.states = _STATES_REQUIRED
|
|
cls.afection.depends = _DEPENDS
|
|
cls.waiting_period.states = {
|
|
'required': Eval('state') != 'draft',
|
|
'readonly': Or(Eval('n_lines', 0) > 1, Eval('state') != 'draft'),
|
|
}
|
|
cls.waiting_period.depends = ['n_lines', 'state']
|
|
cls.product.states = {
|
|
'readonly': Bool(Eval('template', False)),
|
|
}
|
|
cls.product.depends = ['template']
|
|
|
|
if hasattr(Lot, 'expiration_date'):
|
|
cls.lot.domain.append(
|
|
If(Eval('state') != 'done',
|
|
('expiration_date', '>=', Eval('date', Date())),
|
|
()))
|
|
# TODO: use next context when issue4879 whas resolved
|
|
# cls.lot.context['stock_move_date'] = max(
|
|
# Eval('context', {}).get('stock_move_date', Date()),
|
|
# Eval('delivery_date', Date()))
|
|
cls.lot.context['stock_move_date'] = Eval('delivery_date', Date())
|
|
cls.lot.depends |= {'delivery_date', 'date'}
|
|
|
|
cls._transitions |= set((
|
|
('draft', 'confirmed'),
|
|
('confirmed', 'done'),
|
|
))
|
|
cls._buttons.update({
|
|
'set_template': {
|
|
'invisible': Eval('state') != 'draft',
|
|
'readonly': ~Bool(Eval('template', False)),
|
|
'icon': 'tryton-add',
|
|
},
|
|
'confirm': {
|
|
'invisible': Eval('state') != 'draft',
|
|
'icon': 'tryton-forward',
|
|
},
|
|
'done': {
|
|
'invisible': Eval('state') != 'confirmed',
|
|
'icon': 'tryton-forward'
|
|
},
|
|
})
|
|
|
|
@staticmethod
|
|
def default_date():
|
|
return datetime.date.today()
|
|
|
|
@staticmethod
|
|
def default_state():
|
|
return 'draft'
|
|
|
|
def get_rec_name(self, name):
|
|
return '%s - %s (%s)' % (self.reference,
|
|
self.product.rec_name, str(self.date))
|
|
|
|
@fields.depends('template')
|
|
def on_change_template(self):
|
|
if self.template:
|
|
self.product = self.template.product
|
|
|
|
@fields.depends('template')
|
|
def on_change_with_unit(self, name=None):
|
|
return super().on_change_with_unit(name)
|
|
|
|
@fields.depends('animals', 'animal_groups')
|
|
def on_change_with_number_of_animals(self):
|
|
animals = len(self.animals or [])
|
|
for group in (self.animal_groups or []):
|
|
animals += group.quantity
|
|
return animals
|
|
|
|
@fields.depends('product')
|
|
def on_change_with_template(self):
|
|
return (self.product.prescription_template.id
|
|
if (self.product and self.product.prescription_template)
|
|
else None)
|
|
|
|
def get_animal_lots(self, name):
|
|
return [a.lot.id for a in self.animals + self.animal_groups]
|
|
|
|
def get_animals_description(self, name):
|
|
animals = [a.rec_name for a in self.animals]
|
|
groups = ['%sx%s' % (g.quantity, g.rec_name)
|
|
for g in self.animal_groups]
|
|
return ','.join(chain(animals, groups))
|
|
|
|
def get_specie_description(self, name):
|
|
return self.specie.rec_name
|
|
|
|
@classmethod
|
|
def _get_origin(cls):
|
|
'Return list of Model names for origin Reference'
|
|
return []
|
|
|
|
@classmethod
|
|
def get_origin(cls):
|
|
pool = Pool()
|
|
Model = pool.get('ir.model')
|
|
models = cls._get_origin()
|
|
models = Model.search([
|
|
('model', 'in', models),
|
|
])
|
|
return [('', '')] + [(m.model, m.name) for m in models]
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('confirmed')
|
|
def confirm(cls, prescriptions):
|
|
for prescription in prescriptions:
|
|
if not prescription.veterinarian:
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_veterinarian_required_confirmed',
|
|
prescription=prescription.rec_name))
|
|
if prescription.type != 'medical' and not prescription.lines:
|
|
raise UserError(gettext(
|
|
'farm_prescription.msg_lines_required_confirmed',
|
|
prescription=prescription.rec_name))
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('done')
|
|
def done(cls, prescriptions):
|
|
pool = Pool()
|
|
Lot = pool.get('stock.lot')
|
|
|
|
stock_move_date = Transaction().context.get('stock_move_date')
|
|
for prescription in prescriptions:
|
|
if not prescription.lot:
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_lot_required_done',
|
|
prescription=prescription.rec_name))
|
|
if hasattr(prescription.lot, 'expiration_date'):
|
|
prescription_date = prescription.delivery_date
|
|
if stock_move_date:
|
|
prescription_date = max(prescription_date, stock_move_date)
|
|
with Transaction().set_context(
|
|
stock_move_date=prescription_date):
|
|
if Lot(prescription.lot.id).expired:
|
|
raise UserError(gettext('msg_lot_expired',
|
|
lot=prescription.lot.rec_name,
|
|
prescription=prescription.rec_name,
|
|
))
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
def set_template(cls, prescriptions):
|
|
Warning = Pool().get('res.user.warning')
|
|
|
|
for prescription in prescriptions:
|
|
if not prescription.template:
|
|
continue
|
|
|
|
key = 'task_msg_lines_will_be_replaced.%d' % prescription.id
|
|
|
|
if Warning.check(key):
|
|
raise UserWarning(key, gettext('farm_prescription.msg_lines_will_be_replaced',
|
|
prescription=prescription.rec_name))
|
|
prescription.set_template_vals()
|
|
prescription.save()
|
|
|
|
def set_template_vals(self):
|
|
pool = Pool()
|
|
PrescriptionLine = pool.get('farm.prescription.line')
|
|
|
|
if not self.template:
|
|
return
|
|
|
|
if self.lines:
|
|
PrescriptionLine.delete(self.lines)
|
|
|
|
self.afection = self.template.afection
|
|
self.dosage = self.template.dosage
|
|
self.waiting_period = self.template.waiting_period
|
|
self.expiry_period = self.template.expiry_period
|
|
|
|
rate = self.get_factor_change_quantity_unit(self.template.quantity,
|
|
self.template.unit)
|
|
lines = []
|
|
for line_template in self.template.lines:
|
|
line = PrescriptionLine()
|
|
line.set_template_line_vals(line_template, rate)
|
|
lines.append(line)
|
|
self.lines = lines
|
|
self.note = self.template.note
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
pool = Pool()
|
|
Specie = pool.get('farm.specie')
|
|
|
|
context_specie_id = Transaction().context.get('specie')
|
|
default_sequence = (context_specie_id
|
|
and Specie(context_specie_id).prescription_sequence)
|
|
for value in vlist:
|
|
if value.get('reference'):
|
|
continue
|
|
sequence = default_sequence
|
|
if value.get('specie'):
|
|
sequence = Specie(value['specie']).prescription_sequence
|
|
value['reference'] = sequence.get()
|
|
return super(Prescription, cls).create(vlist)
|
|
|
|
@classmethod
|
|
def copy(cls, prescriptions, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
|
|
default['reference'] = None
|
|
default['lot'] = None
|
|
default['state'] = 'draft'
|
|
return super(Prescription, cls).copy(prescriptions, default=default)
|
|
|
|
|
|
class PrescriptionAnimal(ModelSQL):
|
|
'Prescription Animal Relation'
|
|
__name__ = 'farm.prescription-animal'
|
|
|
|
prescription = fields.Many2One('farm.prescription', 'Prescription',
|
|
required=True, ondelete='CASCADE')
|
|
animal = fields.Many2One('farm.animal', 'Animal',
|
|
required=True, ondelete='CASCADE')
|
|
|
|
|
|
class PrescriptionAnimalGroup(ModelSQL):
|
|
'Prescription Animal Group Relation'
|
|
__name__ = 'farm.prescription-animal.group'
|
|
|
|
prescription = fields.Many2One('farm.prescription', 'Prescription',
|
|
required=True, ondelete='CASCADE')
|
|
group = fields.Many2One('farm.animal.group', 'Animal Group',
|
|
required=True, ondelete='CASCADE')
|
|
|
|
|
|
class PrescriptionLine(ModelSQL, ModelView, PrescriptionLineMixin):
|
|
'Prescription Line'
|
|
__name__ = 'farm.prescription.line'
|
|
|
|
prescription = fields.Many2One('farm.prescription', 'Prescription',
|
|
required=True, ondelete='CASCADE')
|
|
|
|
def compute_quantity(self, factor):
|
|
return self.unit.round(self.quantity * factor)
|
|
|
|
def set_template_line_vals(self, template, rate):
|
|
self.product = template.product
|
|
self.unit = template.unit
|
|
quantity = (template.quantity / rate) if rate else template.quantity
|
|
self.quantity = self.unit.round(quantity)
|
|
|
|
|
|
class Move(metaclass=PoolMeta):
|
|
__name__ = 'stock.move'
|
|
prescription = fields.Many2One('farm.prescription', 'Prescription')
|
|
|
|
@classmethod
|
|
def _get_origin(cls):
|
|
models = super(Move, cls)._get_origin()
|
|
models.append('farm.prescription.line')
|
|
return models
|
|
|
|
@classmethod
|
|
def validate(cls, moves):
|
|
super(Move, cls).validate(moves)
|
|
for move in moves:
|
|
move.check_prescription_and_origin()
|
|
|
|
def check_prescription_and_origin(self):
|
|
"""
|
|
If the move is related to a prescription, it is a move of a
|
|
prescription line (and the lines is its origin) or it a move of the
|
|
prescription's product.
|
|
"""
|
|
pool = Pool()
|
|
PrescriptionLine = pool.get('farm.prescription.line')
|
|
Uom = pool.get('product.uom')
|
|
|
|
if self.origin and isinstance(self.origin, PrescriptionLine):
|
|
if (not self.prescription or
|
|
self.origin.prescription != self.prescription):
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_from_prescription_line_invalid_prescription',
|
|
move=self.rec_name,
|
|
origin=self.origin.rec_name,
|
|
))
|
|
if (self.product != self.origin.product or
|
|
self.quantity != Uom.compute_qty(self.origin.unit,
|
|
self.origin.quantity, self.unit)):
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_from_prescription_line_invalid_product_quantity',
|
|
move=self.rec_name,
|
|
origin=self.origin.rec_name,
|
|
))
|
|
elif self.prescription:
|
|
quantity = Uom.compute_qty(self.prescription.unit,
|
|
self.prescription.quantity + self.prescription.drug_quantity,
|
|
self.unit)
|
|
if (self.product != self.prescription.product or
|
|
self.quantity != quantity):
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_prescription_invalid_product_quantity',
|
|
move=self.rec_name,
|
|
prescription=self.prescription.rec_name,
|
|
))
|
|
|
|
@classmethod
|
|
def assign(cls, moves):
|
|
for move in moves:
|
|
move.check_prescription_required()
|
|
super(Move, cls).assign(moves)
|
|
|
|
@classmethod
|
|
def do(cls, moves):
|
|
for move in moves:
|
|
move.check_prescription_required()
|
|
super(Move, cls).do(moves)
|
|
|
|
def check_prescription_required(self):
|
|
pool = Pool()
|
|
FeedEvent = pool.get('farm.feed.event')
|
|
ShipmentIn = pool.get('stock.shipment.in')
|
|
|
|
if self.to_location.prescription_required and (
|
|
not self.prescription and self.product.prescription_required
|
|
and not isinstance(self.shipment, ShipmentIn)
|
|
and not isinstance(self.origin, FeedEvent)):
|
|
# Purchases don't require prescription because are made to stock
|
|
raise UserError(gettext('farm_prescription.msg_need_prescription',
|
|
move=self.rec_name))
|
|
if self.prescription:
|
|
if self.prescription.state == 'draft':
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_unconfirmed_prescription',
|
|
prescription=self.prescription.rec_name,
|
|
move=self.rec_name))
|
|
|
|
|
|
class Location(metaclass=PoolMeta):
|
|
__name__ = 'stock.location'
|
|
|
|
prescription_required = fields.Boolean('Prescription required')
|
|
|
|
@staticmethod
|
|
def default_prescription_required():
|
|
return True
|
|
|
|
|
|
class CreateInternalShipmentStart(ModelView):
|
|
'Create Internal Shipment Start'
|
|
__name__ = "farm.prescription.internal.shipment.start"
|
|
|
|
farm = fields.Many2One('stock.location', 'Farm',
|
|
states={
|
|
'invisible': True,
|
|
},
|
|
domain=[
|
|
('type', '=', 'warehouse'),
|
|
])
|
|
from_location = fields.Many2One('stock.location', 'From Location',
|
|
domain=[
|
|
('type', '=', 'storage'),
|
|
('silo', '=', False),
|
|
], depends=['farm'], required=True)
|
|
|
|
|
|
class CreateInternalShipment(Wizard):
|
|
'Create Integer Shipment'
|
|
__name__ = "farm.prescription.internal.shipment"
|
|
|
|
start = StateView('farm.prescription.internal.shipment.start',
|
|
'farm_prescription.create_internal_shipment_start_view', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Create', 'create_', 'tryton-ok', default=True),
|
|
])
|
|
create_ = StateAction('stock.act_shipment_internal_form')
|
|
|
|
def default_start(self, fields):
|
|
pool = Pool()
|
|
Move = pool.get('stock.move')
|
|
|
|
prescriptions = Prescription.browse(
|
|
Transaction().context['active_ids'])
|
|
|
|
for prescription in prescriptions:
|
|
farm = prescription.farm
|
|
move = Move.search([
|
|
('prescription', '=', prescription)])
|
|
if move:
|
|
raise UserError(gettext('farm_prescription.'
|
|
'msg_prescription_used',
|
|
prescription=prescription.reference,
|
|
shipment=(move[0].shipment and move[0].shipment.code
|
|
or move[0].id)))
|
|
return {
|
|
'from_location': None,
|
|
'farm': farm.id,
|
|
}
|
|
|
|
def do_create_(self, action):
|
|
pool = Pool()
|
|
Prescription = Pool().get('farm.prescription')
|
|
Shipment = pool.get('stock.shipment.internal')
|
|
Company = pool.get('company.company')
|
|
|
|
prescriptions = Prescription.browse(
|
|
Transaction().context['active_ids'])
|
|
|
|
company = Company.search([
|
|
('id', '=', Transaction().context.get('company'))])
|
|
company = company and company[0]
|
|
shipments_vals = []
|
|
for prescription in prescriptions:
|
|
shipment_vals = {}
|
|
shipment_vals['from_location'] = self.start.from_location.id
|
|
shipment_vals['to_location'] = (
|
|
prescription.farm.storage_location.id)
|
|
shipment_vals['company'] = company.id
|
|
|
|
move = {}
|
|
move['product'] = prescription.product.id
|
|
move['company'] = company.id
|
|
move['lot'] = prescription.lot and prescription.lot.id or None
|
|
move['quantity']= prescription.quantity
|
|
move['unit_price'] = prescription.product.list_price
|
|
move['currency'] =company.currency.id
|
|
move['unit'] = prescription.unit.id
|
|
move['from_location'] = shipment_vals['from_location']
|
|
move['to_location'] = shipment_vals['to_location']
|
|
move['prescription'] = prescription.id
|
|
shipment_vals['moves'] = [['create', [move]]]
|
|
|
|
shipments_vals.append(shipment_vals)
|
|
|
|
shipments = Shipment.create(shipments_vals)
|
|
|
|
data = {}
|
|
data['res_id'] = [s.id for s in shipments]
|
|
return action, data
|