def __init__(self): self.__order_storage = OrderStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() self.__message_storage = MessageStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__products_storage = ProductStorageImplementation()
def __response_list(user_id: str) -> dict: customer_storage = CustomerStorageImplementation() customer_id = Id(user_id) customer = customer_storage.get_by_id(customer_id) delivery_addresses = [{ 'hash': delivery_address.address_hash, 'address_type': delivery_address.address_type, 'recipient_name': delivery_address.recipient_name, 'phone_number': delivery_address.phone_number, 'street_address': delivery_address.street_address, 'suburb': delivery_address.suburb, 'city': delivery_address.city, 'province': delivery_address.province, 'business_name': delivery_address.business_name, 'complex_building': delivery_address.complex_building, 'postal_code': delivery_address.postal_code, 'special_instructions': delivery_address.special_instructions, 'address_nickname': delivery_address.address_nickname, 'is_billing': delivery_address.is_billing, 'is_shipping': delivery_address.is_shipping, } for delivery_address in customer.delivery_addresses] return { 'delivery_addresses': delivery_addresses, }
def checkout_next_tier_indication(): # @todo : this is a crutch # This should not be calculated on mpc side. This should be an api request to somewhere, # but this is impossible for now, so we have what we have. from chalicelib.settings import settings # from chalicelib.libs.core.elastic import Elastic from chalicelib.libs.models.mpc.base import DynamoModel from chalicelib.libs.purchase.customer.storage import CustomerStorageImplementation from chalicelib.libs.purchase.customer.storage import CustomerTierStorageImplementation try: user_id = __get_user().id customers_storage = CustomerStorageImplementation() tiers_storage = CustomerTierStorageImplementation() customer = customers_storage.get_by_id(Id(user_id)) if customer.tier.is_neutral: return { 'currently_spent': 0, 'next_tier': None } # Spent amount for customer can not exist, # if customer spent nothing or sqs is delayed, for example. # We can use a tier's minimal amount to return a value close to real. # elastic = Elastic( # settings.AWS_ELASTICSEARCH_CUSTOMER_TIERS_CUSTOMER_INFO_SPENT_AMOUNT, # settings.AWS_ELASTICSEARCH_CUSTOMER_TIERS_CUSTOMER_INFO_SPENT_AMOUNT # ) # row = elastic.get_data(customer.email.value) dynamo_db = DynamoModel(settings.AWS_DYNAMODB_CMS_TABLE_NAME) dynamo_db.PARTITION_KEY = 'PURCHASE_CUSTOMER_SPENT_AMOUNT' row = dynamo_db.find_item(customer.email.value) customer_spent_amount = float(row['spent_amount'] or 0) if row else customer.tier.spent_amount_min current_tiers = list(tiers_storage.get_all()) current_tiers.sort(key=lambda tier: tier.spent_amount_min) for i in range(0, len(current_tiers) - 1): if current_tiers[i].id == customer.tier.id: next_tier = current_tiers[i+1] break else: next_tier = current_tiers[-1] return { 'currently_spent': customer_spent_amount, 'next_tier': { 'name': next_tier.name.value, 'amount_min': next_tier.spent_amount_min, } } except BaseException as e: return http_response_exception_or_throw(e)
def __init__(self): # Need to be imported here, because orders storage depends from payment method class in this file. from chalicelib.libs.purchase.order.storage import OrderStorageImplementation from chalicelib.libs.purchase.product.storage import ProductStorageImplementation self.__order_storage = OrderStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() self.__message_storage = MessageStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__products_storage = ProductStorageImplementation()
def __is_customer_rs_staff(): # @todo : set customer in constructor, storage instead of id # @todo : customer.is_rs_staff and email domain to settings or so ? from chalicelib.libs.purchase.customer.storage import CustomerStorageImplementation customer = CustomerStorageImplementation().get_by_id(self.customer_id) customer_is_rs_staff = customer.email.value.split('@')[-1] == 'runwaysale.co.za' return self.__delivery_address.postal_code == '7777' or customer_is_rs_staff
def tier(self) -> dict: # customer tier - is a part of purchase module, not account information def __tier_to_dict(customer_tier: CustomerTier) -> dict: return { 'name': customer_tier.name.value, 'discount_rate': customer_tier.credit_back_percent.value, 'is_neutral': customer_tier.is_neutral } # cache if self.__purchase_customer_tier_lazy_loading_cache: return __tier_to_dict( self.__purchase_customer_tier_lazy_loading_cache) # guests are in neutral tier if self.is_anonymous: self.__purchase_customer_tier_lazy_loading_cache = CustomerTierStorageImplementation( ).get_neutral() return __tier_to_dict( self.__purchase_customer_tier_lazy_loading_cache) # get assigned customer tier customer = CustomerStorageImplementation().get_by_id( Id(self.customer_id)) self.__purchase_customer_tier_lazy_loading_cache = customer.tier return __tier_to_dict(self.__purchase_customer_tier_lazy_loading_cache)
def customer_delivery_addresses_edit(): customer_storage = CustomerStorageImplementation() delivery_address_service = CustomerDeliveryAddressAppService( customer_storage) try: request_data = blueprint.current_request.json_body address_type = str(request_data.get('address_type')).strip() address_hash = request_data.get('hash', None) address_hash = str( address_hash).strip() if address_hash is not None else None if not address_hash: raise HttpIncorrectInputDataException( '"hash" parameter is incorrect!') user_id = __get_user_id() form = __create_delivery_address_form(address_type) form.load(request_data) if form.validate(): delivery_address_service.edit_delivery_address( user_id, address_hash, form) return __response_list(user_id) else: return { 'validation_errors': form.validation_errors, } except BaseException as e: return http_response_exception_or_throw(e)
def __init__(self, order: Order): if not isinstance(order, Order): raise ArgumentTypeException(self.__init__, 'order', order) self.__order = order self.__customer = CustomerStorageImplementation().get_by_id( order.customer_id)
def __init__(self): from chalicelib.libs.purchase.cart.storage import CartStorageImplementation from chalicelib.libs.purchase.customer.storage import CustomerStorageImplementation from chalicelib.libs.purchase.checkout.storage import CheckoutStorageImplementation super().__init__(CartStorageImplementation(), CustomerStorageImplementation(), CheckoutStorageImplementation())
class OrderRefundSqsHandler(SqsHandlerInterface): def __init__(self) -> None: self.__messages_storage = MessageStorageImplementation() self.__order_storage = OrderStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__product_storage = ProductStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : SQS Message #{} : {}'.format( self.__class__.__qualname__, sqs_message.id, text )) __log_flow('Start - {}'.format(sqs_message.message_data)) order_number = Order.Number(sqs_message.message_data['order_number']) simple_sku = SimpleSku(sqs_message.message_data['simple_sku']) qty = Qty(sqs_message.message_data['qty']) __log_flow('Order Updating...') order = self.__order_storage.load(order_number) order.refund(simple_sku, qty) __log_flow('Order Updated!') __log_flow('Order Saving...') self.__order_storage.save(order) __log_flow('Order Saved!') __log_flow('Order SQS Sending...') self.__sqs_sender.send(OrderChangeSqsSenderEvent(order)) __log_flow('Order SQS Sent!') # add message (silently) try: __log_flow('Notification popup: Adding...') customer = self.__customer_storage.get_by_id(order.customer_id) product = self.__product_storage.load(simple_sku) message = Message( str(uuid.uuid4()), customer.email.value, 'Refund for Order #{}'.format(order.number.value), '"{}" has been Refunded in Qty {} for Order #{}'.format( product.name.value, qty.value, order.number.value ), ) self.__messages_storage.save(message) __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('End')
class InformationsSqsHandler(SqsHandlerInterface): def __init__(self): self.__information_service = InformationService() self.__customers_storage = CustomerStorageImplementation() self.__tiers_storage = CustomerTierStorageImplementation() def handle(self, sqs_message: SqsMessage) -> None: message_type = sqs_message.message_type message_data = sqs_message.message_data if message_type != 'customer_info': raise ValueError('SQS Message type "' + message_type + '" is unknown for ' + self.__class__.__name__) customer_data = message_data.get('customer') if not customer_data: raise ValueError('SQS Message does not have customer field') email = str(customer_data.get('email', '')).strip() if not email: raise ValueError('SQS Message does not have email field') information_model = self.__information_service.get(email) information = information_model.get_information() information.first_name = customer_data.get('first_name') information.last_name = customer_data.get('last_name') information.gender = customer_data.get('gender') information_model.insert_item(information) # set tier tier_id = customer_data['mpc_tier_id'] tier = self.__tiers_storage.get_by_id(Id(str(tier_id))) if not tier: raise ValueError('Unable to change Tier #{} for Customer #{} - tier does not exist!'.format( tier_id, information.customer_id )) customer = self.__customers_storage.get_by_id(Id(information.customer_id)) customer.tier = tier self.__customers_storage.save(customer)
def __init__(self): from chalicelib.libs.purchase.product.storage import ProductStorageImplementation from chalicelib.libs.purchase.checkout.storage import CheckoutStorageImplementation from chalicelib.libs.purchase.customer.storage import CustomerStorageImplementation from chalicelib.libs.purchase.order.dtd_calculator import DtdCalculatorImplementation from chalicelib.libs.purchase.order.storage import OrderStorageImplementation from chalicelib.libs.core.sqs_sender import SqsSenderImplementation from chalicelib.libs.core.mailer import MailerImplementation from chalicelib.libs.core.logger import Logger super().__init__(ProductStorageImplementation(), CheckoutStorageImplementation(), CustomerStorageImplementation(), DtdCalculatorImplementation(), OrderStorageImplementation(), SqsSenderImplementation(), MailerImplementation(), Logger())
def customer_delivery_addresses_edit(): customer_storage = CustomerStorageImplementation() delivery_address_service = CustomerDeliveryAddressAppService( customer_storage) try: request_data = blueprint.current_request.json_body address_hash = request_data.get('hash', None) address_hash = str( address_hash).strip() if address_hash is not None else None if not address_hash: raise HttpIncorrectInputDataException( '"hash" parameter is incorrect!') user_id = __get_user_id() delivery_address_service.remove_delivery_address( user_id, address_hash) return __response_list(user_id) except BaseException as e: return http_response_exception_or_throw(e)
def customer_delivery_addresses_add(): customer_storage = CustomerStorageImplementation() delivery_address_service = CustomerDeliveryAddressAppService( customer_storage) try: request_data = blueprint.current_request.json_body address_type = str(request_data.get('address_type')).strip() user_id = __get_user_id() form = __create_delivery_address_form(address_type) form.load(request_data) if form.validate(): delivery_address_service.add_delivery_address(user_id, form) return __response_list(user_id) else: return { 'validation_errors': form.validation_errors, } except BaseException as e: return http_response_exception_or_throw(e)
class CancelledOrderOnPortalSideSqsHandle(SqsHandlerInterface): def __init__(self): self.__orders_storage = OrderStorageImplementation() self.__products_storage = ProductStorageImplementation() self.__cancel_request_storage = CancelRequestStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__messages_storage = MessageStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : {} : {}'.format( self.__class__.__qualname__, sqs_message.id, text )) __log_flow('Start - {}'.format(sqs_message.message_data)) order_number = Order.Number(sqs_message.message_data['order_number']) simple_sku = SimpleSku(sqs_message.message_data['simple_sku']) qty = Qty(sqs_message.message_data['qty']) order = self.__orders_storage.load(order_number) product = self.__products_storage.load(simple_sku) if order.was_paid: order.request_cancellation_after_payment(simple_sku, qty) order.approve_cancellation_after_payment(simple_sku, qty) else: order.cancel_before_payment(simple_sku, qty) product.restore_qty(qty) order_change_event = OrderChangeSqsSenderEvent(order) __log_flow('Order Saving...') self.__orders_storage.save(order) __log_flow('Order Saved!') __log_flow('Product Saving...') self.__products_storage.update(product) __log_flow('Product Saved!') __log_flow('Order SQS Sending...') self.__sqs_sender.send(order_change_event) __log_flow('Order SQS Sent!') try: __log_flow('Notification popup: Adding...') customer = self.__customer_storage.get_by_id(order.customer_id) message = Message( str(uuid.uuid4()), customer.email.value, 'Order #{} has been Updated!'.format(order.number.value), 'Product "{}" for Order #{} has been Cancelled in Qty {}!'.format( product.name.value, order.number.value, qty.value ), ) self.__messages_storage.save(message) __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('End')
def _order_info(order: Order) -> dict: customer_storage = CustomerStorageImplementation() mpc_products = MpcProducts() customer = customer_storage.get_by_id(order.customer_id) delivery_address = order.delivery_address order_items_data = [] for order_item in order.items: product = mpc_products.getRawDataBySimpleSku( order_item.simple_sku.value, False) size = tuple( filter( lambda s: s['rs_simple_sku'] == order_item.simple_sku. value, product['sizes']))[0] simple_id = int(size['portal_simple_id']) qty_ordered = order_item.qty_ordered.value qty_canceled_before_payment = order_item.qty_cancelled_before_payment.value qty_canceled_after_payment = order_item.qty_cancelled_after_payment_cancelled.value qty_invoiced = ( qty_ordered - qty_canceled_before_payment) if order.was_paid else 0 qty_shipped = ( qty_ordered - qty_canceled_before_payment - qty_canceled_after_payment) if order.was_delivered else 0 qty_returned = order_item.qty_return_returned.value qty_refunded = order_item.qty_refunded.value # @todo : not done yet base_discount_amount = 0 # 1 - may mean "usual item", then 2 - "promo item", ... items_group_id = str(int('1%010d' % simple_id)) order_items_data.append({ 'DTD': str(order_item.dtd.working_days_from) + '-' + str(order_item.dtd.working_days_to), 'item_id': items_group_id, 'sku': order_item.simple_sku.value, 'rs_event_code': order_item.event_code.value, 'qty_ordered': qty_ordered, 'qty_canceled_before_payment': qty_canceled_before_payment, 'qty_invoiced': qty_invoiced, 'qty_canceled_after_payment': qty_canceled_after_payment, 'qty_shipped': qty_shipped, 'qty_returned': qty_returned, 'qty_refunded': qty_refunded, 'original_rsp': order_item.total_original_cost_ordered.value, 'original_price': order_item.total_current_cost_ordered.value, 'base_cost': order_item.product_current_price.value, 'base_discount_amount': base_discount_amount, 'base_price_incl_tax': order_item.product_current_price.value, 'base_tax_amount': (order_item.total_current_cost_ordered.value / (100 + order.vat_percent.value)) * order.vat_percent.value, 'tax_percent': order.vat_percent.value, 'base_row_total_incl_tax': order_item.product_current_price.value * order_item.qty_ordered.value, 'item_selling_incl_marketing_discount': order_item.product_current_price.value, 'order_tax_amount': order.subtotal_vat_amount.value, 'order_total_inc_voucher_discounts': order.total_current_cost_ordered.value, 'rs_store_type': 'MPC', 'product_id': simple_id, 'in_time_for_what': '', 'in_time_for_status': 0, }) if customer.name: customer_first_name = customer.name.first_name.value customer_last_name = customer.name.last_name.value else: customer_name_str = delivery_address.recipient_name customer_first_name = customer_name_str.split(' ')[0] customer_last_name = customer_name_str.replace( customer_first_name, '', 1).strip() customer_genders_map = { CustomerInterface.Gender.MALE: 'male', CustomerInterface.Gender.FEMALE: 'female', } data = { 'increment_id': order.number.value, 'status': order.status.value, 'created_at': __to_utc(order.created_at).strftime('%Y-%m-%d %H:%M:%S'), 'updated_at': __to_utc(order.updated_at).strftime('%Y-%m-%d %H:%M:%S'), 'payment': { 'entity_id': order.number.value, 'method': order.payment_method.descriptor, 'additional_information': order.payment_method.extra_data, 'base_amount_paid': order.total_current_cost_paid.value - order.credit_spent_amount.value, 'base_amount_refunded': order.total_refunded_cost.value, } if order.payment_method else { 'base_amount_ordered': order.total_current_cost_ordered.value if order.was_paid else 0, }, 'status_history': [{ 'status': status_change.status.value, 'created_at': __to_utc(status_change.datetime).strftime('%Y-%m-%d %H:%M:%S'), } for status_change in order.status_history], 'invoice': [{ 'entity_id': order.number.value, 'state': 3 if order.was_closed or order.was_cancelled else 2 if order.was_paid else 1, 'base_grand_total': order.total_current_cost_paid.value - order.credit_spent_amount.value, 'base_tax_amount': ((order.total_current_cost_paid.value - order.credit_spent_amount.value) / (100 + order.vat_percent.value)) * order.vat_percent.value, # @todo : not developed yet 'base_discount_amount': 0, 'base_subtotal_incl_tax': sum([ item.product_current_price.value * ((item.qty_ordered.value - item.qty_cancelled_before_payment.value) if order.was_paid else 0) for item in order.items ]), 'base_shipping_amount': order.delivery_cost.value, 'base_customer_balance_amount': order.credit_spent_amount.value, }] if order.was_paid else [], 'customer_firstname': customer_first_name, 'customer_lastname': customer_last_name, 'customer_email': customer.email.value, 'customer_gender': customer_genders_map.get(customer.gender.descriptor, None) if customer.gender else None, 'ip_address': None, # @TODO : add IP address. Not sure, that it should be inside Order. Create Elastic? 'addresses': [{ # magento address id. We use only one, so can set order number. 'entity_id': order.number.value, # @todo : we have only one address in order, so let it be 'shipping' 'address_type': 'shipping', 'country': 'South Africa', 'country_id': 'ZA', 'region': delivery_address.province, 'city': delivery_address.city, 'street': delivery_address.street_address, 'suburb': delivery_address.suburb, 'postcode': delivery_address.postal_code, 'company': delivery_address.business_name, 'firstname': delivery_address.recipient_name.split(' ')[0], 'lastname': (lambda s: s.replace(s.split(' ')[0] + ' ', '', 1))( delivery_address.recipient_name), 'telephone': delivery_address.phone_number, }], 'items': order_items_data, 'base_subtotal_incl_tax': sum([ item.product_current_price.value * item.qty_ordered.value for item in order.items ]), 'base_customer_balance_invoiced': order.credit_spent_amount.value, } return data
def __init__(self): self.__information_service = InformationService() self.__customers_storage = CustomerStorageImplementation() self.__tiers_storage = CustomerTierStorageImplementation()
def __init__(self): from chalicelib.libs.purchase.customer.storage import CustomerStorageImplementation from chalicelib.libs.purchase.product.storage import ProductStorageImplementation self.__storage = _CheckoutDynamoDbStorage( CustomerStorageImplementation(), ProductStorageImplementation())
class FbucksChargeSqsHandler(SqsHandlerInterface): # @TODO : REFACTORING !!! currently we are working with raw data def __init__(self): self.__orders_storage = OrderStorageImplementation() self.__logger = Logger() # """ # curl -X DELETE localhost:9200/fbucks_handled_orders # curl -X PUT localhost:9200/fbucks_handled_orders -H "Content-Type: application/json" -d'{ # "mappings": { # "fbucks_handled_orders": { # "properties": { # "handled_at": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"} # } # } # } # }' # """ # self.__fbucks_handled_orders_elastic = Elastic( # settings.AWS_ELASTICSEARCH_FBUCKS_HANDLED_ORDERS, # settings.AWS_ELASTICSEARCH_FBUCKS_HANDLED_ORDERS, # ) self.__fbucks_handled_orders_dynamo_db = DynamoModel( settings.AWS_DYNAMODB_CMS_TABLE_NAME) self.__fbucks_handled_orders_dynamo_db.PARTITION_KEY = 'PURCHASE_FBUCKS_REWARD_HANDLED_ORDERS' # Attention! # We can get current customer's amount as a sum of all changes by customer_id # But theoretically elastic can not be in time with index update (1 second) between requests. # So there is another index to store amount value. """ curl -X DELETE localhost:9200/fbucks_customer_amount curl -X PUT localhost:9200/fbucks_customer_amount -H "Content-Type: application/json" -d'{ "mappings": { "fbucks_customer_amount": { "properties": { "amount": {"type": "integer"} } } } }' curl -X DELETE localhost:9200/fbucks_customer_amount_changes curl -X PUT localhost:9200/fbucks_customer_amount_changes -H "Content-Type: application/json" -d'{ "mappings": { "fbucks_customer_amount_changes": { "properties": { "customer_id": {"type": "keyword"}, "amount": {"type": "integer"}, "changed_at": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"}, "order_number": {"type": "keyword"} } } } }' """ self.__fbucks_customer_amount_elastic = Elastic( settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT, settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT, ) self.__fbucks_customer_amount_changes_elastic = Elastic( settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES, settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES, ) self.__customer_storage = CustomerStorageImplementation() self.__messages_storage = MessageStorageImplementation() def handle(self, sqs_message: SqsMessage) -> None: import uuid import datetime from chalicelib.libs.purchase.core import Order order_number_values = sqs_message.message_data['order_numbers'] for order_number_value in order_number_values: try: now_string = datetime.datetime.now().strftime( "%Y-%m-%d %H:%M:%S") # skip duplicates # if self.__fbucks_handled_orders_elastic.get_data(order_number_value): if self.__fbucks_handled_orders_dynamo_db.find_item( order_number_value): self.__logger.log_simple( '{}: Fbucks for order #{} already earned!'.format( self.handle.__qualname__, order_number_value)) continue # ignore orders without fbucks amounts order = self.__orders_storage.load( Order.Number(order_number_value)) fbucks_amount = order.total_fbucks_earnings.value if fbucks_amount == 0: # remember order as handled # self.__fbucks_handled_orders_elastic.create(order_number_value, {'handled_at': now_string}) self.__fbucks_handled_orders_dynamo_db.put_item( order_number_value, {'handled_at': now_string}) continue # earn fbucks self.__fbucks_customer_amount_elastic.update_data( order.customer_id.value, { 'script': 'ctx._source.amount += ' + str(fbucks_amount), 'upsert': { 'amount': fbucks_amount, } }) self.__fbucks_customer_amount_changes_elastic.create( str(uuid.uuid4()) + str(order.customer_id.value), { "customer_id": order.customer_id.value, "amount": +fbucks_amount, "changed_at": now_string, "order_number": order_number_value, }) # remember order as handled # self.__fbucks_handled_orders_elastic.create(order_number_value, {'handled_at': now_string}) self.__fbucks_handled_orders_dynamo_db.put_item( order_number_value, {'handled_at': now_string}) # notify (silently) try: customer = self.__customer_storage.get_by_id( order.customer_id) self.__messages_storage.save( Message( str(uuid.uuid4()), customer.email.value, 'F-Bucks has been Earned!', 'You have earned {} F-Bucks by your Order #{}'. format(fbucks_amount, order.number.value))) except BaseException as e: self.__logger.log_exception(e) except BaseException as e: self.__logger.log_exception(e)
def __init__(self): self.__orders_storage = OrderStorageImplementation() self.__logger = Logger() # """ # curl -X DELETE localhost:9200/fbucks_handled_orders # curl -X PUT localhost:9200/fbucks_handled_orders -H "Content-Type: application/json" -d'{ # "mappings": { # "fbucks_handled_orders": { # "properties": { # "handled_at": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"} # } # } # } # }' # """ # self.__fbucks_handled_orders_elastic = Elastic( # settings.AWS_ELASTICSEARCH_FBUCKS_HANDLED_ORDERS, # settings.AWS_ELASTICSEARCH_FBUCKS_HANDLED_ORDERS, # ) self.__fbucks_handled_orders_dynamo_db = DynamoModel( settings.AWS_DYNAMODB_CMS_TABLE_NAME) self.__fbucks_handled_orders_dynamo_db.PARTITION_KEY = 'PURCHASE_FBUCKS_REWARD_HANDLED_ORDERS' # Attention! # We can get current customer's amount as a sum of all changes by customer_id # But theoretically elastic can not be in time with index update (1 second) between requests. # So there is another index to store amount value. """ curl -X DELETE localhost:9200/fbucks_customer_amount curl -X PUT localhost:9200/fbucks_customer_amount -H "Content-Type: application/json" -d'{ "mappings": { "fbucks_customer_amount": { "properties": { "amount": {"type": "integer"} } } } }' curl -X DELETE localhost:9200/fbucks_customer_amount_changes curl -X PUT localhost:9200/fbucks_customer_amount_changes -H "Content-Type: application/json" -d'{ "mappings": { "fbucks_customer_amount_changes": { "properties": { "customer_id": {"type": "keyword"}, "amount": {"type": "integer"}, "changed_at": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"}, "order_number": {"type": "keyword"} } } } }' """ self.__fbucks_customer_amount_elastic = Elastic( settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT, settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT, ) self.__fbucks_customer_amount_changes_elastic = Elastic( settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES, settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES, ) self.__customer_storage = CustomerStorageImplementation() self.__messages_storage = MessageStorageImplementation()
class RegularEftPaymentSqsHandler(SqsHandlerInterface): def __init__(self): # Need to be imported here, because orders storage depends from payment method class in this file. from chalicelib.libs.purchase.order.storage import OrderStorageImplementation from chalicelib.libs.purchase.product.storage import ProductStorageImplementation self.__order_storage = OrderStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() self.__message_storage = MessageStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__products_storage = ProductStorageImplementation() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : SQS Message #{} : {}'.format( self.__class__.__qualname__, sqs_message.id, text)) __log_flow('Start') if sqs_message.message_type != 'regular_eft_proof_check_result': raise ValueError( '{} does not know how to handle {} sqs message! Message data: {}' .format(self.__class__.__qualname__, sqs_message.message_type, sqs_message.message_data)) order_number_value = sqs_message.message_data.get('order_number') is_proof_accepted = sqs_message.message_data.get('is_proof_accepted') __log_flow('Order #{} - Payment - {}'.format( order_number_value, 'Accepted' if is_proof_accepted else 'Declined')) __log_flow('Updating...') order_number = Order.Number(order_number_value) order = self.__order_storage.load(order_number) if not order: raise ValueError( 'Unable to handle {} sqs-message #{}: order does not exist. Message data: {}' .format(sqs_message.message_type, sqs_message.id, sqs_message.message_data)) if not isinstance(order.payment_method, RegularEftOrderPaymentMethod): raise ValueError( 'Order #{} is not a Regular EFT payment order!'.format( order.number.value)) if is_proof_accepted: # accept order payment __log_flow('Order Updating...') order.status = Order.Status(Order.Status.PAYMENT_RECEIVED) self.__order_storage.save(order) __log_flow('Order Updated!') else: # Attention! # Order must be closed first to avoid multiple "restore-qty" actions! # @todo : refactoring ? # close order __log_flow('Order Closing...') order.status = Order.Status(Order.Status.CLOSED) self.__order_storage.save(order) __log_flow('Order Closed!') # restore products qty __log_flow('Product Qty Restoring - Start') for order_item in order.items: if order_item.qty_processable.value == 0: __log_flow( 'Product Qty Restoring: {} skipped because of 0 qty'. format(order_item.simple_sku.value)) continue try: __log_flow('Product Qty Restoring {} / {} ...'.format( order_item.simple_sku.value, order_item.qty_processable.value)) product = self.__products_storage.load( order_item.simple_sku) product.restore_qty(order_item.qty_processable) self.__products_storage.update(product) __log_flow('Product Qty Restored {} / {}!'.format( order_item.simple_sku.value, order_item.qty_processable.value)) except BaseException as e: self.__logger.log_exception(e) __log_flow( 'Product Qty NOT Restored {} / {} because of Error: '. format(order_item.simple_sku.value, order_item.qty_processable.value, str(e))) __log_flow('Product Qty Restoring - End') __log_flow('Updated!') # send to portal __log_flow('Order SQS: Sending...') self.__sqs_sender.send(OrderChangeSqsSenderEvent(order)) __log_flow('Order SQS: Sent!') # silently add notification (silently) try: __log_flow('Notification popup: Adding...') customer = self.__customer_storage.get_by_id(order.customer_id) if not customer: raise ValueError( '{} cant notify customer #{} about Regular-EFT payment updates for Order #{}' .format(self.handle.__qualname__, order.customer_id.value, order.number.value)) self.__message_storage.save( Message( str(uuid.uuid4()), customer.email.value, 'Regular EFT Payment has been checked!', 'Regular EFT Payment for Order #{} has been checked and {}!' .format(order.number.value, 'Accepted' if is_proof_accepted else 'Declined'))) __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow( 'Notification popup: Not Added because of Error : {}'.format( str(e))) __log_flow('End')
class OrderPaymentOhHoldHandler(SqsHandlerInterface): def __init__(self): self.__order_storage = OrderStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() self.__message_storage = MessageStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__products_storage = ProductStorageImplementation() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : SQS Message #{} : {}'.format( self.__class__.__qualname__, sqs_message.id, text )) __log_flow('Start : {}'.format(sqs_message.message_data)) if sqs_message.message_type != 'fixel_order_on_hold_by_portal': raise ValueError('{} does not know how to handle {} sqs message! Message data: {}'.format( self.__class__.__qualname__, sqs_message.message_type, sqs_message.message_data )) order_number_value = sqs_message.message_data.get('order_number') on_hold_status = sqs_message.message_data.get('status') order_number = Order.Number(order_number_value) order = self.__order_storage.load(order_number) if on_hold_status == Order.Status.CLOSED: self.__close_order_on_hold(order, __log_flow) else: self.__on_hold_not_closed_status(order, on_hold_status, __log_flow) self.__send_order_change_to_portal(order, __log_flow) self.__notify_about_order_status_change_silently(order, __log_flow) __log_flow('End') def __on_hold_not_closed_status(self, order: Order, on_hold_status: str, __log_flow) -> None: __log_flow('Order Updating...') order.status = Order.Status(on_hold_status) __log_flow('Order Updated!') __log_flow('Order Saving...') self.__order_storage.save(order) __log_flow('Order Saved!') def __close_order_on_hold(self, order: Order, __log_flow) -> None: __log_flow('Updating...') # close order __log_flow('Order Updating...') order.status = Order.Status(Order.Status.CLOSED) __log_flow('Order Updated!') # restore products qty __log_flow('Product Qty Updating - Start') products_to_save = [] for order_item in order.items: if order_item.qty_processable.value == 0: __log_flow('Product Qty Updating: {} skipped because of 0 qty'.format(order_item.simple_sku.value)) continue __log_flow('Product Qty Updating {} / {} ...'.format( order_item.simple_sku.value, order_item.qty_processable.value )) product = self.__products_storage.load(order_item.simple_sku) product.restore_qty(order_item.qty_processable) products_to_save.append(product) __log_flow('Product Qty Updated {} / {}!'.format( order_item.simple_sku.value, order_item.qty_processable.value )) __log_flow('Product Qty Updating - End') __log_flow('Updated!') __log_flow('Saving...') __log_flow('Order Saving...') self.__order_storage.save(order) __log_flow('Order Saved!') __log_flow('Products Saving...') for product in products_to_save: __log_flow('Product {} Saving...'.format(product.simple_sku.value)) self.__products_storage.update(product) __log_flow('Product {} Saved!'.format(product.simple_sku.value)) __log_flow('Products Saved!') __log_flow('Saved!') def __send_order_change_to_portal(self, order: Order, __log_flow) -> None: __log_flow('Order SQS: Sending...') self.__sqs_sender.send(OrderChangeSqsSenderEvent(order)) __log_flow('Order SQS: Sent!') def __notify_about_order_status_change_silently(self, order: Order, __log_flow) -> None: try: __log_flow('Notification popup: Adding...') customer = self.__customer_storage.get_by_id(order.customer_id) self.__message_storage.save(Message( str(uuid.uuid4()), customer.email.value, 'Order #{} status is changed!', 'Order #{} has been turned to "{}" status!'.format(order.number.value, order.status.label) )) __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e)))
class CancelRequestPaidOrderAnswerSqsHandler(SqsHandlerInterface): def __init__(self): self.__orders_storage = OrderStorageImplementation() self.__products_storage = ProductStorageImplementation() self.__cancel_request_storage = CancelRequestStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__messages_storage = MessageStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : {} : {}'.format( self.__class__.__qualname__, sqs_message.id, text )) __log_flow('Start - {}'.format(sqs_message.message_data)) request_number = CancelRequest.Number(sqs_message.message_data['request_number']) order_number = Order.Number(sqs_message.message_data['order_number']) simple_sku = SimpleSku(sqs_message.message_data['simple_sku']) qty = Qty(sqs_message.message_data['qty']) status = sqs_message.message_data['status'] actions_map = { 'approved': self.__approve, 'declined': self.__decline, } action = actions_map.get(status) if not action: raise Exception('{} can\'t handle SQS message {}:{}! Status is unknown!'.format( self.handle.__qualname__, sqs_message.message_type, sqs_message.message_data )) action(request_number, order_number, simple_sku, qty, __log_flow) __log_flow('End') def __approve( self, request_number: CancelRequest.Number, order_number: Order.Number, simple_sku: SimpleSku, qty: Qty, __log_flow ) -> None: __log_flow('Approving...') cancel_request = self.__cancel_request_storage.get_by_number(request_number) order = self.__orders_storage.load(order_number) product = self.__products_storage.load(simple_sku) cancel_request.approve_item(simple_sku) order.approve_cancellation_after_payment(simple_sku, qty) product.restore_qty(qty) order_change_event = OrderChangeSqsSenderEvent(order) __log_flow('Cancel Request Saving...') self.__cancel_request_storage.save(cancel_request) __log_flow('Cancel Request Saved!') __log_flow('Order Saving...') self.__orders_storage.save(order) __log_flow('Order Saved!') __log_flow('Product Saving...') self.__products_storage.update(product) __log_flow('Product Saved!') __log_flow('Order SQS Sending...') self.__sqs_sender.send(order_change_event) __log_flow('Order SQS Sent!') try: __log_flow('Notification popup: Adding...') self.__add_notification_message(cancel_request, order, product, 'Approved') __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('Approved!') def __decline( self, request_number: CancelRequest.Number, order_number: Order.Number, simple_sku: SimpleSku, qty: Qty, __log_flow ) -> None: __log_flow('Declining...') cancel_request = self.__cancel_request_storage.get_by_number(request_number) order = self.__orders_storage.load(order_number) product = self.__products_storage.load(simple_sku) cancel_request.decline_item(simple_sku) order.decline_cancellation_after_payment(simple_sku, qty) order_change_event = OrderChangeSqsSenderEvent(order) __log_flow('Cancel Request Saving...') self.__cancel_request_storage.save(cancel_request) __log_flow('Cancel Request Saved!') __log_flow('Order Saving...') self.__orders_storage.save(order) __log_flow('Order Saved!') __log_flow('Order SQS Sending...') self.__sqs_sender.send(order_change_event) __log_flow('Order SQS Sent!') try: __log_flow('Notification popup: Adding...') self.__add_notification_message(cancel_request, order, product, 'Declined') __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('Declined!') def __add_notification_message( self, cancel_request: CancelRequest, order: Order, product: ProductInterface, status_label: str ) -> None: customer = self.__customer_storage.get_by_id(order.customer_id) message = Message( str(uuid.uuid4()), customer.email.value, 'Cancellation Request #{} has been Updated!'.format(cancel_request.number.value), 'Cancellation Request for Product "{}" for Order #{} has been {}!'.format( product.name.value, order.number.value, status_label ), ) self.__messages_storage.save(message)
class ReturnRequestChangeSqsHandler(SqsHandlerInterface): def __init__(self): self.__returns_storage = ReturnRequestStorageImplementation() self.__order_storage = OrderStorageImplementation() self.__customer_storage = CustomerStorageImplementation() self.__product_storage = ProductStorageImplementation() self.__messages_storage = MessageStorageImplementation() self.__sqs_sender = SqsSenderImplementation() self.__logger = Logger() def handle(self, sqs_message: SqsMessage) -> None: def __log_flow(text: str) -> None: self.__logger.log_simple('{} : {} : {}'.format( self.__class__.__qualname__, sqs_message.id, text )) __log_flow('Start - {}'.format(sqs_message.message_data)) request_number = sqs_message.message_data.get('return_request_number') order_number = sqs_message.message_data.get('order_number') simple_sku = sqs_message.message_data.get('simple_sku') status = sqs_message.message_data.get('status') __log_flow('Updating...') return_request = self.__returns_storage.load(ReturnRequest.Number(request_number)) if not return_request: raise ValueError('{} can\'t handle SQS message {}:{}! Return Request does not exist!'.format( self.handle.__qualname__, sqs_message.message_type, sqs_message.message_data )) actions_map = { 'approved': self.__approve, 'cancelled': self.__decline, 'closed': self.__close, } action = actions_map.get(status) if not action: raise Exception('{} can\'t handle SQS message {}:{}! Status is unknown!'.format( self.handle.__qualname__, sqs_message.message_type, sqs_message.message_data )) action(return_request, OrderNumber(order_number), SimpleSku(simple_sku), __log_flow) __log_flow('Updated!') __log_flow('End') def __approve( self, return_request: ReturnRequest, order_number: OrderNumber, simple_sku: SimpleSku, __log_flow ) -> None: __log_flow('Approving...') return_request.make_item_approved(order_number, simple_sku) __log_flow('Return Request Saving...') self.__returns_storage.save(return_request) __log_flow('Return Request Saved!') try: __log_flow('Notification popup: Adding...') self.__add_notification_message(return_request, order_number, simple_sku, 'Approved') __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('Approved!') def __decline( self, return_request: ReturnRequest, order_number: OrderNumber, simple_sku: SimpleSku, __log_flow ) -> None: __log_flow('Declining...') qty = return_request.get_item_qty(order_number, simple_sku) order = self.__order_storage.load(order_number) # updating return_request.make_item_declined(order_number, simple_sku) order.decline_return(simple_sku, qty) # saving __log_flow('Declining: Return Request Saving...') self.__returns_storage.save(return_request) __log_flow('Declining: Return Request Saved!') __log_flow('Declining: Order Saving...') self.__order_storage.save(order) __log_flow('Declining: Order Saved!') # add notification silently try: __log_flow('Notification popup: Adding...') self.__add_notification_message(return_request, order_number, simple_sku, 'Declined') __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('Declined!') def __close( self, return_request: ReturnRequest, order_number: OrderNumber, simple_sku: SimpleSku, __log_flow ) -> None: __log_flow('Closing...') qty = return_request.get_item_qty(order_number, simple_sku) product = self.__product_storage.load(simple_sku) order = self.__order_storage.load(order_number) product.restore_qty(qty) order.close_return(simple_sku, qty) return_request.make_item_closed(order_number, simple_sku) # update product __log_flow('Closing: Product Qty: Restoring...') self.__product_storage.update(product) __log_flow('Closing: Product Qty: Restored!') # update order __log_flow('Closing: Order Qty: Returning...') self.__order_storage.save(order) __log_flow('Closing: Order Qty: Returned!') # update request __log_flow('Closing: Return Request: Updating...') self.__returns_storage.save(return_request) __log_flow('Closing: Return Request: Updated!') __log_flow('Closing: Order SQS: Sending...') self.__sqs_sender.send(OrderChangeSqsSenderEvent(order)) __log_flow('Closing: Order SQS: Sent!') # add notification silently try: __log_flow('Notification popup: Adding...') self.__add_notification_message(return_request, order_number, simple_sku, 'Closed') __log_flow('Notification popup: Added!') except BaseException as e: self.__logger.log_exception(e) __log_flow('Notification popup: Not Added because of Error : {}'.format(str(e))) __log_flow('Closed!') def __add_notification_message( self, return_request: ReturnRequest, order_number: OrderNumber, simple_sku: SimpleSku, status_label: str ) -> None: order = self.__order_storage.load(order_number) customer = self.__customer_storage.get_by_id(order.customer_id) product = self.__product_storage.load(simple_sku) message = Message( str(uuid.uuid4()), customer.email.value, 'Return Request #{} has been Updated!'.format(return_request.number.value), 'Return Request for Product "{}" for Order #{} has been {}!'.format( product.name.value, order_number.value, status_label ), ) self.__messages_storage.save(message)