Repository URL to install this package:
|
Version:
0.4.192 ▾
|
lib-py-b2b
/
shipnotice.py
|
|---|
from os import environ
import datetime
import logging
from boto3 import resource
from botocore.exceptions import ClientError
from dateutil.tz import UTC
from validators import url
from .errors import CapturePaymentFailedError, ShipNoticeFailedError, ShipNoticeFailedWarning
fulfillment_table = environ['fulfillment_table'] if 'fulfillment_table' in environ else 'test.b2b.Fulfillment'
log_level = logging.getLevelName(environ['LOG_LEVEL']) if 'LOG_LEVEL' in environ else logging.INFO
logging.basicConfig()
logger = logging.getLogger()
logger.setLevel(log_level)
MAX_RETRY_COUNT = 5
class ShipNotice:
def __init__(self, fulfillment):
self.fulfillment = fulfillment
self.profile = Profile.profile_for(customer=self.fulfillment['customer_edi_id'])
def __record_status(self, was_successful, msg=None):
try:
dynamodb_resource = resource('dynamodb', region_name='us-east-1')
table = dynamodb_resource.Table(fulfillment_table)
fid = self.fulfillment['id']
sdt = self.fulfillment['ship_date']
logger.debug(f"Updating ship notice status (success: {was_successful}) for fulfillment {fid}")
if was_successful:
response = table.update_item(
Key={'id': fid},
UpdateExpression="set #s = :s, sent_date = :d",
ExpressionAttributeValues={
':s': 'SENT',
':d': datetime.datetime.now().astimezone(UTC).isoformat()
},
ExpressionAttributeNames={
'#s': 'status'
},
ReturnValues="UPDATED_NEW"
)
else:
retry_count = self.fulfillment['retry_count'] if 'retry_count' in self.fulfillment else 0
retry_count = retry_count + 1
response = table.update_item(
Key={'id': fid},
UpdateExpression="set #s = :s, last_error = :m, last_error_date = :d, retry_count = :r",
ExpressionAttributeValues={
':s': 'FAILED',
':m': msg,
':r': retry_count,
':d': datetime.datetime.now().astimezone(UTC).isoformat()
},
ExpressionAttributeNames={
'#s': 'status'
},
ReturnValues="UPDATED_NEW"
)
return response
except ClientError as ce:
logger.error(ce)
if ce.response['Error']['Code'] != 'ConditionalCheckFailedException':
raise ce
except Exception as e:
logger.error(e)
def is_required(self):
try:
if self.profile.integration_config.send_notice_to_url_from_order:
if 'notification_url' in self.fulfillment:
notification_url = self.fulfillment['notification_url']
if url(notification_url) is not True:
return False
else:
return False
retry_count = self.fulfillment['retry_count'] if 'retry_count' in self.fulfillment else 0
if (FulfillmentStatus.for_(self.fulfillment['status']) in (FulfillmentStatus.NOT_SENT, FulfillmentStatus.FAILED)
or 'status' not in self.fulfillment) \
and retry_count < MAX_RETRY_COUNT:
return True
return False
except KeyError:
return False
def cancel(self):
if self.profile.integration_type == 'shopify':
try:
if 'channel_fulfillment_id' in self.fulfillment:
from lib_b2b.shopify import CancelFullfillmentAction
CancelFullfillmentAction(self.fulfillment).send()
logging.info(f"Sent shopify fulfillment cancellation: [{self.fulfillment['id']}] "
f"for order [{self.fulfillment['order_id']}]")
return self.fulfillment
else:
logger.warning(f"Unable to send cancellation, fulfillment {self.fulfillment['id']} does not contain a channel_fulfillment_id.")
except Exception as e:
logging.warning(f"Failed to send shopify fulfillmment cancellation: "
f"[{self.fulfillment['id']}] for order [{self.fulfillment['order_id']}]", e)
def notify(self):
if self.is_required():
if self.profile.integration_type == 'standard':
if self.profile.integration_config.send_notice_to_url_from_order:
try:
StandardShipNoticeAction(self.fulfillment).send()
self.__record_status(was_successful=True)
logging.info(f"Sent ship notice for fulfillment: [{self.fulfillment['id']}] "
f"with status [{self.fulfillment['status']}] "
f"for order: [{self.fulfillment['order_id']}]")
return self.fulfillment
except Exception as e:
snf = ShipNoticeFailedWarning(e)
self.__record_status(was_successful=False, msg=str(snf))
logging.warning(f"Failed to send ship notice for order: [{self.fulfillment['id']}]. "
f"Will retry on next scheduled interval. ", snf)
logging.warning(snf)
if self.profile.integration_type == 'shopify':
if self.profile.integration_config.capture_payment_on_ship:
try:
from lib_b2b.shopify import CapturePaymentAction
CapturePaymentAction(self.fulfillment).send()
logging.info(f"Successfully sent capture payment transaction to "
f"shopify for order: [{self.fulfillment['order_id']}]")
return self.fulfillment
except Exception as e:
ce = CapturePaymentFailedError(e)
logging.error(f"Failed to send shopify capture payment order: [{self.fulfillment['order_id']}]", ce)
logging.error(ce)
if self.profile.integration_config.send_fulfillment_on_ship:
try:
from lib_b2b.shopify import FullfillmentAction
FullfillmentAction(self.fulfillment).send()
self.__record_status(was_successful=True)
logging.info(f"Sent shopify fulfillment: [{self.fulfillment['id']}] "
f"for order [{self.fulfillment['order_id']}]")
return self.fulfillment
except Exception as e:
snf = ShipNoticeFailedWarning(e)
self.__record_status(was_successful=False, msg=str(snf))
logging.warning(f"Failed to send shopify fulfillmment: "
f"[{self.fulfillment['id']}] for order [{self.fulfillment['order_id']}]", snf)
logging.warning(snf)
else:
if 'status' not in self.fulfillment or FulfillmentStatus.for_(self.fulfillment['status']) != FulfillmentStatus.SENT:
retry_count = self.fulfillment['retry_count'] if 'retry_count' in self.fulfillment else 0
if retry_count >= MAX_RETRY_COUNT:
snf = ShipNoticeFailedError(f"Maximum retries exhausted trying to send ship notice "
f"for fulfillmment: [{self.fulfillment['id']}] for order "
f"[{self.fulfillment['order_id']}]. ")
logging.error(snf)
else:
snf = ShipNoticeFailedError(f"Ship notice not required for fulfillment that is not sent. "
f"fulfillmment: [{self.fulfillment['id']}], "
f"order: [{self.fulfillment['order_id']}]")
logging.error(snf)
# Moved to bottom to avoid silly python circular import problems
from lib_b2b.fulfillment_status import FulfillmentStatus
from lib_b2b.standard import StandardShipNoticeAction
from lib_b2b.profile import Profile