Repository URL to install this package:
|
Version:
0.4.139 ▾
|
lib-py-b2b
/
shipnotice.py
|
|---|
from os import environ
import datetime
import logging
from boto3 import resource
from botocore.exceptions import ClientError
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().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().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