Пример #1
0
    def __init__(self, product_data: dict, simple_sku: str):
        if not isinstance(product_data, dict):
            raise ArgumentTypeException(self.__init__, 'product_data',
                                        product_data)

        if not isinstance(simple_sku, str):
            raise ArgumentTypeException(self.__init__, 'simple_sku',
                                        simple_sku)

        for size in product_data.get('sizes', ()):
            if size.get('simple_sku') == simple_sku:
                simple_data = size
                break
        else:
            raise ValueError(
                '{0} cannot be created: simple sku {1} is not related to product {2}'
                .format(self.__class__.__name__, simple_sku, product_data))

        # @todo : refactoring
        original_price = float(product_data.get('price'))
        discount = float(product_data.get('discount'))
        current_price = original_price - original_price * discount / 100

        self.__event_code = EventCode(str(product_data.get('event_code')))
        self.__simple_sku = SimpleSku(str(simple_data.get('simple_sku')))
        self.__qty = Qty(int(simple_data.get('qty')))
        self.__original_price = Cost(original_price)
        self.__current_price = Cost(current_price)
        self.__name = Name(product_data.get('title'))
        self.__size_name = Name(simple_data.get('size'))
        self.__image_urls = tuple([product_data['image']['src']])
Пример #2
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 = 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')
Пример #3
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))

        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')
Пример #4
0
    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')
Пример #5
0
    def cancellation_create_get_initial_data(order_number):
        user = __get_user()
        orders_storage = OrderStorageImplementation()
        products_storage = ProductStorageImplementation()

        try:
            order_number = Order.Number(order_number)
        except BaseException:
            raise BadRequestError('Incorrect Input Data!')

        order = orders_storage.load(order_number)
        if not order:
            raise NotFoundError('Order #{} does not exist!'.format(
                order_number.value))
        elif order.customer_id.value != user.id:
            raise ForbiddenError('Order #{} is not your!'.format(
                order_number.value))
        elif not order.is_cancellable:
            raise UnprocessableEntityError(
                'Order #{} is not Cancellable!'.format(order.number.value))

        products_map: Dict[str, ProductInterface] = {}
        for item in order.items:
            product = products_map.get(
                item.simple_sku.value) or products_storage.load(
                    item.simple_sku)
            products_map[item.simple_sku.value] = product

        return {
            'items': [{
                'simple_sku':
                item.simple_sku.value,
                'product_name':
                products_map[item.simple_sku.value].name.value,
                'img_url':
                (products_map[item.simple_sku.value].image_urls[0]
                 if products_map[item.simple_sku.value].image_urls else None),
                'costs': [{
                    'qty': qty,
                    'cost': item.get_refund_cost(Qty(qty)).value
                } for qty in range(1, item.qty_processable.value + 1)],
                'qty_can_cancel':
                item.qty_processable.value,
            } for item in order.items],
            'refund_methods':
            __get_refund_methods_initial_data(order),
        }
Пример #6
0
    def __restore(self, data) -> Checkout:
        customer_id = Id(data.get('sk'))
        customer = self.__customer_storage.get_by_id(customer_id)

        checkout_items = []
        for item in data.get('checkout_items', tuple()):
            simple_sku = SimpleSku(str(item.get('simple_sku')))
            qty = Qty(int(item.get('qty')))

            product = self.__product_storage.load(simple_sku)
            checkout_item = Checkout.Item(product, qty)
            checkout_items.append(checkout_item)

        delivery_address = DeliveryAddress(
            data.get('delivery_address').get('recipient_name'),
            data.get('delivery_address').get('phone_number'),
            data.get('delivery_address').get('street_address'),
            data.get('delivery_address').get('suburb'),
            data.get('delivery_address').get('city'),
            data.get('delivery_address').get('province'),
            data.get('delivery_address').get('complex_building'),
            data.get('delivery_address').get('postal_code'),
            data.get('delivery_address').get('business_name'),
            data.get('delivery_address').get('special_instructions')
        ) if data.get('delivery_address', None) else None

        delivery_cost = Cost(float(data.get('delivery_cost')))
        vat = Percentage(float(data.get('vat_percent')))

        # can not exist for old data
        available_credits_amount = Cost(
            float(data.get('credits_available_amount', '0') or '0'))
        is_credits_in_use = bool(int(
            data.get('is_credits_in_use', '0') or '0'))

        # @todo : reflection
        checkout = object.__new__(Checkout)
        checkout._Checkout__customer = customer
        checkout._Checkout__checkout_items = checkout_items
        checkout._Checkout__delivery_address = delivery_address
        checkout._Checkout__delivery_cost = delivery_cost
        checkout._Checkout__vat_percent = vat
        checkout._Checkout__available_credits_amount = available_credits_amount
        checkout._Checkout__is_credits_in_use = is_credits_in_use

        return checkout
