Repository URL to install this package:
|
Version:
0.4.184 ▾
|
import abc
import logging
from datetime import datetime
from dateutil.tz import UTC
from lib_b2b.address import Address
from lib_b2b.change import ChangeRecord, ChangeDataProvider
from lib_b2b.erp import ERP
from lib_b2b.errors import OrderNotFoundError, NotFoundError, ErrorRecord, TaxLocationNotFoundError, \
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 OrderAddressChangeDataProvider(ChangeDataProvider, metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def new_address(self) -> Address:
raise NotImplementedError
class OrderAddressChangeRequest(OrderChangeRequest):
# TODO: - Move these policies to be data driven
_policy = Policy(
name='address_change_policy',
subject=Policy.SUBJECT_ALL,
predicates=[
SimpleOrderPredicate(
lambda o: o.status < OrderStatus.PARTIALLY_SHIPPED,
'Address change is not allowed after shipping.')
]
)
def __init__(self, on_behalf_of: str, change_data_provider: OrderAddressChangeDataProvider):
super().__init__(on_behalf_of, OrderAddressChangeRequest._policy)
self.change_data_provider = change_data_provider
def will_change(self, order: Order) -> bool:
return order.ship_to != self.change_data_provider.new_address
def apply(self, order: Order):
try:
self.changeable_object = order
if super().permitted(order):
if self.will_change(order):
order.set_ship_to(self.change_data_provider.new_address,
version=self.change_data_provider.version())
order.record(
ChangeRecord(before=self.changeable_object.ship_to.as_dict(),
after=self.change_data_provider.new_address.as_dict(),
description="Ship to address was updated", when=datetime.now().astimezone(UTC),
who=self.on_behalf_of)
)
erp = ERP.for_(order.customer_edi_id)
if order.status is OrderStatus.ACCEPTED:
# if it's accepted, we can't restart the order flow, so, we need to
# make sure it is not entered and modify the adapter tables
try:
order.update_erp_reference()
except OrderNotFoundError:
# in this case let's reset the order in the adapter tables
erp.add(order)
elif order.status < OrderStatus.ACCEPTED:
# Force the order to go back through validation,
# this will restart the order flow and modify the glovia order
# setting it to NONE first ensures that the listener picks it up as a change
order.status = OrderStatus.NONE
order.status = OrderStatus.RECEIVED
elif order.status < OrderStatus.SHIPPED:
try:
erp_customer = erp.fetch_customer(order.customer_edi_id)
tax_loc = erp.find_tax_location_code(customer_edi_id=order.customer_edi_id,
ship_to=order.ship_to)
erp.update_address(ccn=erp_customer.ccn,
sales_order=self.changeable_object['glovia_sales_order'],
new_ship_address=self.change_data_provider.new_address,
tax_location_code=tax_loc)
order.record(
ChangeRecord(before=self.changeable_object.ship_to.as_dict(),
after=self.change_data_provider.new_address.as_dict(),
description=f"Address change caused ERP tax location change to {tax_loc}.",
when=datetime.now().astimezone(UTC), who=self.on_behalf_of)
)
except NotFoundError:
order.error(ErrorRecord(code="ADDR-4",
msg=f"Unable to find tax location for address change.",
extra_data=self.change_data_provider.new_address.as_dict()))
raise TaxLocationNotFoundError(f"Unable to find tax location for address change with postal"
f" code {self.change_data_provider.new_address.postalCode5}")
else:
logger.info(f"Order Address Change handler does not need to modify Glovia for "
f"order {order.order_id} with status {order.status.value}")
except ValueError as ve:
raise VersionConflictError(str(ve))