Files

215 lines
7.8 KiB
Python
Raw Permalink Normal View History

2014-12-02 12:54:51 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2011-01-16 22:07:50 +01:00
from decimal import Decimal
2021-12-11 17:58:42 +01:00
from trytond.i18n import gettext
from trytond.model import Check, ModelView, Workflow, fields
from trytond.model.exceptions import AccessError
2012-09-24 22:17:37 +02:00
from trytond.pool import Pool, PoolMeta
2024-10-27 16:20:45 +01:00
from trytond.pyson import Eval
2013-01-13 22:50:22 +01:00
from trytond.transaction import Transaction
2012-09-24 22:17:37 +02:00
2011-01-16 22:07:50 +01:00
def _get_field(type_):
if type_.startswith('in_'):
return 'in_anglo_saxon_quantity'
else:
return 'out_anglo_saxon_quantity'
2018-06-26 19:11:44 +02:00
class Move(metaclass=PoolMeta):
2012-09-24 22:17:37 +02:00
__name__ = 'stock.move'
2024-10-27 16:20:45 +01:00
in_anglo_saxon_quantity = fields.Float(
"Input Anglo-Saxon Quantity", required=True,
domain=[
('in_anglo_saxon_quantity', '<=', Eval('quantity', 0)),
])
out_anglo_saxon_quantity = fields.Float(
"Output Anglo-Saxon Quantity", required=True,
domain=[
('out_anglo_saxon_quantity', '<=', Eval('quantity', 0)),
])
2011-01-16 22:07:50 +01:00
2012-09-24 22:17:37 +02:00
@classmethod
def __setup__(cls):
2025-04-04 00:45:18 +02:00
super().__setup__()
cls._allow_modify_closed_period.update(['in_anglo_saxon_quantity',
'out_anglo_saxon_quantity'])
t = cls.__table__()
2012-09-24 22:17:37 +02:00
cls._sql_constraints += [
('check_in_anglo_saxon_quantity',
Check(t, t.quantity >= t.in_anglo_saxon_quantity),
2018-12-29 14:20:29 +01:00
'account_stock_anglo_saxon.msg_move_quantity_greater'),
('check_out_anglo_saxon_quantity',
Check(t, t.quantity >= t.out_anglo_saxon_quantity),
2018-12-29 14:20:29 +01:00
'account_stock_anglo_saxon.msg_move_quantity_greater'),
2012-09-24 22:17:37 +02:00
]
2011-01-16 22:07:50 +01:00
2012-09-24 22:17:37 +02:00
@staticmethod
def default_in_anglo_saxon_quantity():
return 0.0
@staticmethod
def default_out_anglo_saxon_quantity():
2011-01-16 22:07:50 +01:00
return 0.0
2012-09-24 22:17:37 +02:00
def _get_account_stock_move_lines(self, type_):
pool = Pool()
Uom = pool.get('product.uom')
AccountMoveLine = pool.get('account.move.line')
2013-03-05 10:38:11 +01:00
Currency = pool.get('currency.currency')
2025-04-04 00:45:18 +02:00
lines = super()._get_account_stock_move_lines(type_)
cost_price_method = self.product.get_multivalue(
'cost_price_method', **self._cost_price_pattern)
if type_.endswith('supplier') and cost_price_method == 'fixed':
2023-08-22 13:18:33 +02:00
cost_price = Uom.compute_price(
self.product.default_uom, self.cost_price, self.unit)
2013-03-05 10:38:11 +01:00
with Transaction().set_context(date=self.effective_date):
unit_price = Currency.compute(self.currency, self.unit_price,
self.company.currency, round=False)
2012-09-24 22:17:37 +02:00
amount = self.company.currency.round(
2013-03-05 10:38:11 +01:00
Decimal(str(self.quantity)) * (unit_price - cost_price))
2012-09-24 22:17:37 +02:00
if self.company.currency.is_zero(amount):
2012-02-06 16:02:26 +01:00
return lines
2020-01-08 14:36:45 +01:00
account = self.product.account_stock_in_used
for move_line in lines:
2012-09-24 22:17:37 +02:00
if move_line.account == account:
break
else:
return lines
2012-02-06 16:02:26 +01:00
if type_.startswith('in_'):
2012-09-24 22:17:37 +02:00
move_line.credit += amount
debit = amount
credit = Decimal(0)
2012-02-06 16:02:26 +01:00
else:
2012-09-24 22:17:37 +02:00
move_line.debit += amount
debit = Decimal(0)
credit = amount
if amount < Decimal(0):
debit, credit = -credit, -debit
2012-09-24 22:17:37 +02:00
move_line = AccountMoveLine(
debit=debit,
credit=credit,
account=self.product.account_expense_used,
)
2012-02-06 16:02:26 +01:00
lines.append(move_line)
return lines
2012-09-24 22:17:37 +02:00
@classmethod
def _get_anglo_saxon_move(cls, moves, quantity, type_):
2011-01-16 22:07:50 +01:00
'''
Generator of (move, qty, cost_price) where move is the move to be
consumed, qty is the quantity (in the product default uom) to be
consumed on this move and cost_price is in the company currency.
2011-01-16 22:07:50 +01:00
'''
pool = Pool()
Uom = pool.get('product.uom')
Currency = pool.get('currency.currency')
2011-01-16 22:07:50 +01:00
as_qty_field = _get_field(type_)
2011-01-16 22:07:50 +01:00
consumed_qty = 0.0
for move in moves:
2023-08-22 13:18:33 +02:00
qty = Uom.compute_qty(
move.unit,
move.quantity - getattr(move, as_qty_field),
move.product.default_uom, round=False)
2011-01-16 22:07:50 +01:00
if qty <= 0.0:
continue
if qty > quantity - consumed_qty:
qty = quantity - consumed_qty
if consumed_qty >= quantity:
break
if type_.endswith('supplier'):
with Transaction().set_context(date=move.effective_date):
unit_price = Currency.compute(move.currency,
move.unit_price, move.company.currency, round=False)
2023-08-22 13:18:33 +02:00
cost_price = Uom.compute_price(
move.unit, unit_price, move.product.default_uom)
else:
cost_price = move.cost_price
yield (move, qty, cost_price)
consumed_qty += qty
2011-01-16 22:07:50 +01:00
2012-09-24 22:17:37 +02:00
@classmethod
def update_anglo_saxon_quantity_product_cost(cls, product, moves,
2023-08-22 13:18:33 +02:00
quantity, unit, type_):
2011-01-16 22:07:50 +01:00
'''
Return the cost for quantity based on lines.
Update anglo_saxon_quantity on the concerned moves.
'''
2013-03-25 19:30:02 +01:00
pool = Pool()
Uom = pool.get('product.uom')
2011-01-16 22:07:50 +01:00
assert all(m.product == product for m in moves), 'wrong product'
2012-04-03 20:06:29 +02:00
assert type_.startswith('in_') or type_.startswith('out_'), \
'wrong type'
2011-01-16 22:07:50 +01:00
2023-08-22 13:18:33 +02:00
total_qty = Uom.compute_qty(
unit, quantity, product.default_uom, round=False)
2011-01-16 22:07:50 +01:00
as_qty_field = _get_field(type_)
cost = Decimal(0)
2011-01-16 22:07:50 +01:00
consumed_qty = 0.0
for move, move_qty, move_cost_price in cls._get_anglo_saxon_move(
moves, total_qty, type_):
2011-01-16 22:07:50 +01:00
consumed_qty += move_qty
cost += move_cost_price * Decimal(str(move_qty))
2017-10-11 22:45:27 +02:00
move_qty = Uom.compute_qty(
2023-08-22 13:18:33 +02:00
product.default_uom, move_qty, move.unit, round=False)
2017-10-11 22:45:27 +02:00
# Avoid float rounding issue but allow only rounding precision lost
new_qty = (getattr(move, as_qty_field) or 0.0) + move_qty
2023-08-22 13:18:33 +02:00
assert move.unit.round(new_qty) <= move.quantity
2017-10-11 22:45:27 +02:00
new_qty = min(new_qty, move.quantity)
2014-09-17 21:45:07 +02:00
cls.write([move], {
2017-10-11 22:45:27 +02:00
as_qty_field: new_qty,
2014-09-17 21:45:07 +02:00
})
2011-01-16 22:07:50 +01:00
if consumed_qty < total_qty:
qty = total_qty - consumed_qty
consumed_qty += qty
cost += product.cost_price * Decimal(str(qty))
return cost
2012-09-24 22:17:37 +02:00
@classmethod
def copy(cls, moves, default=None):
2012-03-28 17:48:07 +02:00
if default is None:
default = {}
2018-09-01 01:02:03 +02:00
else:
default = default.copy()
for prefix in ('in_', 'out_'):
default.setdefault(prefix + 'anglo_saxon_quantity',
getattr(cls, 'default_%sanglo_saxon_quantity' % prefix)())
2025-04-04 00:45:18 +02:00
return super().copy(moves, default=default)
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, moves):
for move in moves:
if move.in_anglo_saxon_quantity or move.out_anglo_saxon_quantity:
raise AccessError(
gettext('account_stock_anglo_saxon'
'.msg_move_cancel_anglo_saxon',
move=move.rec_name))
super().cancel(moves)
@classmethod
def check_modification(cls, mode, moves, values=None, external=False):
super().check_modification(
mode, moves, values=values, external=external)
if mode == 'delete':
for move in moves:
if (move.in_anglo_saxon_quantity
or move.out_anglo_saxon_quantity):
raise AccessError(gettext(
'account_stock_anglo_saxon'
'.msg_move_delete_anglo_saxon',
move=move.rec_name))