Пример #7
0
    def __restore(self, data: dict) -> Cart:
        cart_items = []
        for item_data in data.get('cart_items', tuple()):
            simple_sku = SimpleSku(str(item_data.get('simple_sku')))
            qty = Qty(int(item_data.get('qty')))
            product = self.__product_storage.load(simple_sku)
            cart_items.append(
                self.__reflector.construct(
                    Cart.Item, {
                        self.__class__.__ENTITY_PROPERTY_ITEMS_PRODUCT:
                        product,
                        self.__class__.__ENTITY_PROPERTY_ITEMS_QTY: qty,
                    }))

        cart: Cart = self.__reflector.construct(
            Cart, {
                self.__class__.__ENTITY_PROPERTY_ID: Id(data.get('sk')),
                self.__class__.__ENTITY_PROPERTY_ITEMS: cart_items,
                self.__class__.__ENTITY_PROPERTY_VAT_PERCENT:
                self.__vat_percent
            })

        return cart
Пример #8
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
Пример #9
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}
Пример #10
0
    def __get_initial_data():
        orders_storage = OrderStorageImplementation()
        products_storage = ProductStorageImplementation()

        orders = []
        products_map = {}

        # "...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..."
        payment_refund_methods_map = {
            # @todo : payment methods descriptors
            'regular_eft': [{
                'key': refund_method.descriptor,
                'label': refund_method.label,
            } for refund_method in [
                StoreCreditRefundMethod(),
                EftRefundMethod('test', 'test', 'test')
            ]],
            'customer_credit': [{
                'key': refund_method.descriptor,
                'label': refund_method.label,
            } for refund_method in [
                StoreCreditRefundMethod(),
                EftRefundMethod('test', 'test', 'test')
            ]],
            'mobicred': [{
                'key': refund_method.descriptor,
                'label': refund_method.label,
            } for refund_method in [
                StoreCreditRefundMethod(),
                EftRefundMethod('test', 'test', 'test'),
                MobicredRefundMethod(),
            ]],
            'credit_card': [{
                'key': refund_method.descriptor,
                'label': refund_method.label,
            } for refund_method in [
                StoreCreditRefundMethod(),
                EftRefundMethod('test', 'test', 'test'),
                CreditCardRefundMethod()
            ]]
        }

        for order in orders_storage.get_all_for_customer(Id(__get_user().id)):
            if not order.is_returnable:
                continue

            items = []
            for item in order.items:
                product = products_map.get(
                    item.simple_sku.value) or products_storage.load(
                        item.simple_sku)
                products_map[item.simple_sku.value] = product

                items.append({
                    'simple_sku':
                    item.simple_sku.value,
                    'product_name':
                    product.name.value,
                    'img_url':
                    product.image_urls[0] if product.image_urls else None,
                    'costs': [{
                        'qty': qty,
                        'cost': item.get_refund_cost(Qty(qty)).value
                    } for qty in range(1, item.qty_processable.value + 1)],
                    'qty_can_return':
                    item.qty_processable.value,
                })

            orders.append({
                'order_number':
                order.number.value,
                'ordered_at':
                order.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                'can_be_returned_till':
                order.is_returnable_till.strftime('%Y-%m-%d %H:%M:%S'),
                'items':
                items,
                'refund_methods':
                payment_refund_methods_map[order.payment_method.descriptor]
            })

        return {
            'reasons': [{
                'key': reason.descriptor,
                'label': reason.label,
            } for reason in [
                ReturnRequest.Item.Reason(ReturnRequest.Item.Reason.TOO_BIG),
                ReturnRequest.Item.Reason(ReturnRequest.Item.Reason.TOO_SMALL),
                ReturnRequest.Item.Reason(
                    ReturnRequest.Item.Reason.SELECTED_WRONG_SIZE),
                ReturnRequest.Item.Reason(
                    ReturnRequest.Item.Reason.DONT_LIKE_IT),
                ReturnRequest.Item.Reason(
                    ReturnRequest.Item.Reason.NOT_HAPPY_WITH_QTY),
                ReturnRequest.Item.Reason(
                    ReturnRequest.Item.Reason.RECEIVED_WRONG_SIZE),
                ReturnRequest.Item.Reason(
                    ReturnRequest.Item.Reason.RECEIVED_DAMAGED),
            ]],
            'delivery_methods': [{
                'key': delivery_method.descriptor,
                'label': delivery_method.label,
            } for delivery_method in [
                HandDeliveryMethod(),
                CourierOrPostOffice(),
                RunwaysaleToCollect()
            ]],
            'orders':
            orders,
        }
