Repository URL to install this package:
|
Version:
0.4.184 ▾
|
import abc
import logging
from datetime import datetime
from typing import Optional
from dateutil.tz import UTC
from lib_b2b.change import ChangeRecord, ChangeDataProvider
from lib_b2b.errors import OrderCancelError, ErrorRecord, VersionConflictError, \
ChangeFailedError, ChangeNotAllowed, BusinessError, TechnicalError
from lib_b2b.order import Order
from lib_b2b.order_change import OrderChangeRequest, SimpleOrderPredicate
from lib_b2b.order_status import OrderStatus
from lib_b2b.policy import Policy
logger = logging.getLogger(__name__)
class OrderCancelChangeDataProvider(ChangeDataProvider, metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def cancellation_reason(self) -> Optional[str]:
raise NotImplementedError
@property
@abc.abstractmethod
def cancelled_on(self) -> Optional[datetime]:
raise NotImplementedError
class OrderCancelChangeRequest(OrderChangeRequest):
# TODO: - Move these policies to be data driven
_policy = Policy(
name='cancel_change_policy',
subject=Policy.SUBJECT_ALL,
predicates=[
SimpleOrderPredicate(
lambda o: o.status < OrderStatus.SHIPPED,
'Cancellation is not allowed after shipping.')
]
)
def __init__(self, on_behalf_of: str, change_data_provider: OrderCancelChangeDataProvider):
super().__init__(on_behalf_of, OrderCancelChangeRequest._policy)
self.change_data_provider = change_data_provider
def will_change(self, order: Order) -> bool:
return order.status is not OrderStatus.CANCELLED and self.change_data_provider.cancelled_on
def apply(self, order: Order):
try:
self.changeable_object = order
if super().permitted(order):
if self.will_change(order):
before_status = order.status
if order.status < OrderStatus.SHIPPED:
order.set_status(OrderStatus.CANCELLED,
version=self.change_data_provider.version())
order.modify(
data={'cancellation_reason': self.change_data_provider.cancellation_reason,
'cancelled_at': self.change_data_provider.cancelled_on.isoformat()})
order.record(ChangeRecord(before=before_status.value, after=OrderStatus.CANCELLED.value,
description="Order was Cancelled", when=datetime.now().astimezone(UTC),
who=self.on_behalf_of)
)
if order.status >= OrderStatus.ENTERED:
try:
from lib_b2b.erp import ERP
ERP.for_(order.customer_edi_id).cancel(order.order_id)
order.record(ChangeRecord(before=before_status.value, after=OrderStatus.CANCELLED.value,
description="Order was cancelled in ERP", when=datetime.now().astimezone(UTC),
who=self.on_behalf_of)
)
except OrderCancelError as oce:
order.error(ErrorRecord(code="ORDER-96",
msg=oce.sanitized_message))
raise oce
except (BusinessError, TechnicalError) as be:
order.error(ErrorRecord(code="ORDER-98", msg=be.sanitized_message))
raise be
except Exception as erpe:
order.error(ErrorRecord(code="ORDER-97",
msg=f"{str(erpe)}"))
raise ChangeFailedError(str(erpe)) from erpe
else:
raise ChangeNotAllowed(f'Cancellation of order {order.order_id} with '
f'status {order.status.value} is not allowed.')
except ValueError as ve:
raise VersionConflictError(str(ve))