first commit
This commit is contained in:
182
modules/purchase_shipment_cost/stock.py
Normal file
182
modules/purchase_shipment_cost/stock.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from decimal import ROUND_DOWN, ROUND_HALF_EVEN, Decimal
|
||||
from operator import itemgetter
|
||||
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.modules.product import price_digits, round_price
|
||||
from trytond.modules.stock_shipment_cost import ShipmentCostMixin
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
|
||||
|
||||
class ShipmentIn(ShipmentCostMixin, metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in'
|
||||
|
||||
@fields.depends('state')
|
||||
def on_change_with_shipment_cost_readonly(self, name=None):
|
||||
return self.state != 'draft'
|
||||
|
||||
@property
|
||||
def shipment_cost_moves(self):
|
||||
return [
|
||||
m for m in self.incoming_moves
|
||||
if m.state not in {'done', 'cancelled'}]
|
||||
|
||||
def allocate_cost_by_value(self):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
cost = self._get_shipment_cost()
|
||||
if not cost:
|
||||
return
|
||||
|
||||
moves = self.shipment_cost_moves
|
||||
|
||||
sum_value = 0
|
||||
unit_prices = {}
|
||||
for move in moves:
|
||||
unit_price = Currency.compute(move.currency, move.unit_price,
|
||||
self.company.currency, round=False)
|
||||
unit_prices[move.id] = unit_price
|
||||
sum_value += unit_price * Decimal(str(move.quantity))
|
||||
|
||||
costs = []
|
||||
digit = Move.unit_price.digits[1]
|
||||
exp = Decimal(str(10.0 ** -digit))
|
||||
difference = cost
|
||||
for move in moves:
|
||||
quantity = Decimal(str(move.quantity))
|
||||
if not sum_value:
|
||||
move_cost = cost / Decimal(len(moves))
|
||||
else:
|
||||
move_cost = cost * quantity * unit_prices[move.id] / sum_value
|
||||
unit_shipment_cost = round_price(
|
||||
move_cost / quantity, rounding=ROUND_DOWN)
|
||||
costs.append({
|
||||
'unit_shipment_cost': unit_shipment_cost,
|
||||
'difference': move_cost - (unit_shipment_cost * quantity),
|
||||
'move': move,
|
||||
})
|
||||
difference -= unit_shipment_cost * quantity
|
||||
costs.sort(key=itemgetter('difference'))
|
||||
for cost in costs:
|
||||
move = cost['move']
|
||||
quantity = Decimal(str(move.quantity))
|
||||
if exp * quantity < difference:
|
||||
cost['unit_shipment_cost'] += exp
|
||||
difference -= exp * quantity
|
||||
if difference < exp:
|
||||
break
|
||||
|
||||
for cost in costs:
|
||||
move = cost['move']
|
||||
unit_shipment_cost = Currency.compute(
|
||||
self.company.currency, cost['unit_shipment_cost'],
|
||||
move.currency, round=False)
|
||||
unit_shipment_cost = round_price(
|
||||
unit_shipment_cost, rounding=ROUND_HALF_EVEN)
|
||||
move.unit_price += unit_shipment_cost
|
||||
move.unit_shipment_cost = unit_shipment_cost
|
||||
Move.save(moves)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('received')
|
||||
def receive(cls, shipments):
|
||||
Carrier = Pool().get('carrier')
|
||||
for shipment in shipments:
|
||||
shipment.cost = shipment.cost_used
|
||||
shipment.cost_currency = shipment.cost_currency_used
|
||||
if shipment.carrier:
|
||||
allocation_method = \
|
||||
shipment.carrier.carrier_cost_allocation_method
|
||||
else:
|
||||
allocation_method = \
|
||||
Carrier.default_carrier_cost_allocation_method()
|
||||
getattr(shipment, 'allocate_cost_by_%s' % allocation_method)()
|
||||
super().receive(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
def cancel(cls, shipments):
|
||||
for shipment in shipments:
|
||||
shipment.cost = None
|
||||
shipment.cost_currency = None
|
||||
cls.save(shipments)
|
||||
super().cancel(shipments)
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
unit_shipment_cost = fields.Numeric(
|
||||
"Unit Shipment Cost", digits=price_digits, readonly=True,
|
||||
states={
|
||||
'invisible': ~Eval('unit_shipment_cost'),
|
||||
})
|
||||
|
||||
def _compute_unit_price(self, unit_price):
|
||||
if self.unit_shipment_cost:
|
||||
unit_price -= self.unit_shipment_cost
|
||||
unit_price = super()._compute_unit_price(unit_price)
|
||||
if self.unit_shipment_cost:
|
||||
unit_price += self.unit_shipment_cost
|
||||
return unit_price
|
||||
|
||||
def _compute_component_unit_price(self, unit_price):
|
||||
if self.unit_shipment_cost:
|
||||
unit_price -= self.unit_shipment_cost
|
||||
unit_price = super()._compute_component_unit_price(unit_price)
|
||||
if self.unit_shipment_cost:
|
||||
unit_price += self.unit_shipment_cost
|
||||
return unit_price
|
||||
|
||||
# Split the shipment cost if account_stock_continental is activated
|
||||
def _get_account_stock_move_lines(self, type_):
|
||||
pool = Pool()
|
||||
AccountMoveLine = pool.get('account.move.line')
|
||||
Currency = pool.get('currency.currency')
|
||||
move_lines = super()._get_account_stock_move_lines(type_)
|
||||
if (type_.startswith('in_')
|
||||
and self.unit_shipment_cost
|
||||
and self.shipment
|
||||
and self.shipment.carrier):
|
||||
shipment_cost = Currency.compute(self.currency,
|
||||
Decimal(str(self.quantity)) * self.unit_shipment_cost,
|
||||
self.company.currency)
|
||||
shipment_cost_account = \
|
||||
self.shipment.carrier.carrier_product.account_expense_used
|
||||
account = self.product.account_stock_in_used
|
||||
for move_line in move_lines:
|
||||
if move_line.account == account:
|
||||
move_line.credit -= shipment_cost
|
||||
shipment_cost_line = AccountMoveLine(
|
||||
debit=Decimal('0'),
|
||||
credit=shipment_cost,
|
||||
account=shipment_cost_account,
|
||||
)
|
||||
move_lines.append(shipment_cost_line)
|
||||
break
|
||||
else:
|
||||
raise AssertionError('missing account_stock_supplier')
|
||||
return move_lines
|
||||
|
||||
# Remove shipment cost if account_stock_anglo_saxon is activated
|
||||
@classmethod
|
||||
def _get_anglo_saxon_move(cls, moves, quantity, type_):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
Uom = pool.get('product.uom')
|
||||
for move, qty, cost_price in super()._get_anglo_saxon_move(
|
||||
moves, quantity, type_):
|
||||
if (type_.startswith('in_')
|
||||
and move.unit_shipment_cost):
|
||||
shipment_cost = Uom.compute_price(
|
||||
move.unit, move.unit_shipment_cost,
|
||||
move.product.default_uom)
|
||||
shipment_cost = Currency.compute(move.currency,
|
||||
shipment_cost, move.company.currency)
|
||||
cost_price -= shipment_cost
|
||||
yield move, qty, cost_price
|
||||
Reference in New Issue
Block a user