trytond-sale_farm/sale.py

289 lines
11 KiB
Python
Raw Permalink Normal View History

2014-05-07 14:10:58 +02:00
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from datetime import datetime
from decimal import Decimal
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, Id, Not, Or
from trytond.transaction import Transaction
2019-06-17 02:00:27 +02:00
from trytond.exceptions import UserError
from trytond.i18n import gettext
2014-05-07 14:10:58 +02:00
2019-06-17 02:00:27 +02:00
class MoveEvent(metaclass=PoolMeta):
2014-05-07 14:10:58 +02:00
__name__ = 'farm.move.event'
origin = fields.Reference('Origin', selection='get_origin', readonly=True)
2014-05-07 14:10:58 +02:00
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return ['sale.line']
@classmethod
def get_origin(cls):
pool = Pool()
Model = pool.get('ir.model')
models = cls._get_origin()
models = Model.search([
('model', 'in', models),
])
return [('', '')] + [(m.model, m.name) for m in models]
@classmethod
def validate_event(cls, events):
pool = Pool()
Sale = pool.get('sale.sale')
sales_to_process = set()
for event in events:
if event.origin and isinstance(event.origin, SaleLine):
if not event.weight:
2019-06-17 02:00:27 +02:00
raise UserError(gettext(
'sale_farm.msg_weight_required_in_sale_move_event',
event=event.rec_name))
2014-05-07 14:10:58 +02:00
sales_to_process.add(event.origin.sale.id)
user_farms = Transaction().context.get('farms')
with Transaction().set_user(0, set_context=True), \
Transaction().set_context(farms=user_farms):
2014-05-14 16:58:02 +02:00
super(MoveEvent, cls).validate_event(events)
if sales_to_process:
Sale.process(Sale.browse(list(sales_to_process)))
2014-05-07 14:10:58 +02:00
2019-06-17 02:00:27 +02:00
class Sale(metaclass=PoolMeta):
2014-05-07 14:10:58 +02:00
__name__ = 'sale.sale'
2019-06-17 02:00:27 +02:00
move_events = fields.Function(fields.Many2Many('farm.move.event', None,
None, "Animal's Moves"), 'get_move_events')
2014-05-07 14:10:58 +02:00
def get_move_events(self, name):
return [m.id for l in self.lines for m in l.move_events]
def get_shipment_state(self):
state = super(Sale, self).get_shipment_state()
if state in ('none', 'sent') and self.move_events:
if all(l.move_event_done for l in self.lines):
return 'sent'
else:
return 'waiting'
return state
@classmethod
def process(cls, sales):
super(Sale, cls).process(sales)
for sale in sales:
if sale.invoice_state != 'paid' or sale.shipment_state != 'sent':
continue
if not sale.move_events:
continue
for line in sale.lines:
line.set_move_event_unit_price()
def create_shipment(self, shipment_type):
2021-06-25 15:44:11 +02:00
pool = Pool()
MoveEvent = pool.get('farm.move.event')
2014-05-07 14:10:58 +02:00
res = super(Sale, self).create_shipment(shipment_type)
if self.shipment_method == 'manual':
return res
2021-06-25 15:44:11 +02:00
to_save = []
2014-05-07 14:10:58 +02:00
for line in self.lines:
move_event = line.get_move_event(shipment_type)
if move_event:
2021-06-25 15:44:11 +02:00
to_save.append(move_event)
MoveEvent.save(to_save)
2014-05-07 14:10:58 +02:00
return res
2019-06-17 02:00:27 +02:00
class SaleLine(metaclass=PoolMeta):
2014-05-07 14:10:58 +02:00
__name__ = 'sale.line'
animal = fields.Reference('Animal', selection='get_animal_models', states={
'invisible': Or(Eval('type') != 'line',
Not(Eval('context', {}).get('groups', []).contains(
Id('farm', 'group_farm')))),
'readonly': ~Eval('_parent_sale', {}),
2015-01-22 17:54:51 +01:00
}, depends=['type'])
2014-05-07 14:10:58 +02:00
animal_type = fields.Function(fields.Selection([
(None, ''),
('male', 'Male'),
('female', 'Female'),
('individual', 'Individual'),
('group', 'Group'),
2015-01-22 17:54:51 +01:00
], 'Animal Type'),
2014-05-07 14:10:58 +02:00
'on_change_with_animal_type')
animal_location = fields.Many2One('stock.location', 'Animal Location',
domain=[
('type', '=', 'storage'),
], states={
'invisible': ~Bool(Eval('animal_type')),
'required': Bool(Eval('animal_type')),
'readonly': Eval('animal_type', '') != 'group',
2015-01-22 17:54:51 +01:00
}, depends=['animal_type'])
2014-05-07 14:10:58 +02:00
animal_quantity = fields.Integer('Animal Quantity', states={
'invisible': Eval('animal_type', '') != 'group',
'required': Eval('animal_type', '') == 'group',
}, depends=['animal_type'])
move_events = fields.One2Many('farm.move.event', 'origin',
"Animal's Moves", readonly=True)
2014-05-07 14:10:58 +02:00
@classmethod
def get_animal_models(cls):
IrModel = Pool().get('ir.model')
models = IrModel.search([
('model', 'in', [
'farm.animal',
'farm.animal.group',
]),
])
return [('', '')] + [(m.model, m.name) for m in models]
2019-06-17 02:00:27 +02:00
@fields.depends('type', 'animal', 'sale', '_parent_sale.sale_date')
2014-05-07 14:10:58 +02:00
def on_change_animal(self):
Animal = Pool().get('farm.animal')
self.animal_location = None
self.animal_quantity = None
2014-05-07 14:10:58 +02:00
if (not self.type or self.type != 'line' or not self.animal or
self.animal.id < 0):
return
2014-05-07 14:10:58 +02:00
analytic_accounts = None
if isinstance(self.animal, Animal):
self.animal_location = (self.animal.location
if self.animal.location else None)
self.animal_quantity = 1
2014-05-07 14:10:58 +02:00
if hasattr(self.animal.location, 'analytic_accounts'):
analytic_accounts = self.animal.location.analytic_accounts
elif len(self.animal.locations) == 1:
group_location = self.animal.locations[0]
with Transaction().set_context(
locations=[group_location.id],
stock_date_end=(self.sale.sale_date
if self.sale and self.sale.sale_date else None)):
self.animal_location = group_location
self.animal_quantity = self.animal.quantity or None
2014-05-07 14:10:58 +02:00
if hasattr(group_location, 'analytic_accounts'):
analytic_accounts = group_location.analytic_accounts
if (self.animal_location and hasattr(self, 'analytic_accounts') and
2014-05-07 14:10:58 +02:00
analytic_accounts):
for account in analytic_accounts.accounts:
setattr(self, 'analytic_account_%s' % account.root.id,
account.id)
2014-05-07 14:10:58 +02:00
2015-01-22 17:54:51 +01:00
@fields.depends('type', 'animal')
2014-05-07 14:10:58 +02:00
def on_change_with_animal_type(self, name=None):
Animal = Pool().get('farm.animal')
if not self.type or not self.animal or self.animal.id < 0:
return None
if isinstance(self.animal, Animal):
return self.animal.type
return 'group'
2019-06-17 02:00:27 +02:00
@fields.depends('animal', 'animal_location', 'sale',
'_parent_sale.sale_date')
2014-05-07 14:10:58 +02:00
def on_change_animal_location(self):
if not self.animal or not self.animal_location:
self.animal_quantity = None
return
2014-05-07 14:10:58 +02:00
with Transaction().set_context(
locations=[self.animal_location.id],
stock_date_end=(self.sale.sale_date
if self.sale and self.sale.sale_date else None)):
self.animal_quantity = self.animal.lot.quantity or None
2014-05-07 14:10:58 +02:00
if (hasattr(self, 'analytic_accounts') and
self.animal_location.analytic_accounts):
for account in self.animal_location.analytic_accounts.accounts:
setattr(self, 'analytic_account_%s' % account.root.id,
account.id)
2014-05-07 14:10:58 +02:00
@property
def move_event_done(self):
if not self.animal_type:
return True
quantity = self.animal_quantity
for event in self.move_events:
if event.state == 'draft':
return False
if event.state == 'validated':
quantity -= event.quantity
if (self.animal_quantity > 0 and quantity > 0 or
self.animal_quantity < 0 and quantity < 0):
return False
return True
def get_move_event(self, shipment_type):
'''
Return move event for the sale line according ot shipment_type
'''
pool = Pool()
MoveEvent = pool.get('farm.move.event')
if self.type != 'line':
return
if not self.animal_type:
return
if (shipment_type == 'out') != (self.animal_quantity >= 0):
return
quantity = self.animal_quantity
for event in self.move_events:
if event.state != 'cancelled':
2014-05-07 14:10:58 +02:00
quantity -= event.quantity
if (self.animal_quantity > 0 and quantity <= 0 or
self.animal_quantity < 0 and quantity >= 0):
return
if not self.sale.party.customer_location:
2019-06-17 02:00:27 +02:00
raise UserError(gettext('sale.msg_sale_customer_location_required',
sale=self.sale.rec_name,
line=self.rec_name,
))
2014-05-07 14:10:58 +02:00
with Transaction().set_user(0, set_context=True):
move_event = MoveEvent()
move_event.animal_type = self.animal_type
move_event.specie = self.animal.specie
move_event.farm = self.animal_location.warehouse
if self.animal_type == 'group':
move_event.animal_group = self.animal
else:
move_event.animal = self.animal
move_event.timestamp = datetime.combine(self.shipping_date,
datetime.now().time()) if self.shipping_date else datetime.now()
2014-05-07 14:10:58 +02:00
move_event.from_location = self.animal_location
move_event.to_location = self.sale.party.customer_location
move_event.quantity = self.animal_quantity
move_event.unit_price = Decimal('0.0')
move_event.origin = self
return move_event
def set_move_event_unit_price(self):
invoice_amount = sum(il.on_change_with_amount()
for il in self.invoice_lines if il.invoice.paid)
delivered_animals = sum(e.quantity for e in self.move_events
if e.state == 'validated')
2015-10-29 12:04:13 +01:00
unit_price = self.sale.currency.round(invoice_amount
/ delivered_animals)
2014-05-07 14:10:58 +02:00
for event in self.move_events:
if event.state != 'validated':
continue
event.unit_price = unit_price
event.save()
@classmethod
def copy(cls, lines, default=None):
if default is None:
default = {}
else:
default = default.copy()
2019-06-17 02:00:27 +02:00
default.setdefault('move_events', None)
2014-05-07 14:10:58 +02:00
return super(SaleLine, cls).copy(lines, default=default)