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 / ship_date.py
Size: Mime:
import abc
import logging
from datetime import datetime, timezone
from typing import Optional

from lib_b2b.change import ChangeRecord, ChangeDataProvider
from lib_b2b.erp import ERP
from lib_b2b.errors import ChangeNotAllowed, ErrorRecord, ShipDateChangeError, VersionConflictError
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 OrderShipDateChangeDataProvider(ChangeDataProvider, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def new_ship_date(self) -> Optional[datetime]:
        raise NotImplementedError


class OrderShipDateChangeRequest(OrderChangeRequest):
    # TODO: - Move these policies to be data driven
    _policy = Policy(
        name='shipdate_change_policy',
        subject=Policy.SUBJECT_ALL,
        predicates=[
            SimpleOrderPredicate(
                lambda o: o.status < OrderStatus.SHIPPED,
                'The ship date can only be changed until the order has shipped.')
        ]
    )

    def __init__(self, on_behalf_of: str, change_data_provider: OrderShipDateChangeDataProvider):
        super().__init__(on_behalf_of, OrderShipDateChangeRequest._policy)
        self.change_data_provider = change_data_provider

    def will_change(self, order: Order) -> bool:
        if self.change_data_provider.new_ship_date:
            return order.ship_date != self.change_data_provider.new_ship_date
        return False

    def apply(self, order: Order):
        try:
            self.changeable_object = order
            if super().permitted(order):
                if self.will_change(order):
                    new_ship_date_utc = self.change_data_provider.new_ship_date.astimezone(timezone.utc)
                    start_of_today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
                    if new_ship_date_utc >= start_of_today:
                        erp = ERP.for_(order.customer_edi_id)
                        erp_customer = erp.fetch_customer(order.customer_edi_id)

                        if order.status in (OrderStatus.SHIPPED,
                                            OrderStatus.PARTIALLY_SHIPPED,
                                            OrderStatus.CANCELLED,
                                            OrderStatus.REJECTED):
                            raise ChangeNotAllowed(f"Unable to change the ship date on an order that is in status: {order.status.value}")

                        order.set_not_before_date(self.change_data_provider.new_ship_date,
                                                  version=self.change_data_provider.version())
                        order.recalculate_dates()

                        order.record(ChangeRecord(before=self.changeable_object.ship_date.isoformat(),
                                                  after=self.change_data_provider.new_ship_date.isoformat(),
                                                  description="Ship date was updated", when=datetime.now(),
                                                  who=self.on_behalf_of)
                                     )

                        if order.status is OrderStatus.ENTERED:
                            try:
                                erp.update_ship_date(ccn=erp_customer.ccn,
                                                     sales_order=self.changeable_object['glovia_sales_order'],
                                                     new_ship_date=self.change_data_provider.new_ship_date)
                                order.record(ChangeRecord(before=self.changeable_object.ship_date.isoformat(),
                                                          after=self.change_data_provider.new_ship_date.isoformat(),
                                                          description="ERP required and promise dates were updated",
                                                          when=datetime.now(), who=self.on_behalf_of)
                                             )
                            except Exception as e:
                                order.error(ErrorRecord(code='ORDER-32', msg=f"Unable to update ERP with new "
                                                                             f"ship date.[{str(e)}]"))
                                raise ShipDateChangeError(f"Unable to update ERP with new ship date.[{str(e)}]")

                        else:
                            logger.info(f"Order Ship Date Change handler does not need to modify Glovia for "
                                        f"order {order.order_id} with status {order.status.value}")
                    else:
                        raise ShipDateChangeError(f"Unable to set ship date to a value less than today: {start_of_today.isoformat()} change value: {new_ship_date_utc.isoformat()}")
        except ValueError as ve:
            raise VersionConflictError(str(ve))