All checks were successful
Deploy MES Core / deploy (push) Successful in 11s
71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
from django.db import transaction
|
||
from django.utils import timezone
|
||
|
||
from warehouse.models import StockItem, TransferLine, TransferRecord
|
||
|
||
|
||
@transaction.atomic
|
||
def receive_transfer(transfer_id: int, receiver_id: int) -> None:
|
||
"""
|
||
Строгое перемещение: принять TransferRecord.
|
||
|
||
Логика:
|
||
- если уже received -> идемпотентно выходим
|
||
- блокируем TransferRecord
|
||
- блокируем связанные StockItem
|
||
- обновляем location на to_location
|
||
- ставим receiver/received_at/status
|
||
"""
|
||
tr = (
|
||
TransferRecord.objects.select_for_update()
|
||
.select_related('from_location', 'to_location')
|
||
.get(pk=transfer_id)
|
||
)
|
||
|
||
if tr.is_applied:
|
||
return
|
||
|
||
lines = list(TransferLine.objects.filter(transfer=tr).select_related('stock_item', 'stock_item__location', 'stock_item__material', 'stock_item__entity'))
|
||
if not lines:
|
||
raise RuntimeError('В перемещении нет строк.')
|
||
|
||
for ln in lines:
|
||
if float(ln.quantity) <= 0:
|
||
continue
|
||
|
||
src = StockItem.objects.select_for_update().get(pk=ln.stock_item_id)
|
||
if src.location_id != tr.from_location_id:
|
||
raise RuntimeError('Единица на складе находится не на складе-источнике.')
|
||
|
||
if float(ln.quantity) > float(src.quantity):
|
||
raise RuntimeError('Недостаточно количества в источнике для перемещения.')
|
||
|
||
if src.unique_id and float(ln.quantity) != float(src.quantity):
|
||
raise RuntimeError('Нельзя частично перемещать позицию с ID/маркировкой.')
|
||
|
||
if float(ln.quantity) == float(src.quantity):
|
||
src.location_id = tr.to_location_id
|
||
src.created_at = timezone.now()
|
||
src.save(update_fields=['location', 'created_at'])
|
||
continue
|
||
|
||
src.quantity = float(src.quantity) - float(ln.quantity)
|
||
src.save(update_fields=['quantity'])
|
||
|
||
# ВАЖНО: не объединяем с существующими позициями на складе-получателе,
|
||
# чтобы сохранялась история поступлений/дат/партий.
|
||
StockItem.objects.create(
|
||
material=src.material,
|
||
entity=src.entity,
|
||
location_id=tr.to_location_id,
|
||
quantity=float(ln.quantity),
|
||
is_remnant=src.is_remnant,
|
||
current_length=src.current_length,
|
||
current_width=src.current_width,
|
||
)
|
||
|
||
tr.status = 'received'
|
||
tr.receiver_id = receiver_id
|
||
tr.received_at = timezone.now()
|
||
tr.is_applied = True
|
||
tr.save(update_fields=['status', 'receiver_id', 'received_at', 'is_applied']) |