Пример #11
0
    def __restore(self, data: dict) -> Order:
        order_number = Order.Number(data.get('sk'))
        customer_id = Id(data.get('customer_id'))
        delivery_cost = Cost(float(data.get('delivery_cost')))
        vat_percent = Percentage(float(data.get('vat_percent')))
        credits_spent = Cost(float(data.get('credits_spent') or '0'))

        payment_method = self.__restore_payment_method(
            data.get('payment_method'),
            json.loads(data.get('payment_method_extra_data_json') or '{}')
            if data.get('payment_method') else None)

        delivery_address = DeliveryAddress(
            data.get('delivery_address_recipient_name'),
            data.get('delivery_address_phone_number'),
            data.get('delivery_address_street_address'),
            data.get('delivery_address_suburb'),
            data.get('delivery_address_city'),
            data.get('delivery_address_province'),
            data.get('delivery_address_complex_building'),
            data.get('delivery_address_postal_code'),
            data.get('delivery_address_business_name'),
            data.get('delivery_address_special_instructions'))

        status_changes = []
        for status_change_data in data.get('status_history'):
            status = Order.Status(status_change_data.get('status'))
            changed_at = datetime.datetime.strptime(
                status_change_data.get('datetime'), '%Y-%m-%dT%H:%M:%S.%f')
            status_change = self.__reflector.construct(
                Order.StatusChangesHistory.Change, {
                    '__status': status,
                    '__datetime': changed_at
                })
            status_changes.append(status_change)

        status_change_history = Order.StatusChangesHistory(
            tuple(status_changes))

        order_items = []
        for item_data in data.get('order_items'):
            event_code = EventCode(item_data.get('event_code'))
            simple_sku = SimpleSku(item_data.get('simple_sku'))
            product_original_price = Cost(
                item_data.get('product_original_price'))
            product_current_price = Cost(
                item_data.get('product_current_price'))
            fbucks_earnings = Cost(item_data.get('fbucks_earnings'))
            dtd = Dtd(
                Dtd.Occasion(
                    Name(item_data.get('dtd_occasion_name')),
                    Description(item_data.get('dtd_occasion_description')))
                if item_data.get('dtd_occasion_name') else None,
                datetime.date(
                    int(item_data.get('dtd_date_from').split('-')[0]),
                    int(item_data.get('dtd_date_from').split('-')[1]),
                    int(item_data.get('dtd_date_from').split('-')[2])),
                datetime.date(int(item_data.get('dtd_date_to').split('-')[0]),
                              int(item_data.get('dtd_date_to').split('-')[1]),
                              int(item_data.get('dtd_date_to').split('-')[2])),
                int(item_data.get('dtd_working_days_from')),
                int(item_data.get('dtd_working_days_to')))

            qty_ordered = Qty(int(item_data.get('qty_ordered')))
            qty_return_requested = Qty(
                int(item_data.get('qty_return_requested') or 0))
            qty_return_returned = Qty(
                int(item_data.get('qty_return_returned') or 0))
            qty_cancelled_before_payment = Qty(
                int(item_data.get('qty_cancelled_before_payment') or 0))
            qty_cancelled_after_payment_requested = Qty(
                int(
                    item_data.get('qty_cancelled_after_payment_requested')
                    or 0))
            qty_cancelled_after_payment_cancelled = Qty(
                int(
                    item_data.get('qty_cancelled_after_payment_cancelled')
                    or 0))
            qty_refunded = Qty(int(item_data.get('qty_refunded') or 0))
            qty_modified_at = datetime.datetime.strptime(
                item_data.get('qty_modified_at'), '%Y-%m-%dT%H:%M:%S.%f')

            order_item = self.__reflector.construct(
                Order.Item, {
                    '__event_code': event_code,
                    '__simple_sku': simple_sku,
                    '__product_original_price': product_original_price,
                    '__product_current_price': product_current_price,
                    '__dtd': dtd,
                    '__qty_ordered': qty_ordered,
                    '__qty_return_requested': qty_return_requested,
                    '__qty_return_returned': qty_return_returned,
                    '__qty_cancelled_before_payment':
                    qty_cancelled_before_payment,
                    '__qty_cancelled_after_payment_requested':
                    qty_cancelled_after_payment_requested,
                    '__qty_cancelled_after_payment_cancelled':
                    qty_cancelled_after_payment_cancelled,
                    '__qty_refunded': qty_refunded,
                    '__qty_modified_at': qty_modified_at,
                    '__fbucks_earnings': fbucks_earnings
                })
            order_items.append(order_item)

        order = self.__reflector.construct(
            Order, {
                '__order_number': order_number,
                '__customer_id': customer_id,
                '__items': order_items,
                '__delivery_address': delivery_address,
                '__delivery_cost': delivery_cost,
                '__vat_percent': vat_percent,
                '__payment_method': payment_method,
                '__status_history': status_change_history,
                '__credits_spent': credits_spent,
            })

        return order
