Add wizard to create female with history, add consumed_feed field to Animals and Groups and fix context and domain manipulation when create menus
This commit is contained in:
parent
e90569b000
commit
d41796804c
168
animal.py
168
animal.py
|
@ -1,5 +1,5 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from datetime import date, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
@ -53,6 +53,16 @@ class Tag(ModelSQL, ModelView):
|
|||
|
||||
|
||||
class AnimalMixin:
|
||||
feed_unit_digits = fields.Function(fields.Integer('Feed Unit Digits'),
|
||||
'get_feed_unit_digits')
|
||||
|
||||
def get_feed_unit_digits(self, name):
|
||||
Uom = Pool().get('product.uom')
|
||||
weight_base_uom, = Uom.search([
|
||||
('symbol', '=', 'kg'),
|
||||
])
|
||||
return weight_base_uom.id
|
||||
|
||||
@classmethod
|
||||
def _create_and_done_first_stock_move(cls, records):
|
||||
"""
|
||||
|
@ -117,7 +127,7 @@ class Animal(ModelSQL, ModelView, AnimalMixin):
|
|||
('male', 'Male'),
|
||||
('female', 'Female'),
|
||||
('individual', 'Individual'),
|
||||
], 'Type', required=True, readonly=True, select=True)
|
||||
], 'Type', required=True, readonly=True, select=True)
|
||||
specie = fields.Many2One('farm.specie', 'Specie', required=True,
|
||||
readonly=True, select=True)
|
||||
breed = fields.Many2One('farm.specie.breed', 'Breed', required=True,
|
||||
|
@ -173,6 +183,10 @@ class Animal(ModelSQL, ModelView, AnimalMixin):
|
|||
tags = fields.Many2Many('farm.animal-farm.tag', 'animal', 'tag', 'Tags')
|
||||
notes = fields.Text('Notes')
|
||||
active = fields.Boolean('Active')
|
||||
consumed_feed = fields.Function(fields.Numeric('Consumed Feed (Kg)',
|
||||
digits=(16, Eval('feed_unit_digits', 2)),
|
||||
depends=['feed_unit_digits']),
|
||||
'get_consumed_feed')
|
||||
# Individual Fields
|
||||
sex = fields.Selection([
|
||||
('male', "Male"),
|
||||
|
@ -277,6 +291,40 @@ class Animal(ModelSQL, ModelView, AnimalMixin):
|
|||
if self.weights:
|
||||
return self.weights[0].id
|
||||
|
||||
def get_consumed_feed(self, name):
|
||||
pool = Pool()
|
||||
FeedEvent = pool.get('farm.feed.event')
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
now = datetime.now()
|
||||
feed_events = FeedEvent.search([
|
||||
('animal_type', '=', self.type),
|
||||
('animal', '=', self.id),
|
||||
('state', 'in', ['provisional', 'validated']),
|
||||
['OR', [
|
||||
('start_date', '=', None),
|
||||
('timestamp', '<=', now),
|
||||
], [
|
||||
('start_date', '<=', now.date()),
|
||||
]],
|
||||
])
|
||||
|
||||
kg, = Uom.search([
|
||||
('symbol', '=', 'kg'),
|
||||
])
|
||||
consumed_feed = Decimal('0.0')
|
||||
for event in feed_events:
|
||||
# TODO: it uses compute_price() because quantity is a Decimal
|
||||
# quantity in feed_product default uom. The method is not for
|
||||
# this purpose but it works
|
||||
event_feed_quantity = Uom.compute_price(kg, event.feed_quantity,
|
||||
event.uom)
|
||||
if event.timestamp > now:
|
||||
event_feed_quantity /= (event.end_date - event.start_date).days
|
||||
event_feed_quantity *= (now.date() - event.start_date).days
|
||||
consumed_feed += event_feed_quantity
|
||||
return consumed_feed
|
||||
|
||||
def check_in_location(self, location, timestamp):
|
||||
with Transaction().set_context(
|
||||
locations=[location.id],
|
||||
|
@ -284,6 +332,8 @@ class Animal(ModelSQL, ModelView, AnimalMixin):
|
|||
return self.lot.quantity == 1
|
||||
|
||||
def check_allowed_location(self, location, event_rec_name):
|
||||
if not location.warehouse:
|
||||
return
|
||||
for farm_line in self.specie.farm_lines:
|
||||
if farm_line.farm.id == location.warehouse.id:
|
||||
if getattr(farm_line, 'has_%s' % self.type):
|
||||
|
@ -573,7 +623,7 @@ class Female:
|
|||
def get_state(self):
|
||||
if self.type != 'female':
|
||||
return
|
||||
if (self.removal_date and self.removal_date <= date.today()):
|
||||
if self.removal_date and self.removal_date <= date.today():
|
||||
state = 'removed'
|
||||
elif (not self.cycles or len(self.cycles) == 1 and
|
||||
not self.cycles[0].weaning_event and
|
||||
|
@ -906,17 +956,21 @@ class FemaleCycle(ModelSQL, ModelView):
|
|||
previous_cycles = self.search([
|
||||
('animal', '=', self.animal.id),
|
||||
('sequence', '<=', self.sequence),
|
||||
('ordination_date', '<', self.ordination_date)
|
||||
('id', '!=', self.id)
|
||||
],
|
||||
order=[
|
||||
('sequence', 'DESC'),
|
||||
('ordination_date', 'DESC'),
|
||||
], limit=1)
|
||||
if not previous_cycles or not previous_cycles[0].weaning_event:
|
||||
print "pc:", previous_cycles, " sq:", self.sequence
|
||||
if not previous_cycles or (not previous_cycles[0].weaning_event and
|
||||
not previous_cycles[0].abort_event):
|
||||
return None
|
||||
weaning_date = previous_cycles[0].weaning_event.timestamp.date()
|
||||
previous_date = (previous_cycles[0].weaning_event.timestamp.date()
|
||||
if previous_cycles[0].weaning_event
|
||||
else previous_cycles[0].abort_event.timestamp.date())
|
||||
insemination_date = self.insemination_events[0].timestamp.date()
|
||||
return (insemination_date - weaning_date).days
|
||||
return (insemination_date - previous_date).days
|
||||
|
||||
def on_change_with_pregnant(self, name=None):
|
||||
if self.abort_event:
|
||||
|
@ -940,7 +994,7 @@ class FemaleCycle(ModelSQL, ModelView):
|
|||
return self.weaning_event and self.weaning_event.quantity or 0
|
||||
|
||||
def get_removed(self, name):
|
||||
return self.live + self.fostered + self.weaned
|
||||
return self.live + self.fostered - self.weaned
|
||||
|
||||
def get_lactating_days(self, name):
|
||||
if not self.farrowing_event or not self.weaning_event:
|
||||
|
@ -986,8 +1040,7 @@ class CreateFemaleStart(ModelView):
|
|||
('specie', '=', Eval('specie')),
|
||||
],
|
||||
depends=['specie'])
|
||||
cycles = fields.One2Many('farm.create_female.line', 'start', 'Cycles',
|
||||
required=True)
|
||||
cycles = fields.One2Many('farm.create_female.line', 'start', 'Cycles')
|
||||
last_cycle_active = fields.Boolean('Last cycle active',
|
||||
help='If marked the moves for the last cycle will be created.')
|
||||
|
||||
|
@ -1021,44 +1074,41 @@ class CreateFemaleLine(ModelView):
|
|||
second_insemination_date = fields.Date('Second Insemination Date')
|
||||
third_insemination_date = fields.Date('Third Insemination Date')
|
||||
abort = fields.Boolean('Aborted?')
|
||||
farrowing_date = fields.DateTime('Farrowing Date',
|
||||
states={
|
||||
abort_date = fields.Date('Abort Date', states={
|
||||
'invisible': ~Eval('abort', False),
|
||||
}, depends=['abort'])
|
||||
farrowing_date = fields.Date('Farrowing Date', states={
|
||||
'required': Bool(Eval('weaning_date')),
|
||||
'invisible': Bool(Eval('abort')),
|
||||
},
|
||||
depends=['weaning_date', 'abort'])
|
||||
live = fields.Integer('Live',
|
||||
states={
|
||||
}, depends=['weaning_date', 'abort'])
|
||||
live = fields.Integer('Live', states={
|
||||
'required': Bool(Eval('farrowing_date')),
|
||||
'invisible': Bool(Eval('abort')),
|
||||
},
|
||||
depends=['farrowing_date', 'abort'])
|
||||
stillborn = fields.Integer('Stillborn',
|
||||
states={
|
||||
}, depends=['farrowing_date', 'abort'])
|
||||
stillborn = fields.Integer('Stillborn', states={
|
||||
'invisible': Bool(Eval('abort')),
|
||||
},
|
||||
depends=['abort'])
|
||||
mummified = fields.Integer('Mummified',
|
||||
states={
|
||||
}, depends=['abort'])
|
||||
mummified = fields.Integer('Mummified', states={
|
||||
'invisible': Bool(Eval('abort')),
|
||||
},
|
||||
depends=['abort'])
|
||||
fostered = fields.Integer('Fostered',
|
||||
states={
|
||||
}, depends=['abort'])
|
||||
fostered = fields.Integer('Fostered', states={
|
||||
'invisible': Bool(Eval('abort')),
|
||||
},
|
||||
depends=['abort'])
|
||||
weaning_date = fields.DateTime('Weaning Date',
|
||||
states={
|
||||
'invisible': (Bool(Eval('abort')) | (Eval('live', 0) == 0)),
|
||||
},
|
||||
depends=['abort', 'live'])
|
||||
weaned_quantity = fields.Integer('Weaned Quantity',
|
||||
states={
|
||||
}, depends=['abort'])
|
||||
to_weaning_quantity = fields.Function(fields.Integer('To Weaning Quantity',
|
||||
on_change_with=['live', 'fostered']),
|
||||
'on_change_with_to_weaning_quantity')
|
||||
weaning_date = fields.Date('Weaning Date', states={
|
||||
'invisible': (Eval('abort', False) |
|
||||
(Eval('to_weaning_quantity', 0) == 0)),
|
||||
}, depends=['abort', 'to_weaning_quantity'])
|
||||
weaned_quantity = fields.Integer('Weaned Quantity', states={
|
||||
'required': Bool(Eval('weaning_date')),
|
||||
'invisible': (Bool(Eval('abort')) | (Eval('live', 0) == 0)),
|
||||
},
|
||||
depends=['weaning_date', 'abort', 'live'])
|
||||
'invisible': (Eval('abort', False) |
|
||||
(Eval('to_weaning_quantity', 0) == 0)),
|
||||
}, depends=['weaning_date', 'abort', 'to_weaning_quantity'])
|
||||
|
||||
def on_change_with_to_weaning_quantity(self, name=None):
|
||||
return (self.live or 0) + (self.fostered or 0)
|
||||
|
||||
|
||||
class CreateFemale(Wizard):
|
||||
|
@ -1079,6 +1129,8 @@ class CreateFemale(Wizard):
|
|||
'after arrival date "%s".'),
|
||||
'insemination_before_arrival': ('Insemination date "%s" on '
|
||||
'line "%s" can not be before arrival date "%s".'),
|
||||
'abort_before_insemination': ('Abort date "%s" on line'
|
||||
' "%s" can not be before insemination date "%s".'),
|
||||
'farrowing_before_insemination': ('Farrowing date "%s" on line'
|
||||
' "%s" can not be before insemination date "%s".'),
|
||||
'weaning_before_farrowing': ('Weaning date "%s" on line "%s"'
|
||||
|
@ -1102,6 +1154,8 @@ class CreateFemale(Wizard):
|
|||
Insemination = pool.get('farm.insemination.event')
|
||||
Weaning = pool.get('farm.weaning.event')
|
||||
|
||||
events_time = datetime.now().time()
|
||||
|
||||
if (self.start.birthdate and self.start.birthdate >
|
||||
self.start.arrival_date):
|
||||
self.raise_user_error('birthdate_after_arrival',
|
||||
|
@ -1125,34 +1179,43 @@ class CreateFemale(Wizard):
|
|||
self.raise_user_error('greather_than_zero', line.sequence)
|
||||
cycle = Cycle()
|
||||
cycle.animal = female
|
||||
cycle.ordination_date = line.insemination_date
|
||||
|
||||
insemination_events = []
|
||||
last_insemination_date = None
|
||||
for insemination_date in (line.insemination_date,
|
||||
line.second_insemination_date,
|
||||
line.third_insemination_date):
|
||||
if not insemination_date:
|
||||
continue
|
||||
if (not last_insemination_date or
|
||||
last_insemination_date < insemination_date):
|
||||
last_insemination_date = insemination_date
|
||||
if insemination_date < self.start.arrival_date:
|
||||
self.raise_user_error('insemination_before_arrival', (
|
||||
insemination_date, line.sequence,
|
||||
self.start.arrival_date))
|
||||
insemination_date = datetime.combine(insemination_date,
|
||||
datetime.min.time())
|
||||
if (line.farrowing_date and insemination_date >
|
||||
line.farrowing_date):
|
||||
self.raise_user_error('farrowing_before_insemination', (
|
||||
line.farrowing_date, line.sequence,
|
||||
insemination_date))
|
||||
if line.abort_date and insemination_date > line.abort_date:
|
||||
self.raise_user_error('abort_before_insemination', (
|
||||
line.abort_date, line.sequence,
|
||||
insemination_date))
|
||||
insemination = Insemination()
|
||||
insemination.imported = True
|
||||
insemination.animal = female
|
||||
insemination.animal_type = 'female'
|
||||
insemination.farm = farm
|
||||
insemination.specie = self.start.specie
|
||||
insemination.timestamp = insemination_date
|
||||
insemination.timestamp = datetime.combine(insemination_date,
|
||||
events_time)
|
||||
insemination.state = 'validated'
|
||||
insemination_events.append(insemination)
|
||||
cycle.insemination_events = insemination_events
|
||||
|
||||
if line.abort:
|
||||
abort = Abort()
|
||||
abort.female_cycle = cycle
|
||||
|
@ -1161,7 +1224,9 @@ class CreateFemale(Wizard):
|
|||
abort.animal_type = 'female'
|
||||
abort.farm = farm
|
||||
abort.specie = self.start.specie
|
||||
abort.timestamp = line.insemination_date
|
||||
abort.timestamp = datetime.combine(self.abort_date
|
||||
if line.abort_date else last_insemination_date,
|
||||
events_time)
|
||||
abort.state = 'validated'
|
||||
abort.save()
|
||||
cycle.abort_event = abort
|
||||
|
@ -1174,7 +1239,8 @@ class CreateFemale(Wizard):
|
|||
farrowing.animal_type = 'female'
|
||||
farrowing.farm = farm
|
||||
farrowing.specie = self.start.specie
|
||||
farrowing.timestamp = line.farrowing_date
|
||||
farrowing.timestamp = datetime.combine(line.farrowing_date,
|
||||
events_time)
|
||||
farrowing.live = line.live
|
||||
farrowing.stillborn = line.stillborn
|
||||
farrowing.mummified = line.mummified
|
||||
|
@ -1192,7 +1258,8 @@ class CreateFemale(Wizard):
|
|||
foster.animal_type = 'female'
|
||||
foster.farm = farm
|
||||
foster.specie = self.start.specie
|
||||
foster.timestamp = line.farrowing_date
|
||||
foster.timestamp = datetime.combine(line.farrowing_date,
|
||||
events_time)
|
||||
foster.quantity = line.fostered
|
||||
foster.state = 'validated'
|
||||
cycle.foster_events = [foster]
|
||||
|
@ -1202,8 +1269,7 @@ class CreateFemale(Wizard):
|
|||
self.raise_user_error('weaning_before_farrowing', (
|
||||
line.weaning_date, line.sequence,
|
||||
line.farrowing_date))
|
||||
if line.weaned_quantity > (line.live -
|
||||
line.fostered or 0):
|
||||
if line.weaned_quantity > line.to_weaning_quantity:
|
||||
self.raise_user_error('more_weaned_than_live',
|
||||
line.sequence)
|
||||
|
||||
|
@ -1214,7 +1280,8 @@ class CreateFemale(Wizard):
|
|||
weaning.animal_type = 'female'
|
||||
weaning.farm = farm
|
||||
weaning.specie = self.start.specie
|
||||
weaning.timestamp = line.weaning_date
|
||||
weaning.timestamp = datetime.combine(line.weaning_date,
|
||||
events_time)
|
||||
weaning.quantity = line.weaned_quantity
|
||||
weaning.female_to_location = female.initial_location
|
||||
weaning.weaned_to_location = female.initial_location
|
||||
|
@ -1228,6 +1295,7 @@ class CreateFemale(Wizard):
|
|||
self.raise_user_error('missing_weaning', line.sequence)
|
||||
cycle.save()
|
||||
cycle.update_state(None)
|
||||
|
||||
female = Animal(female.id)
|
||||
female.update_current_cycle()
|
||||
if self.start.last_cycle_active:
|
||||
|
|
16
animal.xml
16
animal.xml
|
@ -177,15 +177,6 @@
|
|||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_farm_animal_groups">
|
||||
<field name="model" search="[('model', '=', 'farm.animal')]"/>
|
||||
<field name="group" ref="group_farm_groups"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Sequences -->
|
||||
<record model="ir.sequence.type" id="sequence_type_animal">
|
||||
<field name="name">Farm Animal</field>
|
||||
|
@ -297,7 +288,12 @@
|
|||
<field name="wiz_name">farm.create_female</field>
|
||||
</record>
|
||||
<record model="ir.action-res.group"
|
||||
id="farm_create_female_group_create_femal">
|
||||
id="farm_create_female_group_admin">
|
||||
<field name="action" ref="wizard_farm_create_female"/>
|
||||
<field name="group" ref="group_farm_admin"/>
|
||||
</record>
|
||||
<record model="ir.action-res.group"
|
||||
id="farm_create_female_group_female">
|
||||
<field name="action" ref="wizard_farm_create_female"/>
|
||||
<field name="group" ref="group_farm_females"/>
|
||||
</record>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
|
@ -45,7 +45,7 @@ class AnimalGroup(ModelSQL, ModelView, AnimalMixin):
|
|||
origin = fields.Selection([
|
||||
('purchased', 'Purchased'),
|
||||
('raised', 'Raised'),
|
||||
], 'Origin', required=True, readonly=True,
|
||||
], 'Origin', required=True, readonly=True,
|
||||
help='Raised means that this group was born in the farm. Otherwise, '
|
||||
'it was purchased.')
|
||||
arrival_date = fields.Date('Arrival Date', states={
|
||||
|
@ -83,6 +83,13 @@ class AnimalGroup(ModelSQL, ModelView, AnimalMixin):
|
|||
'Tags')
|
||||
notes = fields.Text('Notes')
|
||||
active = fields.Boolean('Active')
|
||||
feed_unit_digits = fields.Function(fields.Integer('Feed Unit Digits'),
|
||||
'get_unit_digits')
|
||||
consumed_feed = fields.Function(
|
||||
fields.Numeric('Consumed Feed per Animal (Kg)',
|
||||
digits=(16, Eval('feed_unit_digits', 2)),
|
||||
depends=['feed_unit_digits']),
|
||||
'get_consumed_feed')
|
||||
|
||||
# # TODO: Extra
|
||||
# 'type': fields.selection([('static','Static'),('dynamic','Dynamic')],
|
||||
|
@ -180,7 +187,7 @@ class AnimalGroup(ModelSQL, ModelView, AnimalMixin):
|
|||
res = {}
|
||||
for animal_group in animal_groups:
|
||||
ag_lot_id = animal_group.lot.id
|
||||
res[animal_group.id] = [l for l in qbl[ag_lot_id]
|
||||
res[animal_group.id] = [l for l in qbl.get(ag_lot_id, [])
|
||||
if qbl[ag_lot_id][l] > 0.0]
|
||||
return res
|
||||
|
||||
|
@ -286,6 +293,41 @@ class AnimalGroup(ModelSQL, ModelView, AnimalMixin):
|
|||
if self.weights:
|
||||
return self.weights[0].id
|
||||
|
||||
def get_consumed_feed(self, name):
|
||||
pool = Pool()
|
||||
FeedEvent = pool.get('farm.feed.event')
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
now = datetime.now()
|
||||
feed_events = FeedEvent.search([
|
||||
('animal_type', '=', 'group'),
|
||||
('animal_group', '=', self.id),
|
||||
('state', 'in', ['provisional', 'validated']),
|
||||
['OR', [
|
||||
('start_date', '=', None),
|
||||
('timestamp', '<=', now),
|
||||
], [
|
||||
('start_date', '<=', now.date()),
|
||||
]],
|
||||
])
|
||||
|
||||
kg, = Uom.search([
|
||||
('symbol', '=', 'kg'),
|
||||
])
|
||||
consumed_feed = Decimal('0.0')
|
||||
for event in feed_events:
|
||||
if event.start_date and event.timestamp > now:
|
||||
event_feed_quantity = (event.feed_quantity_animal_day *
|
||||
(now.date() - event.start_date).days)
|
||||
else:
|
||||
event_feed_quantity = event.feed_quantity / event.quantity
|
||||
# TODO: it uses compute_price() because quantity is a Decimal
|
||||
# quantity in feed_product default uom. The method is not for
|
||||
# this purpose but it works
|
||||
consumed_feed = Uom.compute_price(kg, event_feed_quantity,
|
||||
event.uom)
|
||||
return consumed_feed
|
||||
|
||||
def check_in_location(self, location, timestamp, quantity=1):
|
||||
with Transaction().set_context(
|
||||
locations=[location.id],
|
||||
|
@ -293,6 +335,8 @@ class AnimalGroup(ModelSQL, ModelView, AnimalMixin):
|
|||
return self.lot.quantity >= quantity
|
||||
|
||||
def check_allowed_location(self, location, event_rec_name):
|
||||
if not location.warehouse:
|
||||
return
|
||||
for farm_line in self.specie.farm_lines:
|
||||
if farm_line.farm.id == location.warehouse.id:
|
||||
if farm_line.has_group:
|
||||
|
|
|
@ -56,6 +56,15 @@
|
|||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_farm_animal_group_groups">
|
||||
<field name="model" search="[('model', '=', 'farm.animal.group')]"/>
|
||||
<field name="group" ref="group_farm_groups"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Sequences -->
|
||||
<record model="ir.sequence.type" id="sequence_type_animal_group">
|
||||
<field name="name">Farm Animal Group</field>
|
||||
|
@ -109,6 +118,17 @@
|
|||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access"
|
||||
id="access_farm_animal_group_weight_farm_groups">
|
||||
<field name="model"
|
||||
search="[('model', '=', 'farm.animal.group.weight')]"/>
|
||||
<field name="group" ref="group_farm_groups"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Menus -->
|
||||
<!--<menuitem action="act_farm_animal_group" id="menu_farm_animal_group" parent="menu_farm" sequence="1"/>-->
|
||||
</data>
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.model import fields, ModelView, ModelSQL, Workflow
|
||||
from trytond.pyson import Equal, Eval, If
|
||||
|
||||
from .abstract_event import AbstractEvent, _STATES_VALIDATED, \
|
||||
_DEPENDS_VALIDATED
|
||||
from .abstract_event import AbstractEvent, ImportedEventMixin, \
|
||||
_STATES_VALIDATED, _DEPENDS_VALIDATED
|
||||
|
||||
__all__ = ['AbortEvent', 'AbortEventFemaleCycle']
|
||||
|
||||
|
||||
class AbortEvent(AbstractEvent):
|
||||
class AbortEvent(AbstractEvent, ImportedEventMixin):
|
||||
'''Farm Abort Event'''
|
||||
__name__ = 'farm.abort.event'
|
||||
_table = 'farm_abort_event'
|
||||
|
@ -32,6 +32,8 @@ class AbortEvent(AbstractEvent):
|
|||
('current_cycle.state', '=', 'pregnant'),
|
||||
()),
|
||||
]
|
||||
if 'imported' not in cls.animal.depends:
|
||||
cls.animal.depends.append('imported')
|
||||
|
||||
@staticmethod
|
||||
def default_animal_type():
|
||||
|
|
|
@ -6,7 +6,7 @@ from trytond.model import fields, ModelSQL, ModelView, Workflow
|
|||
from trytond.pyson import Equal, Eval, If, Id, Not
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
__all__ = ['AbstractEvent']
|
||||
__all__ = ['AbstractEvent', 'ImportedEventMixin']
|
||||
|
||||
_EVENT_STATES = [
|
||||
('draft', 'Draft'),
|
||||
|
@ -27,12 +27,11 @@ _STATES_WRITE_DRAFT_VALIDATED = {
|
|||
}
|
||||
_DEPENDS_WRITE_DRAFT_VALIDATED = ['state']
|
||||
_STATES_VALIDATED_ADMIN = {
|
||||
'required': (Equal(Eval('state'), 'validated') &
|
||||
Not(Eval('imported', False))),
|
||||
'required': Equal(Eval('state'), 'validated'),
|
||||
'invisible': Not(Eval('groups', []).contains(
|
||||
Id('farm', 'group_farm_admin'))),
|
||||
}
|
||||
_DEPENDS_VALIDATED_ADMIN = ['state', 'imported']
|
||||
_DEPENDS_VALIDATED_ADMIN = ['state']
|
||||
|
||||
|
||||
class AbstractEvent(ModelSQL, ModelView, Workflow):
|
||||
|
@ -100,7 +99,6 @@ class AbstractEvent(ModelSQL, ModelView, Workflow):
|
|||
notes = fields.Text('Notes')
|
||||
state = fields.Selection(_EVENT_STATES, 'State', required=True,
|
||||
readonly=True, select=True)
|
||||
imported = fields.Boolean('Imported', readonly=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
|
@ -147,10 +145,6 @@ class AbstractEvent(ModelSQL, ModelView, Workflow):
|
|||
def default_state():
|
||||
return 'draft'
|
||||
|
||||
@staticmethod
|
||||
def default_imported():
|
||||
return False
|
||||
|
||||
def get_rec_name(self, name):
|
||||
if self.animal_type == 'group':
|
||||
return "%s %s" % (self.animal_group.rec_name, self.timestamp)
|
||||
|
@ -217,3 +211,17 @@ class AbstractEvent(ModelSQL, ModelView, Workflow):
|
|||
# @Workflow.transition('cancel')
|
||||
# def cancel(cls, events):
|
||||
# raise NotImplementedError("Please Implement cancel() method")
|
||||
|
||||
|
||||
_STATES_VALIDATED_ADMIN_BUT_IMPORTED = _STATES_VALIDATED_ADMIN.copy()
|
||||
_STATES_VALIDATED_ADMIN_BUT_IMPORTED['required'] &= Not(Eval('imported',
|
||||
False))
|
||||
_DEPENDS_VALIDATED_ADMIN_BUT_IMPORTED = ['state', 'imported']
|
||||
|
||||
|
||||
class ImportedEventMixin:
|
||||
imported = fields.Boolean('Imported', readonly=True)
|
||||
|
||||
@staticmethod
|
||||
def default_imported():
|
||||
return False
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.model import fields, ModelView, ModelSQL, Workflow
|
||||
from trytond.pyson import And, Bool, Equal, Eval, Id, If, Not
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .abstract_event import AbstractEvent, _STATES_WRITE_DRAFT, \
|
||||
_DEPENDS_WRITE_DRAFT, _STATES_VALIDATED, _DEPENDS_VALIDATED
|
||||
from .abstract_event import AbstractEvent, ImportedEventMixin, \
|
||||
_STATES_WRITE_DRAFT, _DEPENDS_WRITE_DRAFT, \
|
||||
_STATES_VALIDATED, _DEPENDS_VALIDATED
|
||||
|
||||
__all__ = ['FarrowingProblem', 'FarrowingEvent', 'FarrowingEventFemaleCycle',
|
||||
'FarrowingEventAnimalGroup']
|
||||
|
@ -20,7 +21,7 @@ class FarrowingProblem(ModelSQL, ModelView):
|
|||
name = fields.Char('Name', required=True, translate=True)
|
||||
|
||||
|
||||
class FarrowingEvent(AbstractEvent):
|
||||
class FarrowingEvent(AbstractEvent, ImportedEventMixin):
|
||||
'''Farm Farrowing Event'''
|
||||
__name__ = 'farm.farrowing.event'
|
||||
_table = 'farm_farrowing_event'
|
||||
|
@ -50,7 +51,7 @@ class FarrowingEvent(AbstractEvent):
|
|||
'required': And(And(Equal(Eval('state'), 'validated'),
|
||||
Bool(Eval('live', 0))), Not(Eval('imported', False))),
|
||||
},
|
||||
depends=['specie', 'live', 'state'])
|
||||
depends=['specie', 'live', 'state', 'imported'])
|
||||
move = fields.Many2One('stock.move', 'Stock Move', readonly=True, domain=[
|
||||
('lot.animal_group', '=', Eval('produced_group')),
|
||||
],
|
||||
|
@ -60,7 +61,7 @@ class FarrowingEvent(AbstractEvent):
|
|||
'invisible': Not(Eval('groups', []).contains(
|
||||
Id('farm', 'group_farm_admin'))),
|
||||
},
|
||||
depends=['produced_group', 'live', 'state'])
|
||||
depends=['produced_group', 'live', 'state', 'imported'])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
|
@ -74,6 +75,8 @@ class FarrowingEvent(AbstractEvent):
|
|||
('current_cycle.state', '=', 'pregnant'),
|
||||
()),
|
||||
]
|
||||
if 'imported' not in cls.animal.depends:
|
||||
cls.animal.depends.append('imported')
|
||||
cls._error_messages.update({
|
||||
'event_without_dead_nor_live': ('The farrowing event "%s" has '
|
||||
'0 in Dead and Live. It has to have some unit in some of '
|
||||
|
|
|
@ -5,14 +5,15 @@ from trytond.pyson import And, Bool, Equal, Eval, If
|
|||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .abstract_event import AbstractEvent, _STATES_WRITE_DRAFT, \
|
||||
_DEPENDS_WRITE_DRAFT, _STATES_VALIDATED, _DEPENDS_VALIDATED, \
|
||||
_STATES_VALIDATED_ADMIN, _DEPENDS_VALIDATED_ADMIN
|
||||
from .abstract_event import AbstractEvent, ImportedEventMixin, \
|
||||
_STATES_WRITE_DRAFT, _DEPENDS_WRITE_DRAFT, \
|
||||
_STATES_VALIDATED, _DEPENDS_VALIDATED, \
|
||||
_STATES_VALIDATED_ADMIN_BUT_IMPORTED, _DEPENDS_VALIDATED_ADMIN_BUT_IMPORTED
|
||||
|
||||
__all__ = ['FosterEvent']
|
||||
|
||||
|
||||
class FosterEvent(AbstractEvent):
|
||||
class FosterEvent(AbstractEvent, ImportedEventMixin):
|
||||
'''Farm Foster Event'''
|
||||
__name__ = 'farm.foster.event'
|
||||
_table = 'farm_foster_event'
|
||||
|
@ -49,7 +50,8 @@ class FosterEvent(AbstractEvent):
|
|||
],
|
||||
states=_STATES_VALIDATED, depends=_DEPENDS_VALIDATED + ['animal'])
|
||||
move = fields.Many2One('stock.move', 'Stock Move', readonly=True,
|
||||
states=_STATES_VALIDATED_ADMIN, depends=_DEPENDS_VALIDATED_ADMIN)
|
||||
states=_STATES_VALIDATED_ADMIN_BUT_IMPORTED,
|
||||
depends=_DEPENDS_VALIDATED_ADMIN_BUT_IMPORTED)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
|
@ -66,6 +68,8 @@ class FosterEvent(AbstractEvent):
|
|||
]
|
||||
if 'farm' not in cls.animal.depends:
|
||||
cls.animal.depends.append('farm')
|
||||
if 'imported' not in cls.animal.depends:
|
||||
cls.animal.depends.append('imported')
|
||||
cls._error_messages.update({
|
||||
'farrowing_group_not_in_location': ('The farrowing group of '
|
||||
'foster event "%(event)s" doesn\'t have %(quantity)s '
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
from trytond.model import fields, ModelView, Workflow
|
||||
from trytond.pyson import Bool, Equal, Eval, If
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .abstract_event import AbstractEvent, _STATES_WRITE_DRAFT, \
|
||||
_DEPENDS_WRITE_DRAFT, _STATES_VALIDATED, _DEPENDS_VALIDATED, \
|
||||
_STATES_VALIDATED_ADMIN, _DEPENDS_VALIDATED_ADMIN
|
||||
from .abstract_event import AbstractEvent, ImportedEventMixin, \
|
||||
_STATES_WRITE_DRAFT, _DEPENDS_WRITE_DRAFT, \
|
||||
_STATES_VALIDATED, _DEPENDS_VALIDATED, \
|
||||
_STATES_VALIDATED_ADMIN_BUT_IMPORTED, _DEPENDS_VALIDATED_ADMIN_BUT_IMPORTED
|
||||
|
||||
__all__ = ['InseminationEvent']
|
||||
|
||||
|
||||
class InseminationEvent(AbstractEvent):
|
||||
class InseminationEvent(AbstractEvent, ImportedEventMixin):
|
||||
'''Farm Insemination Event'''
|
||||
__name__ = 'farm.insemination.event'
|
||||
_table = 'farm_insemination_event'
|
||||
|
@ -40,8 +41,8 @@ class InseminationEvent(AbstractEvent):
|
|||
],
|
||||
states=_STATES_VALIDATED, depends=_DEPENDS_VALIDATED + ['animal'])
|
||||
move = fields.Many2One('stock.move', 'Stock Move', readonly=True,
|
||||
states=_STATES_VALIDATED_ADMIN,
|
||||
depends=_DEPENDS_VALIDATED_ADMIN + ['dose_lot'])
|
||||
states=_STATES_VALIDATED_ADMIN_BUT_IMPORTED,
|
||||
depends=_DEPENDS_VALIDATED_ADMIN_BUT_IMPORTED + ['dose_lot'])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
|
@ -138,9 +139,12 @@ class InseminationEvent(AbstractEvent):
|
|||
'timestamp': insemination_event.timestamp,
|
||||
})
|
||||
current_cycle = insemination_event.animal.current_cycle
|
||||
# Si no es necessari crear un segon cicle quan hi ha
|
||||
# diagnosis_event sense abort/farrowing (que tindra el mateix num)
|
||||
# if (not current_cycle or current_cycle.farrowing_event or
|
||||
# current_cycle.abort_event):
|
||||
# It creates a new cycle if a diagnosis event has been done
|
||||
if (not current_cycle or current_cycle.farrowing_event or
|
||||
current_cycle.abort_event):
|
||||
if not current_cycle or current_cycle.diagnosis_events:
|
||||
current_cycle = FemaleCycle(animal=insemination_event.animal)
|
||||
current_cycle.save()
|
||||
insemination_event.animal.current_cycle = current_cycle
|
||||
|
|
|
@ -31,7 +31,7 @@ class MoveEvent(AbstractEvent):
|
|||
context={'restrict_by_specie_animal_type': True})
|
||||
to_location = fields.Many2One('stock.location', 'Destination',
|
||||
required=True, domain=[
|
||||
('type', '=', 'storage'),
|
||||
('type', 'in', ['storage', 'customer']),
|
||||
('silo', '=', False),
|
||||
('id', '!=', Eval('from_location')),
|
||||
],
|
||||
|
@ -233,12 +233,13 @@ class MoveEvent(AbstractEvent):
|
|||
lot = (self.animal_type != 'group' and self.animal.lot or
|
||||
self.animal_group.lot)
|
||||
|
||||
if lot and lot.cost_price != self.unit_price:
|
||||
if lot and self.unit_price and lot.cost_price != self.unit_price:
|
||||
cost_line = LotCostLine()
|
||||
cost_line.lot = lot
|
||||
cost_line.category = category_id
|
||||
cost_line.origin = str(self)
|
||||
cost_line.unit_price = self.unit_price - lot.cost_price
|
||||
cost_line.unit_price = (self.unit_price - lot.cost_price
|
||||
if lot.cost_price else self.unit_price)
|
||||
cost_line.save()
|
||||
|
||||
return Move(
|
||||
|
@ -280,7 +281,7 @@ class MoveEvent(AbstractEvent):
|
|||
default = {}
|
||||
else:
|
||||
default = default.copy()
|
||||
default.updat({
|
||||
default.update({
|
||||
'move': None,
|
||||
'weight_record': None,
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
"""
|
||||
By now, the Weaning Event will only allow weaning the group of animals
|
||||
associated with the female.
|
||||
|
@ -14,13 +14,14 @@ from trytond.pyson import Equal, Eval, Id, If, Not
|
|||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .abstract_event import AbstractEvent, _STATES_WRITE_DRAFT, \
|
||||
_DEPENDS_WRITE_DRAFT, _STATES_VALIDATED, _DEPENDS_VALIDATED
|
||||
from .abstract_event import AbstractEvent, ImportedEventMixin, \
|
||||
_STATES_WRITE_DRAFT, _DEPENDS_WRITE_DRAFT, \
|
||||
_STATES_VALIDATED, _DEPENDS_VALIDATED
|
||||
|
||||
__all__ = ['WeaningEvent', 'WeaningEventFemaleCycle']
|
||||
|
||||
|
||||
class WeaningEvent(AbstractEvent):
|
||||
class WeaningEvent(AbstractEvent, ImportedEventMixin):
|
||||
'''Farm Weaning Event'''
|
||||
__name__ = 'farm.weaning.event'
|
||||
_table = 'farm_weaning_event'
|
||||
|
@ -108,6 +109,8 @@ class WeaningEvent(AbstractEvent):
|
|||
]
|
||||
if 'farm' not in cls.animal.depends:
|
||||
cls.animal.depends.append('farm')
|
||||
if 'imported' not in cls.animal.depends:
|
||||
cls.animal.depends.append('imported')
|
||||
# TODO: not added constraint for non negative quantity but negative
|
||||
# quantities are not suported
|
||||
|
||||
|
|
36
specie.py
36
specie.py
|
@ -1,11 +1,10 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains the full
|
||||
#copyright notices and license terms.
|
||||
# The COPYRIGHT file at the top level of this repository contains the full
|
||||
# copyright notices and license terms.
|
||||
import logging
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.pyson import Bool, Eval, Id, Not, Or
|
||||
from trytond.pyson import PYSONDecoder, PYSONEncoder, Bool, Eval, Id, Not, Or
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.tools import safe_eval
|
||||
|
||||
__all__ = ['Specie', 'SpecieModel', 'SpecieFarmLine', 'Breed', 'Menu',
|
||||
'ActWindow']
|
||||
|
@ -58,9 +57,9 @@ class Specie(ModelSQL, ModelView):
|
|||
"because it is required.")
|
||||
semen_product = fields.Many2One('product.product', "Semen's Product",
|
||||
# TODO: it doesn't work but it should
|
||||
#domain=[
|
||||
# ('default_uom.category', '=', Id('product', 'uom_cat_volume')),
|
||||
# ],
|
||||
# domain=[
|
||||
# ('default_uom.category', '=', Id('product', 'uom_cat_volume')),
|
||||
# ],
|
||||
states={
|
||||
'readonly': Not(Or(Bool(Eval('male_enabled')),
|
||||
Bool(Eval('female_enabled')))),
|
||||
|
@ -242,10 +241,10 @@ class Specie(ModelSQL, ModelView):
|
|||
seq, icon, None, event_act_window, False,
|
||||
current_menus, current_actions)
|
||||
animal_submenu_seq += 1
|
||||
#Females have an special menu for creating with full history
|
||||
# Females have an special menu for creating with full history
|
||||
if animal_type == 'female':
|
||||
wizard = ActWizard(ModelData.get_id(MODULE_NAME,
|
||||
'farm_create_female'))
|
||||
'wizard_farm_create_female'))
|
||||
|
||||
action = ('ir.action.wizard', wizard.id)
|
||||
menu_vals = {
|
||||
|
@ -457,7 +456,7 @@ class Specie(ModelSQL, ModelView):
|
|||
# is_generic_event if its valid_animal_types are all
|
||||
# animal types => difference is empty
|
||||
'is_generic_event':
|
||||
not animal_types_set.difference(valid_animal_types)
|
||||
not animal_types_set.difference(valid_animal_types)
|
||||
}
|
||||
return event_configuration
|
||||
|
||||
|
@ -512,20 +511,20 @@ class Specie(ModelSQL, ModelView):
|
|||
|
||||
if not new_domain:
|
||||
new_domain = []
|
||||
original_domain = (original_action.domain and
|
||||
safe_eval(original_action.domain))
|
||||
if original_domain and isinstance(original_domain, list):
|
||||
original_domain = (PYSONDecoder().decode(original_action.pyson_domain)
|
||||
if original_action.pyson_domain else [])
|
||||
if original_domain:
|
||||
new_domain.extend(original_domain)
|
||||
|
||||
original_context = (original_action.context
|
||||
and safe_eval(original_action.context))
|
||||
if original_context and isinstance(original_context, dict):
|
||||
original_context = (PYSONDecoder().decode(original_action.pyson_context)
|
||||
if original_action.pyson_context else [])
|
||||
if original_context:
|
||||
new_context.update(original_context)
|
||||
|
||||
action_vals = {
|
||||
'specie': specie.id,
|
||||
'domain': str(new_domain),
|
||||
'context': str(new_context),
|
||||
'domain': PYSONEncoder().encode(new_domain),
|
||||
'context': PYSONEncoder().encode(new_context),
|
||||
}
|
||||
|
||||
if act_window:
|
||||
|
@ -545,7 +544,6 @@ class Specie(ModelSQL, ModelView):
|
|||
if translate_action:
|
||||
cls._write_field_in_langs(ActWindow, act_window, 'name',
|
||||
untranslated_title)
|
||||
pass
|
||||
|
||||
group_ids = group and [group.id] or []
|
||||
if group_ids:
|
||||
|
|
55
stock.xml
55
stock.xml
|
@ -24,6 +24,61 @@
|
|||
<field name="inherit" ref="stock.location_view_list"/>
|
||||
<field name="name">stock_location_list</field>
|
||||
</record>
|
||||
|
||||
<!-- Permissions -->
|
||||
<record model="ir.model.access" id="access_stock_lot_farm">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_stock_lot_admin">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_stock_lot_males">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm_males"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_stock_lot_females">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm_females"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_stock_lot_individuals">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm_individuals"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_stock_lot_groups">
|
||||
<field name="model" search="[('model', '=', 'stock.lot')]"/>
|
||||
<field name="group" ref="group_farm_groups"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
<record model="stock.lot.cost_category"
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<form string="Female Cycle">
|
||||
<form string="Female Cycle" col="6">
|
||||
<label name="insemination_date"/>
|
||||
<field name="insemination_date"/>
|
||||
<label name="second_insemination_date"/>
|
||||
<field name="second_insemination_date"/>
|
||||
<label name="third_insemination_date"/>
|
||||
<field name="third_insemination_date"/>
|
||||
<newline/>
|
||||
<label name="abort"/>
|
||||
<field name="abort"/>
|
||||
<label name="abort_date"/>
|
||||
<field name="abort_date"/>
|
||||
<newline/>
|
||||
<label name="farrowing_date"/>
|
||||
<field name="farrowing_date"/>
|
||||
<label name="live"/>
|
||||
|
@ -18,8 +22,10 @@
|
|||
<field name="stillborn"/>
|
||||
<label name="mummified"/>
|
||||
<field name="mummified"/>
|
||||
<newline/>
|
||||
<label name="fostered"/>
|
||||
<field name="fostered"/>
|
||||
<newline/>
|
||||
<label name="weaning_date"/>
|
||||
<field name="weaning_date"/>
|
||||
<label name="weaned_quantity"/>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<field name="second_insemination_date"/>
|
||||
<field name="third_insemination_date"/>
|
||||
<field name="abort"/>
|
||||
<field name="abort_date"/>
|
||||
<field name="farrowing_date"/>
|
||||
<field name="live"/>
|
||||
<field name="stillborn"/>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<field name="weight"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<label name="weight_record" colspan="4"/>
|
||||
<field name="weight_record" colspan="4"/>
|
||||
</group>
|
||||
</xpath>
|
||||
|
|
Loading…
Reference in New Issue