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 __restore(self, data: dict) -> CancelRequest: cancel_request = self.__reflector.construct( CancelRequest, { self.__class__.__ENTITY_PROPERTY_REQUEST_NUMBER: CancelRequest.Number(data['sk']), self.__class__.__ENTITY_PROPERTY_ORDER_NUMBER: OrderNumber(data['order_number']), self.__class__.__ENTITY_PROPERTY_ITEMS: tuple([ self.__reflector.construct( CancelRequest.Item, { self.__class__.__ENTITY_PROPERTY_ITEMS_SIMPLE_SKU: SimpleSku(item_data['simple_sku']), self.__class__.__ENTITY_PROPERTY_ITEMS_QTY: Qty(item_data['qty']), self.__class__.__ENTITY_PROPERTY_ITEMS_STATUS: CancelRequest.Item.Status(item_data['status']), self.__class__.__ENTITY_PROPERTY_ITEMS_PROCESSED_AT: (datetime.datetime.strptime( item_data['processed_at'], '%Y-%m-%dT%H:%M:%S.%f') if item_data['processed_at'] else None), }) for item_data in data['request_items'] ]), self.__class__.__ENTITY_PROPERTY_REFUND_METHOD: _restore_refund_method( data['refund_method'], json.loads(data['refund_method_extra_data_json'])), self.__class__.__ENTITY_PROPERTY_ADDITIONAL_COMMENT: (CancelRequest.AdditionalComment(data['additional_comment']) if data.get('additional_comment') or None else None), self.__class__.__ENTITY_PROPERTY_REQUESTED_AT: datetime.datetime.strptime(data['requested_at'], '%Y-%m-%dT%H:%M:%S.%f'), }) return cancel_request
def returns_reject(): returns_storage = ReturnRequestStorageImplementation() orders_storage = OrderStorageImplementation() sqs_sender = SqsSenderImplementation() logger = Logger() request_data = blueprint.current_request.json_body request_number = str(request_data.get('request_number') or '').strip() or None if not request_number: raise BadRequestError('"request_number" is required') order_number_value = str(request_data.get('order_number') or '').strip() or None if not order_number_value: raise BadRequestError('"order_number" is required') simple_sku_value = str(request_data.get('simple_sku') or '').strip() or None if not simple_sku_value: raise BadRequestError('"simple_sku" is required') return_request = returns_storage.load( ReturnRequest.Number(request_number)) if not return_request: raise NotFoundError( 'Return Request #{} does not exist!'.format(request_number)) if return_request.customer_id.value != __get_user().id: raise ForbiddenError('It is not your Return Request!') order_number = OrderNumber(order_number_value) simple_sku = SimpleSku(simple_sku_value) order = orders_storage.load(order_number) if (not order or not len([ item for item in return_request.items if item.order_number == order_number ]) or not len( [item for item in order.items if item.simple_sku == simple_sku])): raise NotFoundError( 'Product "{}" is not requested in Return Request #{} for Order #{}!' .format( simple_sku.value, return_request.number.value, order_number.value, )) # update values try: qty = return_request.get_item_qty(order_number, simple_sku) return_request.make_item_rejected(order_number, simple_sku) order.reject_return(simple_sku, qty) except ApplicationLogicException as e: raise UnprocessableEntityError(str(e)) # save updates def __log_flow(text: str) -> None: logger.log_simple('Return Request #{} - Rejecting : {}'.format( return_request.number.value, text)) __log_flow('Start') # saving __log_flow('Saving - Start') try: __log_flow('Order #{} Saving...'.format(order_number.value)) orders_storage.save(order) __log_flow('Order #{} Saving - Done!'.format(order_number.value)) __log_flow('Return Request Saving...') returns_storage.save(return_request) __log_flow('Return Request Saving - Done!') except ApplicationLogicException as e: __log_flow('Not Saved because of Error : {}'.format(str(e))) raise UnprocessableEntityError(str(e)) __log_flow('Saving - End') # send sqs __log_flow('SQS Sending - Start') try: __log_flow('Return Request SQS Sending...') sqs_sender.send(ReturnRequestChangeSqsSenderEvent(return_request)) __log_flow('Return Request SQS Sending - Done!') except BaseException as e: __log_flow( 'Return Request SQS Sending - Not done because of Error : {}'. format(str(e))) logger.log_exception(e) __log_flow('SQS Sending - End') __log_flow('End')
def returns_create_submit(): """ POST : { items: [ { order_number: str, simple_sku: str, qty: int, reason: str, file_ids: [str, ...], additional_comment: str|null, }, ... ], delivery_method: str, refund_method: { type: str, params: { # credit_card_eft account_holder_name: str, account_number: str, branch_code: str } } } """ user_id = __get_user().id now = datetime.datetime.now() returns_storage = ReturnRequestStorageImplementation() orders_storage = OrderStorageImplementation() file_storage = FileStorageImplementation() sqs_sender = SqsSenderImplementation() logger = Logger() # 1. Check input # ------------------------------- request_data = blueprint.current_request.json_body or {} input_items = request_data.get('items') if not input_items or not isinstance(input_items, (list, tuple, set)): raise BadRequestError( 'Incorrect Input Data! Parameter "items" is required!') elif sum([ not isinstance(item, dict) or not isinstance(item.get('order_number'), str) or not isinstance(item.get('simple_sku'), str) or not isinstance(item.get('qty'), int) or not isinstance(item.get('reason'), str) or not isinstance(item.get('file_ids'), (list, tuple, set)) or sum([ not isinstance(file_id, str) for file_id in item['file_ids'] ]) > 0 or not (item.get('additional_comment') is None or isinstance(item.get('additional_comment'), str)) for item in input_items ]) > 0: raise BadRequestError( 'Incorrect Input Data! Incorrect "items" structure!') delivery_method_input_descriptor = request_data.get('delivery_method') if not delivery_method_input_descriptor or not isinstance( delivery_method_input_descriptor, str): raise BadRequestError( 'Incorrect Input Data! Parameter "delivery_method" is required!' ) refund_method_input_data = request_data.get('refund_method') if not refund_method_input_data: raise BadRequestError( 'Incorrect Input Data! Parameter "refund_method" is required!') elif (not isinstance(refund_method_input_data, dict) or not isinstance(refund_method_input_data.get('type'), str) or not isinstance(refund_method_input_data.get('params'), dict)): raise BadRequestError( 'Incorrect Input Data! Parameter "refund_method" is incorrect!' ) # collect control data initial_data = __get_initial_data() control_data = { 'reasons': [reason['key'] for reason in initial_data['reasons']], 'delivery_methods': [ _delivery_method['key'] for _delivery_method in initial_data['delivery_methods'] ], 'orders': {}, } for order_data in initial_data['orders']: order_number = order_data['order_number'] for order_data_item in order_data['items']: simple_sku = order_data_item['simple_sku'] qty = order_data_item['qty_can_return'] control_data['orders'][order_number] = control_data[ 'orders'].get(order_number) or {} control_data['orders'][order_number][simple_sku] = qty # validate input data if ( # items sum([ item['order_number'] not in control_data['orders'].keys() or item['simple_sku'] not in control_data['orders'][item['order_number']].keys() or item['qty'] not in range( 1, control_data['orders'][item['order_number']][ item['simple_sku']] + 1) or item['reason'] not in control_data['reasons'] or sum([ not file_id.strip() or not file_storage.get(file_id) for file_id in item['file_ids'] ]) > 0 or (item['additional_comment'] is not None and len(item['additional_comment']) > 255) for item in input_items ]) > 0 # delivery method or delivery_method_input_descriptor not in control_data['delivery_methods'] # refund method (method structure/data check) or refund_method_input_data['type'] not in [ EftRefundMethod('test', 'test', 'test').descriptor, StoreCreditRefundMethod().descriptor, MobicredRefundMethod().descriptor, CreditCardRefundMethod().descriptor, ] or (refund_method_input_data['type'] == EftRefundMethod( 'test', 'test', 'test').descriptor and (not isinstance( refund_method_input_data.get( 'params', {}).get('account_holder_name'), str) or not refund_method_input_data['params'].get('account_holder_name') or not isinstance( refund_method_input_data.get('params', {}).get('account_number'), str) or not refund_method_input_data['params'].get('account_number') or not isinstance( refund_method_input_data.get('params', {}).get('branch_code'), str) or not refund_method_input_data['params'].get('branch_code'))) or (refund_method_input_data['type'] in ( StoreCreditRefundMethod().descriptor, MobicredRefundMethod().descriptor, CreditCardRefundMethod().descriptor, ) and len(refund_method_input_data['params']) > 0)): raise BadRequestError('Incorrect Input Data! Incorrect values!') # check duplicates in order if len( set([ str(item['order_number']) + str(item['simple_sku']) for item in input_items ])) != len(input_items): raise BadRequestError( 'Incorrect Input Data! Input items has duplicates!') # check refund methods # "...credit-card should be allowed only when one of selected orders was paid by credit card, # but eft and credits should be available for all return-requests..." _allowed_refund_methods_keys = [] for item in input_items: _order_refund_method_keys = [ _order_refund_method['key'] for _order in initial_data['orders'] if _order['order_number'] == item['order_number'] for _order_refund_method in _order['refund_methods'] ] # intersection of all input orders if len(_allowed_refund_methods_keys) == 0: _allowed_refund_methods_keys = _order_refund_method_keys else: _allowed_refund_methods_keys = [ key for key in _allowed_refund_methods_keys if key in _order_refund_method_keys ] if refund_method_input_data[ 'type'] not in _allowed_refund_methods_keys: raise BadRequestError( 'Incorrect Input Data! Refund method {} is not allowed for selected orders!' .format(refund_method_input_data['type'])) # 2. Create Return Request entity # ------------------------------- return_request_items = [] for item in input_items: order_number = item['order_number'] simple_sku = item['simple_sku'] qty = item['qty'] cost = None for initial_order in initial_data['orders']: if initial_order['order_number'] == order_number: for initial_item in initial_order['items']: if initial_item['simple_sku'] == simple_sku: cost = tuple( filter(lambda x: x.get('qty') == qty, initial_item['costs']))[0].get('cost') break reason = ReturnRequest.Item.Reason(item['reason']) attached_files = tuple([ ReturnRequest.Item.AttachedFile(file_storage.get(file_id).url) for file_id in item['file_ids'] ]) additional_comment = item.get('additional_comment') if item.get( 'additional_comment') else None additional_comment = ReturnRequest.Item.AdditionalComment( additional_comment) if additional_comment else None return_request_items.append( ReturnRequest.Item(OrderNumber(order_number), SimpleSku(simple_sku), Qty(qty), Cost(cost), reason, attached_files, additional_comment)) delivery_method_instance = None for _delivery_method_instance in [ HandDeliveryMethod(), CourierOrPostOffice(), RunwaysaleToCollect() ]: if _delivery_method_instance.descriptor == delivery_method_input_descriptor: delivery_method_instance = _delivery_method_instance break refund_method_instance = None for _refund_method_instance in [ StoreCreditRefundMethod(), EftRefundMethod('test', 'test', 'test'), MobicredRefundMethod(), CreditCardRefundMethod() ]: if _refund_method_instance.descriptor == refund_method_input_data[ 'type']: refund_method_instance = _refund_method_instance.__class__( **refund_method_input_data['params']) break return_request = ReturnRequest( Id(user_id), ReturnRequest.Number(now.strftime('%y%j03%f')), tuple(return_request_items), delivery_method_instance, refund_method_instance) # 3. Modify orders qty # ------------------------------- modified_orders = {} for return_item in return_request.items: order = modified_orders.get( return_item.order_number.value) or orders_storage.load( return_item.order_number) order.request_return(return_item.simple_sku, return_item.qty) modified_orders[order.number.value] = order # 4. Save changes # ------------------------------- def __log_flow(text: str) -> None: logger.log_simple('Return Request #{} - Creation : {}'.format( return_request.number.value, text)) __log_flow('Start') __log_flow('Saving Return Request...') returns_storage.save(return_request) __log_flow('Saving Return Request - Done!') __log_flow('Saving Orders...') for order in tuple(modified_orders.values()): __log_flow('Saving Order #{}...'.format(order.number.value)) orders_storage.save(order) __log_flow('Saving Order #{} - Done!'.format(order.number.value)) __log_flow('Saving Orders - Done!') # 5. Send SQS # ------------------------------- __log_flow('SQS Sending Return Request...') sqs_sender.send(ReturnRequestChangeSqsSenderEvent(return_request)) __log_flow('SQS Sending Return Request - Done!') __log_flow('End') return {'request_number': return_request.number.value}
def __restore(self, data: dict) -> ReturnRequest: request_items = [] for item_data in data['request_items']: attached_files = json.loads(item_data['attached_files_urls_json']) attached_files = tuple([ ReturnRequest.Item.AttachedFile(url) for url in attached_files ]) additional_comment = ReturnRequest.Item.AdditionalComment( item_data['additional_comment']) status_history = ReturnRequest.Item.StatusChangesHistory( tuple([ self.__reflector.construct( ReturnRequest.Item.StatusChangesHistory.Change, { '__status': ReturnRequest.Item.Status(change['status']), '__datetime': datetime.datetime.strptime(change['datetime'], '%Y-%m-%dT%H:%M:%S.%f'), }) for change in item_data['status_history'] ])) request_items.append( self.__reflector.construct( ReturnRequest.Item, { self.__class__.__ENTITY_PROPERTY_ITEMS_ORDER_NUMBER: OrderNumber(item_data['order_number']), self.__class__.__ENTITY_PROPERTY_ITEMS_SIMPLE_SKU: SimpleSku(item_data['simple_sku']), self.__class__.__ENTITY_PROPERTY_ITEMS_QTY: Qty(item_data['qty']), self.__class__.__ENTITY_PROPERTY_ITEMS_COST: Cost(item_data['cost']), self.__class__.__ENTITY_PROPERTY_ITEMS_REASON: ReturnRequest.Item.Reason(item_data['reason']), self.__class__.__ENTITY_PROPERTY_ITEMS_ATTACHED_FILES: attached_files, self.__class__.__ENTITY_PROPERTY_ITEMS_ADDITIONAL_COMMENT: additional_comment, self.__class__.__ENTITY_PROPERTY_ITEMS_STATUS_HISTORY: status_history, })) return_request = self.__reflector.construct( ReturnRequest, { self.__class__.__ENTITY_PROPERTY_REQUEST_NUMBER: ReturnRequest.Number(data['request_number']), self.__class__.__ENTITY_PROPERTY_CUSTOMER_ID: Id(data['customer_id']), self.__class__.__ENTITY_PROPERTY_ITEMS: tuple(request_items), self.__class__.__ENTITY_PROPERTY_DELIVERY_METHOD: _restore_delivery_method(data['delivery_method']), self.__class__.__ENTITY_PROPERTY_REFUND_METHOD: _restore_refund_method( data['refund_method'], json.loads(data['refund_method_extra_data_json'])), }) return return_request