Пример #12
0
    def __restore(self, data: dict) -> Order:
        order_number = Order.Number(data.get('order_number'))
        customer_id = Id(data.get('customer_id'))
        delivery_cost = Cost(float(data.get('delivery_cost')))
        vat_percent = Percentage(
            float(
                # I added "vat_percent" after first orders were stored,
                # but it's hard to make changes in elastic, so...
                # @todo : create migration tool.
                data.get('vat_percent') or self.__current_vat_value))
        credits_spent = Cost(float(data.get('credits_spent')
                                   or '0'))  # can be not existed in old data
        payment_method = self.__restore_payment_method(
            data.get('payment_method'),
            json.loads(data.get('payment_method_extra_data_json') or '{}')
            if data.get('payment_method') else None)

        delivery_address = DeliveryAddress(
            data.get('delivery_address_recipient_name'),
            data.get('delivery_address_phone_number'),
            data.get('delivery_address_street_address'),
            data.get('delivery_address_suburb'),
            data.get('delivery_address_city'),
            data.get('delivery_address_province'),
            data.get('delivery_address_complex_building'),
            data.get('delivery_address_postal_code'),
            data.get('delivery_address_business_name'),
            data.get('delivery_address_special_instructions'))

        status_changes = []
        for status_change_data in data.get('status_history'):
            status = Order.Status(status_change_data.get('status'))

            # elastic supports only 3 digits for milliseconds
            changed_at = datetime.datetime.strptime(
                status_change_data.get('datetime') + '000',
                '%Y-%m-%dT%H:%M:%S.%f')

            status_change = self.__reflector.construct(
                Order.StatusChangesHistory.Change, {
                    '__status': status,
                    '__datetime': changed_at
                })
            status_changes.append(status_change)

        status_change_history = Order.StatusChangesHistory(
            tuple(status_changes))

        order_items = []
        for item_data in data.get('order_items'):
            event_code = EventCode(item_data.get('event_code'))
            simple_sku = SimpleSku(item_data.get('simple_sku'))
            product_original_price = Cost(
                item_data.get('product_original_price'))
            product_current_price = Cost(
                item_data.get('product_current_price'))
            fbucks_earnings = Cost(item_data.get('fbucks_amount')
                                   or 0)  # old orders don't have this field
            dtd = Dtd(
                Dtd.Occasion(
                    Name(item_data.get('dtd_occasion_name')),
                    Description(item_data.get('dtd_occasion_description')))
                if item_data.get('dtd_occasion_name') else None,
                datetime.date(
                    int(item_data.get('dtd_date_from').split('-')[0]),
                    int(item_data.get('dtd_date_from').split('-')[1]),
                    int(item_data.get('dtd_date_from').split('-')[2])),
                datetime.date(int(item_data.get('dtd_date_to').split('-')[0]),
                              int(item_data.get('dtd_date_to').split('-')[1]),
                              int(item_data.get('dtd_date_to').split('-')[2])),
                int(item_data.get('dtd_working_days_from')),
                int(item_data.get('dtd_working_days_to')))
            qty_ordered = Qty(int(item_data.get('qty_ordered')))
            qty_return_requested = Qty(
                int(item_data.get('qty_return_requested') or 0))
            qty_return_returned = Qty(
                int(item_data.get('qty_return_returned') or 0))
            qty_cancelled_before_payment = Qty(
                int(item_data.get('qty_cancelled_before_payment') or 0))
            qty_cancelled_after_payment_requested = Qty(
                int(
                    item_data.get('qty_cancelled_after_payment_requested')
                    or 0))
            qty_cancelled_after_payment_cancelled = Qty(
                int(
                    item_data.get('qty_cancelled_after_payment_cancelled')
                    or 0))
            qty_refunded = Qty(int(item_data.get('qty_refunded') or 0))

            # elastic supports only 3 digits for milliseconds
            qty_modified_at = datetime.datetime.strptime(
                (
                    # "qty_modified_at" may not exist for old data (dev, test),
                    # but it's hard to make changes in elastic, so...
                    # @todo : create migration tool.
                    item_data.get('qty_modified_at')
                    or status_change_history.get_last().datetime.strftime(
                        '%Y-%m-%dT%H:%M:%S.%f')[:-3]) + '000',
                '%Y-%m-%dT%H:%M:%S.%f')

            order_item = self.__reflector.construct(
                Order.Item, {
                    '__event_code': event_code,
                    '__simple_sku': simple_sku,
                    '__product_original_price': product_original_price,
                    '__product_current_price': product_current_price,
                    '__dtd': dtd,
                    '__qty_ordered': qty_ordered,
                    '__qty_return_requested': qty_return_requested,
                    '__qty_return_returned': qty_return_returned,
                    '__qty_cancelled_before_payment':
                    qty_cancelled_before_payment,
                    '__qty_cancelled_after_payment_requested':
                    qty_cancelled_after_payment_requested,
                    '__qty_cancelled_after_payment_cancelled':
                    qty_cancelled_after_payment_cancelled,
                    '__qty_refunded': qty_refunded,
                    '__qty_modified_at': qty_modified_at,
                    '__fbucks_earnings': fbucks_earnings
                })
            order_items.append(order_item)

        order = self.__reflector.construct(
            Order, {
                '__order_number': order_number,
                '__customer_id': customer_id,
                '__items': order_items,
                '__delivery_address': delivery_address,
                '__delivery_cost': delivery_cost,
                '__vat_percent': vat_percent,
                '__payment_method': payment_method,
                '__status_history': status_change_history,
                '__credits_spent': credits_spent,
            })

        return order
