Files
2026-03-14 09:42:12 +00:00

239 lines
9.1 KiB
Python

# 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 Decimal
from trytond.i18n import gettext
from trytond.model import fields
from trytond.modules.product import round_price
from trytond.modules.stock.exceptions import MoveValidationError
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval
from trytond.transaction import Transaction
class Configuration(metaclass=PoolMeta):
__name__ = 'stock.configuration'
@classmethod
def __setup__(cls):
super().__setup__()
cls.shipment_internal_transit.domain = [
cls.shipment_internal_transit.domain,
('cost_warehouse', '=', None),
]
class ConfigurationLocation(metaclass=PoolMeta):
__name__ = 'stock.configuration.location'
@classmethod
def __setup__(cls):
super().__setup__()
cls.shipment_internal_transit.domain = [
cls.shipment_internal_transit.domain,
('cost_warehouse', '=', None),
]
class Location(metaclass=PoolMeta):
__name__ = 'stock.location'
cost_warehouse = fields.Many2One(
'stock.location', "Cost Warehouse",
domain=[
('type', '=', 'warehouse'),
],
states={
'invisible': (
(Eval('type') != 'storage')
| Bool(Eval('warehouse'))
| (Eval('id', -1) < 0)),
})
class Move(metaclass=PoolMeta):
__name__ = 'stock.move'
@property
@fields.depends('from_location')
def from_cost_warehouse(self):
if self.from_location:
return (
self.from_location.warehouse
or self.from_location.cost_warehouse)
@property
@fields.depends('to_location')
def to_cost_warehouse(self):
if self.to_location:
return (
self.to_location.warehouse
or self.to_location.cost_warehouse)
@property
def cost_warehouse(self):
return self.from_cost_warehouse or self.to_cost_warehouse
@fields.depends(
'company', methods=['from_cost_warehouse', 'to_cost_warehouse'])
def on_change_with_unit_price_required(self, name=None):
required = super().on_change_with_unit_price_required(name=name)
if (self.company and self.company.cost_price_warehouse
and self.from_cost_warehouse != self.to_cost_warehouse):
required = True
return required
@fields.depends(
'company', methods=['from_cost_warehouse', 'to_cost_warehouse'])
def on_change_with_cost_price_required(self, name=None):
required = super().on_change_with_cost_price_required(name=name)
if (self.company and self.company.cost_price_warehouse
and self.from_cost_warehouse != self.to_cost_warehouse):
required = True
return required
@classmethod
def get_unit_price_company(cls, moves, name):
pool = Pool()
ShipmentInternal = pool.get('stock.shipment.internal')
Uom = pool.get('product.uom')
prices = super().get_unit_price_company(moves, name)
for move in moves:
if (move.company.cost_price_warehouse
and move.from_cost_warehouse != move.to_cost_warehouse
and move.to_cost_warehouse
and isinstance(move.shipment, ShipmentInternal)):
cost = total_qty = 0
for outgoing_move in move.shipment.outgoing_moves:
if outgoing_move.product == move.product:
qty = Uom.compute_qty(
outgoing_move.unit, outgoing_move.quantity,
move.product.default_uom)
qty = Decimal(str(qty))
cost += qty * outgoing_move.cost_price
total_qty += qty
if cost and total_qty:
cost_price = round_price(cost / total_qty)
prices[move.id] = cost_price
return prices
def get_cost_price(self, product_cost_price=None):
pool = Pool()
ShipmentInternal = pool.get('stock.shipment.internal')
cost_price = super().get_cost_price(
product_cost_price=product_cost_price)
if (self.company.cost_price_warehouse
and self.from_cost_warehouse != self.to_cost_warehouse
and self.to_cost_warehouse
and isinstance(self.shipment, ShipmentInternal)):
cost_price = self.unit_price_company
return cost_price
@classmethod
def validate(cls, moves):
pool = Pool()
Configuration = pool.get('stock.configuration')
super().validate(moves)
config = Configuration(1)
transit_locations = {}
for move in moves:
if move.state in {'staging', 'draft'}:
continue
company = move.company
if company not in transit_locations:
transit_location = config.get_multivalue(
'shipment_internal_transit', company=company)
transit_locations[company] = transit_location
else:
transit_location = transit_locations[company]
if (company.cost_price_warehouse
and move.from_location.type == 'storage'
and move.from_location != transit_location
and move.to_location.type == 'storage'
and move.to_location != transit_location):
if move.from_cost_warehouse != move.to_cost_warehouse:
raise MoveValidationError(gettext(
'product_cost_warehouse'
'.msg_move_storage_location_same_warehouse',
from_=move.from_location.rec_name,
to=move.to_location.rec_name))
def _do(self):
cost_price, to_save = super()._do()
if (self.company.cost_price_warehouse
and self.from_location.type == 'storage'
and self.to_location.type == 'storage'
and self.from_cost_warehouse != self.to_cost_warehouse):
if self.from_cost_warehouse:
cost_price = self._compute_product_cost_price('out')
elif self.to_cost_warehouse:
cost_price = self._compute_product_cost_price(
'in', self.unit_price_company)
return cost_price, to_save
@property
def _cost_price_pattern(self):
pattern = super()._cost_price_pattern
if self.company.cost_price_warehouse:
pattern['warehouse'] = (
self.cost_warehouse.id if self.cost_warehouse else None)
return pattern
def _cost_price_key(self):
key = super()._cost_price_key()
if self.company.cost_price_warehouse:
key += (('warehouse',
(self.cost_warehouse.id if self.cost_warehouse else None)),
)
return key
@classmethod
def _cost_price_context(cls, moves):
pool = Pool()
Location = pool.get('stock.location')
context = super()._cost_price_context(moves)
if moves[0].company.cost_price_warehouse:
warehouse = moves[0].cost_warehouse
locations = Location.search([
('type', '=', 'storage'),
['OR',
('parent', 'child_of',
warehouse.id if warehouse else []),
('cost_warehouse', '=',
warehouse.id if warehouse else None),
],
])
context['locations'] = [l.id for l in locations]
return context
def get_fifo_move(self, quantity=0.0, date=None):
warehouse = self.cost_warehouse.id if self.cost_warehouse else None
with Transaction().set_context(warehouse=warehouse):
return super().get_fifo_move(quantity=quantity, date=date)
def _get_account_stock_move_type(self):
type_ = super()._get_account_stock_move_type()
if (self.company.cost_price_warehouse
and self.from_location.type == 'storage'
and self.to_location.type == 'storage'
and self.from_cost_warehouse != self.to_cost_warehouse):
if self.from_cost_warehouse and not self.to_cost_warehouse:
type_ = 'out_warehouse'
elif not self.from_cost_warehouse and self.to_cost_warehouse:
type_ = 'in_warehouse'
return type_
class ShipmentInternal(metaclass=PoolMeta):
__name__ = 'stock.shipment.internal'
@fields.depends('company')
def on_change_with_transit_location(self, name=None):
pool = Pool()
Config = pool.get('stock.configuration')
location = super().on_change_with_transit_location(name=name)
if not location and self.company and self.company.cost_price_warehouse:
location = Config(1).get_multivalue(
'shipment_internal_transit', company=self.company)
return location