first commit
This commit is contained in:
241
modules/product_kit/stock.py
Normal file
241
modules/product_kit/stock.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# 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.model import ModelView, Workflow, fields
|
||||
from trytond.modules.product import round_price
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class ShipmentIn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('received')
|
||||
def receive(cls, shipments):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if isinstance(move.origin, PurchaseLineComponent):
|
||||
move.origin.check_move_quantity()
|
||||
super().receive(shipments)
|
||||
|
||||
|
||||
class ShipmentOutReturn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out.return'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('received')
|
||||
def receive(cls, shipments):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
for shipment in shipments:
|
||||
for move in shipment.incoming_moves:
|
||||
if isinstance(move.origin, SaleLineComponent):
|
||||
move.origin.check_move_quantity()
|
||||
super().receive(shipments)
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
|
||||
def _compute_component_unit_price(self, unit_price):
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
UoM = pool.get('product.uom')
|
||||
amount, quantity = 0, 0
|
||||
for line in self.origin.line.invoice_lines:
|
||||
if line.invoice and line.invoice.state in {'posted', 'paid'}:
|
||||
with Transaction().set_context(date=self.effective_date):
|
||||
amount += Currency.compute(
|
||||
line.invoice.currency, line.amount, self.currency)
|
||||
quantity += UoM.compute_qty(
|
||||
line.unit, line.quantity, self.origin.line.unit)
|
||||
amount *= self.origin.price_ratio
|
||||
if quantity:
|
||||
unit_price = amount / Decimal(str(quantity))
|
||||
if unit_price is not None:
|
||||
unit_price = UoM.compute_price(
|
||||
self.origin.unit, unit_price, self.unit)
|
||||
unit_price = round_price(unit_price)
|
||||
return unit_price
|
||||
|
||||
|
||||
class MoveSale(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + ['sale.line.component']
|
||||
|
||||
def get_sale(self, name):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
sale = super().get_sale(name)
|
||||
if isinstance(self.origin, SaleLineComponent):
|
||||
sale = self.origin.line.sale.id
|
||||
return sale
|
||||
|
||||
@classmethod
|
||||
def search_sale(cls, name, clause):
|
||||
domain = super().search_sale(name, clause)
|
||||
return ['OR',
|
||||
domain,
|
||||
('origin.line.' + clause[0],
|
||||
*clause[1:3], 'sale.line.component', *clause[3:]),
|
||||
]
|
||||
|
||||
def get_sale_exception_state(self, name):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
state = super().get_sale_exception_state(name)
|
||||
if isinstance(self.origin, SaleLineComponent):
|
||||
if self in self.origin.moves_recreated:
|
||||
state = 'recreated'
|
||||
elif self in self.origin.moves_ignored:
|
||||
state = 'ignored'
|
||||
return state
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
category = super().on_change_with_product_uom_category(name=name)
|
||||
# Enforce the same unit category as they are used to compute the
|
||||
# remaining quantity to ship and the quantity to invoice.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, SaleLineComponent)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
def get_cost_price(self, product_cost_price=None):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
Sale = pool.get('sale.sale')
|
||||
# For return sale's move use the cost price of the original sale
|
||||
if (isinstance(self.origin, SaleLineComponent)
|
||||
and self.origin.quantity < 0
|
||||
and self.from_location.type != 'storage'
|
||||
and self.to_location.type == 'storage'
|
||||
and isinstance(self.origin.line.origin, Sale)):
|
||||
sale = self.origin.line.origin
|
||||
cost = Decimal(0)
|
||||
qty = Decimal(0)
|
||||
for move in sale.moves:
|
||||
if (move.state == 'done'
|
||||
and move.from_location.type == 'storage'
|
||||
and move.to_location.type == 'customer'
|
||||
and move.product == self.product):
|
||||
move_quantity = Decimal(str(move.internal_quantity))
|
||||
cost_price = move.get_cost_price(
|
||||
product_cost_price=move.cost_price)
|
||||
qty += move_quantity
|
||||
cost += cost_price * move_quantity
|
||||
if qty:
|
||||
product_cost_price = round_price(cost / qty)
|
||||
return super().get_cost_price(product_cost_price=product_cost_price)
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
name = super().origin_name
|
||||
if isinstance(self.origin, SaleLineComponent) and self.origin.id >= 0:
|
||||
name = self.origin.line.sale.rec_name
|
||||
return name
|
||||
|
||||
def _compute_unit_price(self, unit_price):
|
||||
pool = Pool()
|
||||
SaleLineComponent = pool.get('sale.line.component')
|
||||
unit_price = super()._compute_unit_price(unit_price)
|
||||
if isinstance(self.origin, SaleLineComponent):
|
||||
unit_price = self._compute_component_unit_price(unit_price)
|
||||
return unit_price
|
||||
|
||||
|
||||
class MovePurchase(metaclass=PoolMeta):
|
||||
__name__ = 'stock.move'
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + ['purchase.line.component']
|
||||
|
||||
def get_purchase(self, name):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
purchase = super().get_purchase(name)
|
||||
if isinstance(self.origin, PurchaseLineComponent):
|
||||
purchase = self.origin.line.purchase.id
|
||||
return purchase
|
||||
|
||||
@classmethod
|
||||
def search_purchase(cls, name, clause):
|
||||
domain = super().search_purchase(name, clause)
|
||||
return ['OR',
|
||||
domain,
|
||||
('origin.line.' + clause[0],
|
||||
*clause[1:3], 'purchase.line.component', *clause[3:]),
|
||||
]
|
||||
|
||||
def get_supplier(self, name):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
supplier = super().get_supplier(name)
|
||||
if isinstance(self.origin, PurchaseLineComponent):
|
||||
supplier = self.origin.line.purchase.party.id
|
||||
return supplier
|
||||
|
||||
@classmethod
|
||||
def search_supplier(cls, name, clause):
|
||||
domain = super().search_supplier(name, clause)
|
||||
return ['OR',
|
||||
domain,
|
||||
('origin.line.purchase.party' + clause[0][len(name):],
|
||||
*clause[1:3], 'purchase.line.component', *clause[3:])]
|
||||
|
||||
def get_purchase_exception_state(self, name):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
state = super().get_purchase_exception_state(name)
|
||||
if isinstance(self.origin, PurchaseLineComponent):
|
||||
if self in self.origin.moves_recreated:
|
||||
state = 'recreated'
|
||||
elif self in self.origin.moves_ignored:
|
||||
state = 'ignored'
|
||||
return state
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_product_uom_category(self, name=None):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
category = super().on_change_with_product_uom_category(name=name)
|
||||
# Enforce the same unit category as they are used to compute the
|
||||
# remaining quantity to ship and the quantity to invoice.
|
||||
# Use getattr as reference field can have negative id
|
||||
if (isinstance(self.origin, PurchaseLineComponent)
|
||||
and getattr(self.origin, 'unit', None)):
|
||||
category = self.origin.unit.category
|
||||
return category
|
||||
|
||||
@property
|
||||
def origin_name(self):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
name = super().origin_name
|
||||
if (isinstance(self.origin, PurchaseLineComponent)
|
||||
and self.origin.id >= 0):
|
||||
name = self.origin.line.purchase.rec_name
|
||||
return name
|
||||
|
||||
def _compute_unit_price(self, unit_price):
|
||||
pool = Pool()
|
||||
PurchaseLineComponent = pool.get('purchase.line.component')
|
||||
unit_price = super()._compute_unit_price(unit_price)
|
||||
if isinstance(self.origin, PurchaseLineComponent):
|
||||
unit_price = self._compute_component_unit_price(unit_price)
|
||||
return unit_price
|
||||
Reference in New Issue
Block a user