Пример #13
0
    def cancellation_create_submit():
        user = __get_user()
        orders_storage = OrderStorageImplementation()

        try:
            order_number = Order.Number(
                blueprint.current_request.json_body.get('order_number'))
            items = [{
                'simple_sku': SimpleSku(item.get('simple_sku')),
                'qty': Qty(item.get('qty'))
            } for item in blueprint.current_request.json_body.get('items')
                     or []]
            additional_comment = str(
                blueprint.current_request.json_body.get('additional_comment')
                or '') or None

            refund_method_input_data = blueprint.current_request.json_body.get(
                'refund_method') or {}
            refund_method_input_data['type'] = str(
                refund_method_input_data.get('type') or '')
            refund_method_input_data['params'] = refund_method_input_data.get(
                'params') or {}

        except ValueError:
            raise BadRequestError('Incorrect Input Data!')

        order = orders_storage.load(order_number)
        if not order:
            raise NotFoundError('Order #{} does not exist!'.format(
                order_number.value))
        elif order.customer_id.value != user.id:
            raise ForbiddenError('Order #{} is not your!'.format(
                order_number.value))
        elif not order.is_cancellable:
            raise UnprocessableEntityError(
                'Order #{} is not Cancellable!'.format(order.number.value))

        # @todo : refactoring ???
        try:
            if order.was_paid:
                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

                if not refund_method_instance:
                    error_message = 'Incorrect Input Data! Refund method {} is not allowed for selected orders!'
                    raise BadRequestError(
                        error_message.format(refund_method_input_data['type']))

                cancellation_request = __cancellation_create_submit_after_payment(
                    order, items, refund_method_instance, additional_comment)

                return {'request_number': cancellation_request.number.value}
            else:
                __cancellation_create_submit_before_payment(order, items)

                return {
                    'Code': 'Success',
                    'Message': 'Success',
                }
        except ApplicationLogicException as e:
            raise UnprocessableEntityError(str(e))
Пример #14
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
Пример #15
0
 def set_cart_product_qty(self, cart_id: str, simple_sku: str, qty: int) -> None:
     cart_id = Id(cart_id)
     simple_sku = SimpleSku(simple_sku)
     qty = Qty(qty)
     self.__service.set_cart_product_qty(cart_id, simple_sku, qty)