Files
tryton/modules/purchase/stock.py
T

250 lines
8.4 KiB
Python
Raw Normal View History

2014-12-02 12:55:47 +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.
2018-07-27 09:27:49 +02:00
from functools import wraps
2018-12-29 14:20:29 +01:00
from trytond.i18n import gettext
2021-12-11 17:58:43 +01:00
from trytond.model import ModelView, Workflow, fields
2018-12-29 14:20:29 +01:00
from trytond.model.exceptions import AccessError
from trytond.pool import Pool, PoolMeta
2021-12-11 17:58:43 +01:00
from trytond.pyson import Eval
from trytond.transaction import Transaction, without_check_access
2018-07-27 09:27:49 +02:00
def process_purchase(moves_field):
def _process_purchase(func):
@wraps(func)
def wrapper(cls, shipments):
pool = Pool()
Purchase = pool.get('purchase.purchase')
2022-03-09 00:57:17 +01:00
transaction = Transaction()
context = transaction.context
with without_check_access():
2018-07-27 09:27:49 +02:00
purchases = set(m.purchase for s in cls.browse(shipments)
for m in getattr(s, moves_field) if m.purchase)
func(cls, shipments)
if purchases:
2022-03-09 00:57:17 +01:00
with transaction.set_context(
queue_batch=context.get('queue_batch', True)):
Purchase.__queue__.process(purchases)
2018-07-27 09:27:49 +02:00
return wrapper
return _process_purchase
2018-06-26 19:11:51 +02:00
class ShipmentIn(metaclass=PoolMeta):
__name__ = 'stock.shipment.in'
@classmethod
def __setup__(cls):
super(ShipmentIn, cls).__setup__()
add_remove = [
('supplier', '=', Eval('supplier')),
]
if not cls.incoming_moves.add_remove:
cls.incoming_moves.add_remove = add_remove
else:
cls.incoming_moves.add_remove = [
add_remove,
cls.incoming_moves.add_remove,
]
cls.incoming_moves.depends.add('supplier')
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, shipments):
PurchaseLine = Pool().get('purchase.line')
for shipment in shipments:
for move in shipment.incoming_moves:
2020-06-06 10:30:11 +01:00
if (move.state == 'cancelled'
and isinstance(move.origin, PurchaseLine)):
2018-12-29 14:20:29 +01:00
raise AccessError(
gettext('purchase.msg_purchase_move_reset_draft',
move=move.rec_name))
return super(ShipmentIn, cls).draft(shipments)
2018-07-27 09:27:49 +02:00
@classmethod
@ModelView.button
@Workflow.transition('received')
@process_purchase('incoming_moves')
def receive(cls, shipments):
pool = Pool()
PurchaseLine = pool.get('purchase.line')
for shipment in shipments:
for move in shipment.incoming_moves:
if isinstance(move.origin, PurchaseLine):
move.origin.check_move_quantity()
2018-07-27 09:27:49 +02:00
super(ShipmentIn, cls).receive(shipments)
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
2018-07-27 09:27:49 +02:00
@process_purchase('incoming_moves')
def cancel(cls, shipments):
super(ShipmentIn, cls).cancel(shipments)
2018-06-26 19:11:51 +02:00
class ShipmentInReturn(metaclass=PoolMeta):
__name__ = 'stock.shipment.in.return'
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, shipments):
PurchaseLine = Pool().get('purchase.line')
for shipment in shipments:
for move in shipment.moves:
2020-06-06 10:30:11 +01:00
if (move.state == 'cancelled'
and isinstance(move.origin, PurchaseLine)):
2018-12-29 14:20:29 +01:00
raise AccessError(
gettext('purchase.msg_purchase_move_reset_draft',
move=move.rec_name))
return super(ShipmentInReturn, cls).draft(shipments)
2018-07-27 09:27:49 +02:00
@classmethod
@ModelView.button
@Workflow.transition('done')
@process_purchase('moves')
def done(cls, shipments):
super(ShipmentInReturn, cls).done(shipments)
def process_purchase_move(without_shipment=False):
def _process_purchase_move(func):
@wraps(func)
def wrapper(cls, moves):
pool = Pool()
Purchase = pool.get('purchase.purchase')
2022-03-09 00:57:17 +01:00
transaction = Transaction()
context = transaction.context
with without_check_access():
p_moves = cls.browse(moves)
if without_shipment:
p_moves = [m for m in p_moves if not m.shipment]
purchases = set(m.purchase for m in p_moves if m.purchase)
func(cls, moves)
if purchases:
2022-03-09 00:57:17 +01:00
with transaction.set_context(
queue_batch=context.get('queue_batch', True)):
Purchase.__queue__.process(purchases)
return wrapper
return _process_purchase_move
2018-07-27 09:27:49 +02:00
2018-06-26 19:11:51 +02:00
class Move(metaclass=PoolMeta):
__name__ = 'stock.move'
2019-04-11 19:57:25 +02:00
purchase = fields.Function(
fields.Many2One('purchase.purchase', 'Purchase'),
'get_purchase', searcher='search_purchase')
supplier = fields.Function(fields.Many2One(
'party.party', 'Supplier',
context={
'company': Eval('company', -1),
},
depends={'company'}),
'get_supplier', searcher='search_supplier')
purchase_exception_state = fields.Function(fields.Selection([
('', ''),
('ignored', 'Ignored'),
('recreated', 'Recreated'),
], 'Exception State'), 'get_purchase_exception_state')
@classmethod
def _get_origin(cls):
models = super(Move, cls)._get_origin()
models.append('purchase.line')
return models
@classmethod
def check_origin_types(cls):
types = super(Move, cls).check_origin_types()
types.add('supplier')
return types
def get_purchase(self, name):
PurchaseLine = Pool().get('purchase.line')
if isinstance(self.origin, PurchaseLine):
return self.origin.purchase.id
@classmethod
def search_purchase(cls, name, clause):
2017-09-27 18:37:31 +02:00
return [('origin.' + clause[0],) + tuple(clause[1:3])
+ ('purchase.line',) + tuple(clause[3:])]
def get_purchase_exception_state(self, name):
PurchaseLine = Pool().get('purchase.line')
if not isinstance(self.origin, PurchaseLine):
return ''
if self in self.origin.moves_recreated:
return 'recreated'
if self in self.origin.moves_ignored:
return 'ignored'
def get_supplier(self, name):
PurchaseLine = Pool().get('purchase.line')
if isinstance(self.origin, PurchaseLine):
return self.origin.purchase.party.id
@fields.depends('origin')
def on_change_with_product_uom_category(self, name=None):
pool = Pool()
PurchaseLine = pool.get('purchase.line')
category = super(Move, self).on_change_with_product_uom_category(
name=name)
# Enforce the same unit category as they are used to compute the
# remaining quantity to receive and the quantity to invoice.
2018-04-21 12:56:27 +02:00
# Use getattr as reference field can have negative id
if (isinstance(self.origin, PurchaseLine)
and getattr(self.origin, 'unit', None)):
2023-03-26 09:49:00 +02:00
category = self.origin.unit.category
return category
@property
def origin_name(self):
pool = Pool()
PurchaseLine = pool.get('purchase.line')
name = super(Move, self).origin_name
if isinstance(self.origin, PurchaseLine) and self.origin.id >= 0:
2019-05-22 18:37:08 +02:00
name = self.origin.purchase.rec_name
return name
@classmethod
def search_supplier(cls, name, clause):
return [('origin.purchase.party' + clause[0][len(name):],
*clause[1:3], 'purchase.line', *clause[3:])]
@classmethod
@ModelView.button
@Workflow.transition('done')
@process_purchase_move(without_shipment=True)
def do(cls, moves):
super().do(moves)
@classmethod
@ModelView.button
2020-06-06 10:30:11 +01:00
@Workflow.transition('cancelled')
@process_purchase_move(without_shipment=True)
def cancel(cls, moves):
super(Move, cls).cancel(moves)
@classmethod
@process_purchase_move()
def delete(cls, moves):
super(Move, cls).delete(moves)
2018-06-26 19:11:51 +02:00
class Location(metaclass=PoolMeta):
__name__ = 'stock.location'
supplier_return_location = fields.Many2One(
'stock.location', 'Supplier Return',
states={
'invisible': Eval('type') != 'warehouse',
},
domain=[
('type', '=', 'storage'),
('parent', 'child_of', [Eval('id', -1)]),
],
2019-09-30 09:39:27 +02:00
help='If empty the Storage location is used.')