123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- # -*- coding: utf-8 -*-
- # @authors: Alexander Ezquevo <alexander@acysos.com>
- # Copyright (C) 2015 Acysos S.L.
- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
- from openerp import models, fields, api, _
- from openerp.exceptions import Warning
- from datetime import datetime
- ANIMAL_ORIGIN = [('purchased', 'Purchased'),
- ('raised', 'Raised'), ]
- ANIMAL_TYPE = [('male', 'Male'), ('female', 'Female'),
- ('individual', 'Individual'), ]
- DFORMAT = "%Y-%m-%d %H:%M:%S"
- class Tag(models.Model):
- _name = 'farm.tags'
- _order = 'name DESC'
- name = fields.Char(string='Name', required=True)
- animals = fields.Many2many(
- comodel_name='farm.animal', inverse_name='tags',
- string='Animals')
- animal_group = fields.Many2many(
- comodel_name='farm.animal.group', inverse_name='tags',
- string='Groups')
- class Animal(models.Model):
- _name = 'farm.animal'
- _rec_name = 'number'
- type = fields.Selection(selection=ANIMAL_TYPE, string='Type',
- default='individual')
- specie = fields.Many2one(comodel_name='farm.specie',
- string='Specie', required=True, select=True)
- breed = fields.Many2one(comodel_name='farm.specie.breed', string='Breed')
- tags = fields.Many2many(
- comodel_name='farm.tags', inverse_name='animals',
- string='Tags')
- lot = fields.One2many(comodel_name='stock.lot_farm.animal',
- inverse_name='animal', colum1='lot',
- string='lot')
- number = fields.Char(string='Number',
- related='lot.lot.name')
- location = fields.Many2one(
- comodel_name='stock.location', string='Current Location',
- domain=[
- ('silo', '=', False),
- ], help='Indicates where the animal currently resides.')
- farm = fields.Many2one(comodel_name='stock.location',
- domain=[('usage', '=', 'view'), ],
- string='Current Farm')
- origin = fields.Selection(selection=ANIMAL_ORIGIN, string='Origin',
- required=True)
- arrival_date = fields.Date(string='Arrival Date',
- default=fields.Date.today(),
- help="The date this animal arrived (if it"
- "was purchased) or when it was born.")
- initial_location = fields.Many2one(
- comodel_name='stock.location', string='Initial Location',
- required=True,
- domain=[('usage', '=', 'internal'), ('silo', '=', False), ],
- help="The Location where the animal was reached or where it was "
- "allocated when it was purchased.\nIt is used as historical "
- "information and to get Serial Number.")
- birthdate = fields.Date(string='Birthdate', default=fields.Date.today())
- removal_date = fields.Date(
- string='Removal Date', readonly=True,
- defaulft=fields.Date.today(),
- help='Get information from the corresponding removal event.')
- removal_reason = fields.Many2one(comodel_name='farm.removal.reason',
- readonly=True)
- weight = fields.One2many(comodel_name='farm.animal.weight',
- inverse_name='animal', column1='tag', string='Weigth records',)
- current_weight = fields.Many2one(comodel_name='farm.animal.weight',
- string='Current Weight',
- compute='get_current_weight')
- notes = fields.Text(string='Notes')
- active = fields.Boolean(string='Active', default=True)
- consumed_feed = fields.Float(string='Consumed Feed (kg)')
- sex = fields.Selection([('male', "Male"),
- ('female', "Female"),
- ('undetermined', "Undetermined"),
- ], string='Sex', required=True)
- purpose = fields.Selection([('sale', 'Sale'),
- ('replacement', 'Replacement'),
- ('unknown', 'Unknown'),
- ], string='Purpose', default='unknown')
- account = fields.Many2one(comodel_name='account.analytic.account',
- string='Analytic Account')
- @api.multi
- def create_first_move(self, res):
- moves_obj = self.env['stock.move']
- quant_obj = self.env['stock.quant']
- for record in res:
- quant = quant_obj.search([(
- 'lot_id', '=', record.lot.lot.id)])
- if record.origin == 'raised':
- if not quant:
- raise_location = self.env['stock.location'].search(
- [('usage', '=', 'production')])
- product_tmpt = record.lot.lot.product_id.product_tmpl_id
- new_move = moves_obj.create({
- 'origin': res.origin,
- 'name': 'raise-' + record.lot.lot.name,
- 'create_date': fields.Date.today(),
- 'date': record.arrival_date,
- 'product_id': record.lot.lot.product_id.id,
- 'product_uom_qty': 1,
- 'product_uom': product_tmpt.uom_id.id,
- 'location_id': raise_location.id,
- 'location_dest_id': record.initial_location.id,
- 'company_id': record.farm.company_id.id,
- })
- new_move.action_done()
- new_move.quant_ids.lot_id = record.lot.lot.id
- else:
- raise Warning(
- _('this lot is in use, please create new lot'))
- elif len(self.lot) > 0:
- if not quant:
- raise Warning(
- _('no product in farms for this lot'))
- elif quant.location_id != record.initial_location:
- raise Warning(
- _('animal location and product location are diferent'))
- @api.model
- @api.returns('self', lambda value: value.id)
- def create(self, vals):
- res = super(Animal, self).create(vals)
- self.create_first_move(res)
- res.location = res.initial_location
- analy_ac_obj = self.env['account.analytic.account']
- top_account = analy_ac_obj.search([
- ('name', '=', res.farm.name)])
- if not top_account:
- gen_account = analy_ac_obj.search([
- ('name', '=', 'General Account')])
- if not gen_account:
- gen_account = analy_ac_obj.create({'name': 'General Account'})
- top_account = analy_ac_obj.create({'name': res.farm.name,
- 'parent_id': gen_account.id})
- new_account = analy_ac_obj.create({
- 'name': 'AA-'+res.type+'-'+res.number,
- 'parent_id': top_account.id})
- res.account = new_account
- if res.type == 'female':
- nom_eti = '-unmated'
- else:
- nom_eti = '-fathers'
- tags_obj = self.env['farm.tags']
- new_tag = tags_obj.search([
- ('name', '=', res.farm.name + nom_eti)])
- if len(new_tag) == 0:
- new_tag = tags_obj.create({'name': res.farm.name + nom_eti, })
- res.tags = [(6, 0, [new_tag.id, ])]
- return res
- @api.one
- def get_current_weight(self):
- if self.weight:
- self.current_weight = self.weight[0].id
- @api.multi
- def name_get(self):
- result = ''
- displayName = []
- for animal in self:
- if animal.tags:
- result = ''
- for tag in animal.tags:
- result = result + tag.name + '/'
- displayName.append(
- (animal.id, animal.number + '-' + result))
- else:
- displayName.append((animal.id, animal.number))
- return displayName
- class AnimalWeight(models.Model):
- _name = 'farm.animal.weight'
- _order = 'timestamp DESC'
- _rec_name = 'weight'
- animal = fields.Many2one(comodel_name='farm.animal', string='Animal',
- required=True, ondelete='CASCADE')
- timestamp = fields.Datetime(string='Date & Time', required=True,
- defaulft=fields.Date.today())
- uom = fields.Many2one(comodel_name='product.uom', string='Uom',
- requiered=True, ondelete='CASCADE')
- weight = fields.Float(string='Weight', digits=(16, 2))
- class Male(models.Model):
- _inherit = 'farm.animal'
- extractions = fields.One2many(comodel_name='farm.semen_extraction.event',
- inverse_name='animal',
- string='Semen Extractions')
- last_extraction = fields.Date(string='Last Extraction', readonly=True,
- compute='get_last_extraction')
- @api.one
- def get_last_extraction(self):
- if not self.extractions:
- return False
- extraction = self.extractions.search(
- [('animal', '=', self.id)],
- order='timestamp DESC')[0]
- self.last_extraction = extraction.timestamp
- class Female(models.Model):
- _inherit = 'farm.animal'
- cycles = fields.One2many(comodel_name='farm.animal.female_cycle',
- inverse_name='animal', string='Cycles)')
- current_cycle = fields.Many2one(comodel_name='farm.animal.female_cycle',
- string='Current Cycle', readonly=True,
- compute='get_current_cycle')
- state = fields.Selection(selection=[('initial', ''),
- ('prospective', 'Prospective'),
- ('unmated', 'Unmated'),
- ('mated', 'Mated'),
- ('removed', 'Removed'), ],
- readonly=True, default='prospective',
- help='According to NPPC Production and Financial'
- 'Standards there are four status for breeding'
- 'sows The status change is event driven:arrival'
- 'date, entry date mating event and removal event')
- first_mating = fields.Date(string='First Mating',
- compute='get_first_mating')
- days_from_insemination = fields.Integer(
- string='Insemination Days',
- compute='get_days_from_insemination')
- last_produced_group = fields.Many2one(comodel_name='farm.animal.group',
- string='Last produced group',
- compute='get_last_produced_group')
- days_from_farrowing = fields.Integer(string='Unpregnat Days',
- compute='get_days_from_farrowing')
- def is_lactating(self):
- return (self.current_cycle and
- self.current_cycle.state == 'lactating' or False)
- @api.multi
- def get_current_cycle(self):
- for res in self:
- if len(res.cycles) > 0:
- res.current_cycle = res.cycles[-1]
- @api.one
- def update_state(self):
- if self.type != 'female':
- self.state = 'initial'
- else:
- if self.removal_date and self.removal_date <= fields.Date.today():
- state = 'removed'
- elif (not self.cycles or len(self.cycles) == 1 and
- not self.cycles[0].weaning_event and
- self.cycles[0].state == 'unmated'):
- state = 'prospective'
- elif self.current_cycle and self.current_cycle.state == 'unmated':
- state = 'unmated'
- else:
- state = 'mated'
- self.state = state
- @api.one
- def get_first_mating(self):
- InseminationEvent = self.env['farm.insemination.event']
- if self.type != 'female':
- self.first_mating = False
- else:
- first_inseminations = InseminationEvent.search([
- ('animal', '=', self.id),
- ], limit=1, order='timestamp ASC')
- if not first_inseminations:
- self.first_mating = False
- else:
- first_insemination, = first_inseminations
- self.first_mating = first_insemination.timestamp
- @api.one
- def get_days_from_insemination(self):
- InseminationEvent = self.env['farm.insemination.event']
- last_valid_insemination = InseminationEvent.search([
- ('animal', '=', self.id),
- ('state', '=', 'validated'),
- ], order='timestamp DESC', limit=1)
- if not last_valid_insemination:
- self.days_from_insemination = False
- else:
- val_in = datetime.strptime(
- last_valid_insemination[0].timestamp, DFORMAT)
- days_from_insemination = (
- datetime.today() - val_in).days
- self.days_from_insemination = days_from_insemination
- @api.one
- def get_last_produced_group(self):
- FarrowingEvent = self.env['farm.farrowing.event']
- last_farrowing_events = FarrowingEvent.search([
- ('animal', '=', self.id),
- ('state', '=', 'validated'),
- ('produced_group', '!=', None),
- ], order='timestamp DESC', limit=1)
- if last_farrowing_events:
- self.last_produced_group = \
- last_farrowing_events[0].produced_group.id
- else:
- self.last_produced_group = False
- @api.one
- def get_days_from_farrowing(self):
- FarrowingEvent = self.env['farm.farrowing.event']
- last_valid_farrowing = FarrowingEvent.search([
- ('animal', '=', self.id),
- ('state', '=', 'validated'),
- ], order='timestamp DESC', limit=1)
- if not last_valid_farrowing:
- self.days_from_farrowing = False
- else:
- last_val_farrow = datetime.strptime(
- last_valid_farrowing[0].timestamp, DFORMAT)
- days_from_farrowing = (
- datetime.today() - last_val_farrow).days
- self.days_from_farrowing = days_from_farrowing
- class FemaleCycle(models.Model):
- _name = 'farm.animal.female_cycle'
- _order = 'ordination_date ASC'
- _rec_name = 'sequence'
- animal = fields.Many2one(comodel_name='farm.animal', string='Female',
- required=True, domain=['type', '=', 'female'])
- sequence = fields.Integer(string='Nun. cycle')
- ordination_date = fields.Datetime('Date for ordination', requiered=True,
- default=fields.Datetime.now())
- state = fields.Selection(selection=[
- ('mated', 'Mated'), ('pregnat', 'Pregnat'),
- ('lactating', 'Lactating'), ('unmated', 'Unmated')],
- readonly=True, required=True, default='unmated')
- insemination_events = fields.One2many(
- comodel_name='farm.insemination.event',
- inverse_name='female_cycle', string='Inseminations')
- days_between_wearing_and_insemination = fields.Integer(
- string='Unmated Days',
- compute='get_days_between_weaning_and_insemination')
- diagnosis_events = fields.One2many(
- comodel_name='farm.pregnancy_diagnosis.event',
- inverse_name='female_cycle', string='Diagnosis')
- pregnant = fields.Boolean(string='Pregnat',
- compute='on_change_whith_pregnant')
- abort_event = fields.One2many(comodel_name='farm.abort.event',
- inverse_name='female_cycle',
- string='Abort')
- farrowing_event = fields.One2many(
- comodel_name='farm.farrowing.event_female_cycle',
- inverse_name='cycle', column1='female_cycle.event', string='Farrowing',
- readonly=True)
- live = fields.Integer(string='Live', compute='get_live')
- dead = fields.Integer(string='Dead', compute='get_dead')
- foster_events = fields.One2many(comodel_name='farm.foster.event',
- inverse_name='female_cycle',
- string='Fosters')
- fostered = fields.Integer(string='Fostered',
- compute='on_change_with_fostered',
- store=True)
- weaning_event = fields.One2many(
- comodel_name='farm.weaning.event_female_cycle',
- inverse_name='cycle', column1='event', readonly=True)
- weared = fields.Integer(string='Weared', compute='get_weaned')
- removed = fields.Integer(
- string='Removed Quantity',
- help='Number of removed animals from Produced Group. Diference '
- 'between born live and weaned, computing Fostered diference.',
- compute='get_removed')
- days_between_farrowing_weaning = fields.Integer(
- string='Lactating Days',
- help='Number of days between Farrowing and Weaning.',
- compute='get_lactating_days')
- @api.one
- def update_state(self, validated_event):
- '''
- Sorted rules:
- - A cycle will be considered 'unmated'
- if weaning_event_id != False and weaning_event.state == 'validated'
- or if abort_event != False has abort_event.state == 'validated'
- or has not any validated event in insemination_event_ids.
- - A female will be considered 'lactating'
- if farrowing_event_id!=False and farrowing_event.state=='validated'
- - A female will be considered 'pregnant' if there are more than one
- diagnosis in 'validated' state and the last one has a positive result
- - A female will be considered 'mated' if there are any items in
- insemination_event_ids with 'validated' state.
- '''
- def check_event(event_to_check):
- return(type(event_to_check) == type(validated_event) and
- event_to_check == validated_event or
- event_to_check.state == 'validated')
- state = 'unmated'
- if (self.abort_event and check_event(self.abort_event) or
- self.weaning_event and check_event(self.weaning_event.event)):
- state = 'unmated'
- elif self.farrowing_event and check_event(self.farrowing_event.event):
- if self.farrowing_event.event.live > 0:
- state = 'lactating'
- else:
- state = 'unmated'
- elif self.pregnant:
- state = 'pregnat'
- else:
- for insemination_event in self.insemination_events:
- if check_event(insemination_event):
- state = 'mated'
- break
- self.state = state
- self.animal.update_state()
- self.state = state
- @api.model
- @api.returns('self', lambda value: value.id)
- def create(self, vals):
- res = super(FemaleCycle, self).create(vals)
- femaleCycle_obj = self.env['farm.animal.female_cycle']
- cycles = femaleCycle_obj.search([
- ('animal.id', '=', res.animal.id), ])
- secuence = 1
- for cycle in cycles:
- if len(cycle.farrowing_event) != 0:
- secuence += 1
- res.sequence = secuence
- return res
- @api.one
- def get_days_between_weaning_and_insemination(self):
- if not self.insemination_events:
- return False
- previous_cycles = self.search([
- ('animal', '=', self.animal.id),
- ('sequence', '<=', self.sequence),
- ('id', '!=', self.id)
- ],
- order='sequence, ordination_date DESC', limit=1)
- if len(previous_cycles) < 1 or (
- not previous_cycles[0].weaning_event and
- not previous_cycles[0].abort_event):
- self.days_between_wearing_and_insemination = 0
- else:
- previous_date = (
- datetime.strptime(
- previous_cycles[0].weaning_event.event.timestamp, DFORMAT)
- if previous_cycles[0].weaning_event
- else datetime.strptime(
- previous_cycles[0].abort_event.timestamp, DFORMAT))
- insemination_date = \
- datetime.strptime(
- self.insemination_events[0].timestamp, DFORMAT)
- self.days_between_wearing_and_insemination = \
- (insemination_date - previous_date).days
- @api.one
- def get_live(self):
- if self.farrowing_event:
- self.live = self.farrowing_event[-1].cycle.live
- else:
- self.live = False
- @api.one
- def get_dead(self):
- if self.farrowing_event:
- self.live = self.farrowing_event[-1].cycle.dead
- else:
- self.live = False
- @api.one
- def on_change_with_fostered(self):
- self.fostered = sum(e.quantity for e in self.foster_events)
- @api.one
- def get_weaned(self):
- self.weared = self.weaning_event and \
- self.weaning_event.event.quantity or 0
- @api.one
- def get_removed(self):
- self.removed = self.live + self.fostered - self.weared
- @api.one
- def get_lactating_days(self):
- if not self.farrowing_event or not self.weaning_event:
- return None
- w_e = datetime.strptime(
- self.weaning_event[-1].event.timestamp, DFORMAT)
- f_e = datetime.strptime(
- self.farrowing_event[-1].event.timestamp, DFORMAT)
- self.days_between_farrowing_weaning = (w_e-f_e).days
- @api.one
- @api.onchange('abort_event', 'diagnosis_events', 'farrowing_event')
- def on_change_whith_pregnant(self):
- if self.abort_event:
- self.pregnant = False
- elif not self.diagnosis_events:
- self.pregnant = False
- elif self.farrowing_event:
- self.pregnant = False
- else:
- self.pregnant = \
- self.diagnosis_events[-1].result == 'positive'
- @api.one
- @api.onchange('pregnant')
- def on_change_pregnant(self):
- if self.pregnant:
- self.state = 'pregnat'
- def get_last_produced_group(self):
- farrowingEvent_obj = self.env['farm.farrowing.event']
- last_farrowing_events = farrowingEvent_obj.search([
- ('animal', '=', self),
- ('state', '=', 'validated'),
- ('produced_group', '!=', None),
- ],
- order='timestamp DESC', limit=1)
- if last_farrowing_events:
- return last_farrowing_events[0].produced_group.id
- return None
|