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:
Guillem Barba 2014-05-07 14:00:26 +02:00
parent e90569b000
commit d41796804c
16 changed files with 342 additions and 130 deletions

168
animal.py
View File

@ -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:

View File

@ -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>

View File

@ -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:

View File

@ -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>

View File

@ -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():

View File

@ -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

View File

@ -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 '

View File

@ -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 '

View File

@ -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

View File

@ -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,
})

View File

@ -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

View File

@ -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:

View File

@ -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"

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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>