示例#1
0
    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')
示例#2
0
    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
示例#3
0
    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')
示例#4
0
    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}
示例#5
0
    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