Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
lib-py-b2b / order_change / cancel.py
Size: Mime:
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))