Repository URL to install this package:
|
Version:
0.4.139 ▾
|
import abc
import logging
from datetime import datetime
from lib_b2b.change import ChangeRecord, ChangeDataProvider
from lib_b2b.errors import LineCancelError, ErrorRecord, VersionConflictError
from lib_b2b.order import Order
from lib_b2b.order_change import OrderChangeRequest, SimpleOrderPredicate
from lib_b2b.order_line import OrderLineCancellation, OrderLine
from lib_b2b.order_status import OrderStatus
from lib_b2b.policy import Policy
logger = logging.getLogger(__name__)
class OrderCancelLineChangeDataProvider(ChangeDataProvider, metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def cancellations(self) -> [OrderLineCancellation]:
raise NotImplementedError
class OrderCancelLineChangeRequest(OrderChangeRequest):
# TODO: - Move these policies to be data driven
_policy = Policy(
name='cancel_line_change_policy',
subject=Policy.SUBJECT_ALL,
predicates=[
SimpleOrderPredicate(
lambda o: o.status < OrderStatus.SHIPPED,
'Line cancellation is only allowed prior to shipping.')
]
)
def __init__(self, on_behalf_of: str, change_data_provider: OrderCancelLineChangeDataProvider):
super().__init__(on_behalf_of, OrderCancelLineChangeRequest._policy)
self.change_data_provider = change_data_provider
def will_change(self, order: Order) -> bool:
if order.status is OrderStatus.CANCELLED:
return False
if self.change_data_provider.cancellations:
for cancellation in self.change_data_provider.cancellations:
line = order.line_matching(purchase_order_line=cancellation.purchase_order_line,
channel_line_id=cancellation.channel_line_id)
if line and not line.cancellation_exists(cancellation) and not line.cancelled:
return True
return False
def __cancel_line(self, line: OrderLine, cancellation: OrderLineCancellation):
if line.cancellation_exists(cancellation):
logger.info(f'Line cancellation request {cancellation.cancellation_id} has already '
f'been processed for this line.')
return
if line.fulfilled:
raise LineCancelError(line_id=line.order_line_id,
message=f"Unable to cancel a line that is already fulfilled "
f"[line_id: {line.order_line_id}, "
f"cancellation_id: {cancellation.cancellation_id}]")
if line.cancelled:
logger.warning(f"Attempted to cancel line {line.order_line_id} that was already cancelled.")
return
from lib_b2b.orders import Orders
from lib_b2b.order_status import OrderStatus
order = Orders.for_(cancellation.order_id)
if order.status >= OrderStatus.SHIPPED:
raise LineCancelError(line_id=line.order_line_id,
message=f"Unable to cancel a line on an order that is already shipped or cancelled"
f"[line_id: {line.order_line_id}, "
f"cancellation_id: {cancellation.cancellation_id}]")
# Can we cancel this amount on this line??
if cancellation.cancel_qty > line.remaining_qty:
raise LineCancelError(line.order_line_id, f"Requested quantity {cancellation.cancel_qty} "
f"exceeds the remaining quantity "
f"{line.remaining_qty} for the line "
f"{line.order_line_id}")
else:
line.cancelled_qty = line.cancelled_qty + cancellation.cancel_qty
if line.cancelled_qty >= line.quantity:
line.cancelled = True
line.cancellations.append(cancellation)
order._modify_line(line, version=self.change_data_provider.version())
if order.status >= OrderStatus.ENTERED:
from lib_b2b.erp import ERP
ERP.for_(order.customer_edi_id).cancel_line(line.order_id,
line.order_line_id,
quantity=cancellation.cancel_qty)
else:
logger.info(f"Order not yet in ERP, cancellation there of order "
f"line {line.order_line_id} is unnecessary.")
def apply(self, order: Order):
self.changeable_object = order
if super().permitted(order):
if self.will_change(order):
for cancellation in self.change_data_provider.cancellations:
try:
line = order.line_matching(purchase_order_line=cancellation.purchase_order_line,
channel_line_id=cancellation.channel_line_id)
before_cancellations = list(map(lambda x: x.as_dict(), line.cancellations))
self.__cancel_line(line, cancellation)
after_cancellations = list(map(lambda x: x.as_dict(), line.cancellations))
order.record(ChangeRecord(before=before_cancellations, after=after_cancellations,
description=f"Qty of {cancellation.cancel_qty} cancelled on line {line.order_line_id}",
when=datetime.now(), who=self.on_behalf_of)
)
except LineCancelError as lce:
order.error(ErrorRecord(code="ORDER-64",
msg=lce.sanitized_message))
raise lce
except ValueError as ve:
raise VersionConflictError(str(ve))