예제 #1
0
    def credit_cards_shopper_result():
        # Attention! All payment logic is done in webhooks!

        # Theoretically we can just get the last order by customer,
        # because currently it's hard for user to do some tricks here,
        # but event in this case user just gets info about his another order.

        order_storage = OrderStorageImplementation()

        try:
            user = __get_user()

            last_order: Optional[Order] = None
            orders = order_storage.get_all_for_customer(Id(user.id))
            for order in orders:
                if not last_order or order.created_at > last_order.created_at:
                    last_order = order

            if not last_order:
                raise UnprocessableEntityError('No orders - something wrong!')

            return {'order_number': last_order.number.value}

        except BaseException as e:
            return http_response_exception_or_throw(e)
예제 #2
0
    def regular_eft_payment_proof_send():
        order_storage = OrderStorageImplementation()
        logger = Logger()
        file_storage = FileStorageImplementation()
        sqs_sender = SqsSenderImplementation()

        try:
            user_id = __get_user().id

            request_data = blueprint.current_request.json_body
            order_number_value = str(request_data.get('order_number', '')).strip()
            file_id = str(request_data.get('file_id', '')).strip()
            if not order_number_value:
                raise HttpIncorrectInputDataException('order_number is required!')
            elif not file_id:
                raise HttpIncorrectInputDataException('file_id is required!')

            order_number = Order.Number(order_number_value)
            order = order_storage.load(order_number)
            if not order:
                raise HttpNotFoundException('Order "{}" does not exist!'.format(order_number))
            elif order.customer_id.value != user_id:
                raise HttpAccessDenyException('Access Denied!')
            elif not isinstance(order.payment_method, RegularEftOrderPaymentMethod):
                raise ApplicationLogicException('Order "{}" does not have Regular EFT payment!')

            file = file_storage.get(file_id)
            if not file:
                raise HttpNotFoundException('File does not exist!')

            def __log_flow(text: str) -> None:
                logger.log_simple('Regular EFT : Sending POP : {} : {}'.format(order.number.value, text))

            __log_flow('Order Updating...')
            order.status = Order.Status(Order.Status.PAYMENT_SENT)
            order_storage.save(order)
            __log_flow('Order Updated!')

            try:
                __log_flow('Order SQS Sending...')
                sqs_sender.send(OrderChangeSqsSenderEvent(order))
                __log_flow('Order SQS Sent!')
            except BaseException as e:
                logger.log_exception(e)
                __log_flow('Order SQS NOT Sent because of Error: {}!'.format(str(e)))

            try:
                __log_flow('Regular EFT POP SQS Sending...')
                sqs_sender.send(RegularEftProofUploadedSqsSenderEvent(order, file))
                __log_flow('Regular EFT POP SQS Sent!')
            except BaseException as e:
                logger.log_exception(e)
                __log_flow('Regular EFT POP SQS NOT Sent because of Error: {}!'.format(str(e)))

            return {
                'Code': 'Success',
                'Message': 'Success',
            }
        except BaseException as e:
            return http_response_exception_or_throw(e)
예제 #3
0
 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()
예제 #4
0
    def mobicred_shopper_result():
        """
        https://support.peachpayments.com/hc/en-us/articles/360026704471-Mobicred-integration-guide
        mobicred_api_resource_path = str(blueprint.current_request.get_query_parameter('resourcePath') or '').strip()
        """

        # Attention! All payment logic is done in webhooks!

        # Theoretically we can just get the last order by customer,
        # because currently it's hard for user to do some tricks here,
        # but event in this case user just gets info about his another order.

        user = blueprint.current_request.current_user
        if user.is_anyonimous:
            raise UnauthorizedError('Authentication is required!')

        order_storage = OrderStorageImplementation()

        last_order: Optional[Order] = None
        orders = order_storage.get_all_for_customer(Id(user.id))
        for order in orders:
            if not last_order or order.created_at > last_order.created_at:
                last_order = order

        if not last_order:
            raise UnprocessableEntityError('No orders - something wrong!')

        return {'order_number': last_order.number.value}
예제 #5
0
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')
예제 #6
0
    def orders_list():
        orders_storage = OrderStorageImplementation()

        try:
            user_id = __get_user_id()
            customer_id = Id(user_id)
            orders = orders_storage.get_all_for_customer(customer_id)
            return __orders_response(orders)
        except BaseException as e:
            return http_response_exception_or_throw(e)
예제 #7
0
 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()
예제 #8
0
    def info(order_number):
        try:
            __check_header_or_error()

            order_storage = OrderStorageImplementation()
            order = order_storage.load(Order.Number(order_number))
            if not order:
                raise HttpNotFoundException('Order does not exist!')

            return {'order': _order_info(order)}
        except BaseException as e:
            return http_response_exception_or_throw(e)
예제 #9
0
    def returns_list():
        returns_storage = ReturnRequestStorageImplementation()
        orders_storage = OrderStorageImplementation()

        customer_id = Id(__get_user().id)
        returns = returns_storage.get_all_for_customer(customer_id)

        orders_map = {}
        _order_numbers = [
            return_request_item.order_number for return_request in returns
            for return_request_item in return_request.items
        ]
        _orders = orders_storage.get_all_by_numbers(tuple(_order_numbers))
        for _order_number in _order_numbers:
            for _order in _orders:
                if _order.number.value == _order_number.value:
                    orders_map[_order_number.value] = _order
                    break
            else:
                raise ValueError(
                    '{} - Unable to find Order #{} for Customer\'s #{} returns'
                    .format(returns_list.__qualname__, _order_number.value,
                            __get_user().id))

        response = []
        for return_request in returns:
            items = []
            for return_item in return_request.items:
                order = orders_map[return_item.order_number.value]
                items.append({
                    'order_number':
                    return_item.order_number.value,
                    'ordered_at':
                    order.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                    'cost':
                    return_item.cost.value,
                })

            response.append({
                'request_number':
                return_request.number.value,
                'requested_at':
                return_request.requested_at.strftime('%Y-%m-%d %H:%M:%S'),
                'items':
                items,
                'status': {
                    'value': return_request.total_status.value,
                    'label': return_request.total_status.label,
                }
            })

        return response
예제 #10
0
    def __cancellation_create_submit_before_payment(order: Order, items):
        orders_storage = OrderStorageImplementation()
        products_storage = ProductStorageImplementation()
        sqs_sender = SqsSenderImplementation()
        logger = Logger()

        def __log_flow(text: str):
            logger.log_simple(
                'Cancellation for Order #{} Before Payment: {}'.format(
                    order.number.value, text))

        __log_flow('Start - {}'.format([{
            'simple_sku': item['simple_sku'].value,
            'qty': item['qty'].value,
        } for item in items]))

        # updates
        products_to_update = {}
        for item in items:
            products_to_update[
                item['simple_sku'].value] = products_storage.load(
                    item['simple_sku'])
            products_to_update[item['simple_sku'].value].restore_qty(
                item['qty'])
            order.cancel_before_payment(item['simple_sku'], item['qty'])

        order_change_event = OrderChangeSqsSenderEvent(order)

        # save order
        __log_flow('Order Saving...')
        orders_storage.save(order)
        __log_flow('Order Saved!')

        # saving products
        __log_flow('Products Saving... {}'.format(
            tuple(products_to_update.keys())))
        for product in tuple(products_to_update.values()):
            __log_flow('Product {} Saving...'.format(product.simple_sku.value))
            products_storage.update(product)
            __log_flow('Product {} Saved!'.format(product.simple_sku.value))
        __log_flow('Products Saved!')

        # send order
        __log_flow('Order SQS Sending...')
        sqs_sender.send(order_change_event)
        __log_flow('Order SQS Sent!')

        __log_flow('End')
예제 #11
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),
        }
예제 #12
0
    def orders_view(order_number):
        orders_storage = OrderStorageImplementation()

        try:
            order_number = str(order_number).strip()
            if not order_number:
                raise HttpIncorrectInputDataException('order_number is required!')

            order_number = Order.Number(order_number)
            order = orders_storage.load(order_number)
            if not order:
                raise HttpNotFoundException('Order does not exist!')

            user_id = __get_user_id()
            customer_id = Id(user_id)
            if order.customer_id != customer_id:
                raise HttpAccessDenyException()

            return __orders_response([order])[0]
        except BaseException as e:
            return http_response_exception_or_throw(e)
예제 #13
0
    def cancellation_get_by_order(order_number):
        user = __get_user()
        orders_storage = OrderStorageImplementation()
        cancel_requests_storage = CancelRequestStorageImplementation()

        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))

        requests = cancel_requests_storage.get_all_by_order_number(
            order.number)
        return __get_cancellation_requests_response(requests)
예제 #14
0
    def cancellation_get(request_number):
        user = __get_user()
        orders_storage = OrderStorageImplementation()
        cancel_requests_storage = CancelRequestStorageImplementation()

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

        request = cancel_requests_storage.get_by_number(request_number)
        if not request:
            raise NotFoundError(
                'Cancellation Request #{} does not exist!'.format(
                    request_number.value))

        order = orders_storage.load(request.order_number)
        if order.customer_id.value != user.id:
            raise ForbiddenError(
                'Cancellation Request #{} is not your!'.format(
                    request.number.value))

        return __get_cancellation_requests_response(tuple([request]))[0]
예제 #15
0
 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())
예제 #16
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')
예제 #17
0
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)
예제 #18
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}
예제 #19
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,
        }
예제 #20
0
    def returns_view(return_number):
        customer_id = Id(__get_user().id)
        returns_storage = ReturnRequestStorageImplementation()
        orders_storage = OrderStorageImplementation()
        products_storage = ProductStorageImplementation()

        return_request = returns_storage.load(
            ReturnRequest.Number(return_number))
        if not return_request:
            raise NotFoundError(
                'Return Request #{} does not exist!'.format(return_number))
        elif return_request.customer_id != customer_id:
            raise ForbiddenError('It is not your Return Request!')

        response = {
            'request_number':
            return_request.number.value,
            'requested_at':
            return_request.requested_at.strftime('%Y-%m-%d %H:%M:%S'),
            'items': [],
            'delivery_method':
            return_request.delivery_method.label,
            'refund_method':
            return_request.refund_method.label,
            'status': {
                'value': return_request.total_status.value,
                'label': return_request.total_status.label,
            }
        }

        orders_map = {}
        products_map = {}
        for return_item in return_request.items:
            product = products_map.get(
                return_item.simple_sku.value) or products_storage.load(
                    return_item.simple_sku)
            products_map[return_item.simple_sku.value] = product

            order = orders_map.get(
                return_item.order_number.value) or orders_storage.load(
                    return_item.order_number)
            orders_map[return_item.order_number.value] = order

            response['items'].append({
                'order_number':
                return_item.order_number.value,
                'simple_sku':
                return_item.simple_sku.value,
                'product_name':
                product.name.value,
                'product_image_url':
                product.image_urls[0] if product.image_urls else None,
                'size_name':
                product.size_name.value,
                'ordered_at':
                order.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                'cost':
                return_item.cost.value,
                'qty':
                return_item.qty.value,
                'status':
                return_item.status.label,
                'reason':
                return_item.reason.label,
                'attached_files': [{
                    'url': file.url
                } for file in return_item.attached_files],
                'additional_comment':
                return_item.additional_comment.value
                if return_item.additional_comment else None,
            })

        return response
예제 #21
0
    def customer_credits_checkout():
        checkout_storage = CheckoutStorageImplementation()
        order_storage = OrderStorageImplementation()
        order_app_service = OrderAppService()
        cart_service = CartAppService()
        checkout_service = CheckoutAppService()
        sqs_sender = SqsSenderImplementation()
        logger = Logger()

        try:
            user = __get_user()

            # @todo : refactoring
            checkout = checkout_storage.load(Id(user.id))
            if not checkout:
                raise ApplicationLogicException('Checkout does not exist!')
            elif checkout.total_due.value != 0:
                raise ApplicationLogicException('Unable to checkout not 0 amount with Customer Credits!')

            order = order_app_service.get_waiting_for_payment_by_checkout_or_checkout_new(user.id)

            def __log_flow(text: str) -> None:
                logger.log_simple('Customer Credits Payment Log for Order #{} : {}'.format(
                    order.number.value,
                    text
                ))

            __log_flow('Start')

            try:
                __log_flow('Credits Spending...')
                # Attention!
                # Currently we use f-bucks only! Other credits are not available for now!
                # @todo : other credit types
                # @todo : copy-paste code
                # @todo : when reservation of credits amount will be done, perhaps, use sqs to spend credits
                """"""
                from chalicelib.libs.purchase.core import Checkout
                see = Checkout.__init__
                """"""
                # @TODO : refactoring : raw data usage
                import uuid
                import datetime
                from chalicelib.settings import settings
                from chalicelib.libs.core.elastic import Elastic
                fbucks_customer_amount_elastic = Elastic(
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT,
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT,
                )
                fbucks_customer_amount_changes_elastic = Elastic(
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES,
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES,
                )
                fbucks_customer_amount_elastic.update_data(order.customer_id.value, {
                    'script': 'ctx._source.amount -= ' + str(order.credit_spent_amount.value)
                })
                fbucks_customer_amount_changes_elastic.create(str(uuid.uuid4()) + str(order.customer_id.value), {
                    "customer_id": order.customer_id.value,
                    "amount": -order.credit_spent_amount.value,
                    "changed_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "order_number": order.number.value,
                })
                __log_flow('Credits Spent!')

                __log_flow('Order Updating...')
                order.payment_method = CustomerCreditsOrderPaymentMethod()
                order.status = Order.Status(Order.Status.PAYMENT_SENT)
                order.status = Order.Status(Order.Status.PAYMENT_RECEIVED)
                order_storage.save(order)
                __log_flow('Order Updated!')
            except BaseException as e:
                __log_flow('Not done because of Error : {}'.format(str(e)))
                raise e

            # send order update to sqs
            try:
                __log_flow('Order Change SQS Sending...')
                sqs_sender.send(OrderChangeSqsSenderEvent(order))
                __log_flow('Order Change SQS Sent!')
            except BaseException as e:
                __log_flow('Order Change SQS NOT Sent because of Error: {}!'.format(str(e)))
                logger.log_exception(e)

            # flush cart
            try:
                __log_flow('Cart Flushing...')
                cart_service.clear_cart(user.session_id)
                __log_flow('Cart Flushed!')
            except BaseException as e:
                __log_flow('Cart NOT Flushed because of Error: {}'.format(str(e)))
                logger.log_exception(e)

            # flush checkout
            try:
                __log_flow('Checkout Flushing...')
                checkout_service.remove(user.id)
                __log_flow('Checkout Flushed!')
            except BaseException as e:
                __log_flow('Checkout NOT Flushed because of Error: {}'.format(str(e)))
                logger.log_exception(e)

            result = {
                'order_number': order.number.value
            }

            __log_flow('End')

            return result
        except BaseException as e:
            logger.log_exception(e)
            return http_response_exception_or_throw(e)
예제 #22
0
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')
예제 #23
0
 def __init__(self):
     self.__messages_storage = MessageStorageImplementation()
     self.__order_storage = OrderStorageImplementation()
     self.__sqs_sender = SqsSenderImplementation()
     self.__logger = Logger()
예제 #24
0
class OrderChangeSqsHandler(SqsHandlerInterface):
    def __init__(self):
        self.__messages_storage = MessageStorageImplementation()
        self.__order_storage = OrderStorageImplementation()
        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))

        data = {
            'order_number': sqs_message.message_data.get('order_number', '') or '',
            'order_status_mpc': sqs_message.message_data.get('order_status_mpc', '') or '',
            'popup_message': {
                'customer_email': sqs_message.message_data.get('popup_message').get('customer_email'),
                'message_title': sqs_message.message_data.get('popup_message').get('message_title'),
                'message_text': sqs_message.message_data.get('popup_message').get('message_text'),
            } if sqs_message.message_data.get('popup_message', None) or None else None,
        }

        __log_flow('Order: Updating...')
        order_number = Order.Number(data.get('order_number'))
        order = self.__order_storage.load(order_number)
        if not order:
            raise ValueError('Order "{}" does not exist in the MPC!')

        mpc_order_status = str(data.get('order_status_mpc'))
        order.status = Order.Status(mpc_order_status)
        __log_flow('Order: Updated!')

        __log_flow('Order: Saving...')
        self.__order_storage.save(order)
        __log_flow('Order: Saved!')

        # Attention!
        # We need to send-back order changes because of compatibility reason.
        __log_flow('Order: SQS Sending-Back...')
        self.__sqs_sender.send(OrderChangeSqsSenderEvent(order))
        __log_flow('Order: SQS Sent-Back!')

        # add message, if is needed (silently)
        try:
            message_data = data.get('popup_message') or None
            if message_data:
                __log_flow('Notification popup: Adding...')
                message = Message(
                    str(uuid.uuid4()),
                    message_data.get('customer_id'),
                    message_data.get('message_title'),
                    message_data.get('message_text'),
                )
                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')
예제 #25
0
    def regular_eft_checkout():
        checkout_storage = CheckoutStorageImplementation()
        order_storage = OrderStorageImplementation()
        order_app_service = OrderAppService()
        logger = Logger()
        mailer = MailerImplementation()

        # 1. Get or create order. Critical!
        # ------------------------------------------------------

        try:
            user = __get_user()

            # @todo : refactoring
            checkout = checkout_storage.load(Id(user.id))
            if not checkout:
                raise ApplicationLogicException('Checkout does not exist!')
            elif checkout.total_due.value == 0:
                raise ApplicationLogicException('Unable to checkout 0 amount with Regular Eft!')

            order = order_app_service.get_waiting_for_payment_by_checkout_or_checkout_new(user.id)

            def __log_order_flow(text: str) -> None:
                logger.log_simple('Regular EFT : Checkout : {} : {}'.format(order.number.value, text))

            __log_order_flow('Start')

            # Attention!
            # Currently we use f-bucks only! Other credits are not available for now!
            # @todo : other credit types
            # @todo : copy-paste code
            # @todo : when reservation of credits amount will be done, perhaps, use sqs to spend credits
            if order.credit_spent_amount.value > 0:
                __log_order_flow('Spending Credits...')
                """"""
                from chalicelib.libs.purchase.core import Checkout
                see = Checkout.__init__
                """"""
                # @TODO : refactoring : raw data usage
                import uuid
                import datetime
                from chalicelib.settings import settings
                from chalicelib.libs.core.elastic import Elastic
                fbucks_customer_amount_elastic = Elastic(
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT,
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT,
                )
                fbucks_customer_amount_changes_elastic = Elastic(
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES,
                    settings.AWS_ELASTICSEARCH_FBUCKS_CUSTOMER_AMOUNT_CHANGES,
                )
                fbucks_customer_amount_elastic.update_data(order.customer_id.value, {
                    'script': 'ctx._source.amount -= ' + str(order.credit_spent_amount.value),
                })
                fbucks_customer_amount_changes_elastic.create(str(uuid.uuid4()) + str(order.customer_id.value), {
                    "customer_id": order.customer_id.value,
                    "amount": -order.credit_spent_amount.value,
                    "changed_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "order_number": order.number.value,
                })
                __log_order_flow('Spending Credits: Done!')

            __log_order_flow('Order Updating...')
            order.payment_method = RegularEftOrderPaymentMethod()
            order_storage.save(order)
            __log_order_flow('Order Updated!')
        except BaseException as e:
            logger.log_exception(e)
            return http_response_exception_or_throw(e)

        # 2. Send eft email. Not critical.
        # Theoretically can be redone or downloaded manually.
        # ------------------------------------------------------

        try:
            __log_order_flow('EFT Email Sending...')
            message = RegularEftBankDetailsMailMessage(order)
            mailer.send(message)
            __log_order_flow('EFT Email Sent!')
        except BaseException as e:
            logger.log_exception(e)
            __log_order_flow('EFT Email Not Sent because of Error: {}'.format(str(e)))

        # 3. Flush cart, checkout. Not critical.
        # ------------------------------------------------------

        # flush cart
        try:
            __log_order_flow('Cart Flushing...')
            from chalicelib.libs.purchase.cart.service import CartAppService
            cart_service = CartAppService()
            cart_service.clear_cart(user.session_id)
            __log_order_flow('Cart Flushed!')
        except BaseException as e:
            logger.log_exception(e)
            __log_order_flow('Cart Not Flushed because of Error: {}'.format(str(e)))

        # flush checkout
        try:
            __log_order_flow('Checkout Flushing...')
            from chalicelib.libs.purchase.checkout.service import CheckoutAppService
            checkout_service = CheckoutAppService()
            checkout_service.remove(user.id)
            __log_order_flow('Checkout Flushed!')
        except BaseException as e:
            logger.log_exception(e)
            __log_order_flow('Checkout Not Flushed because of Error: {}'.format(str(e)))

        return {
            'order_number': order.number.value
        }
예제 #26
0
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)))
예제 #27
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))
예제 #28
0
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)
예제 #29
0
    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()
예제 #30
0
    def __cancellation_create_submit_after_payment(
            order: Order, items, refund_method: RefundMethodAbstract,
            additional_comment: Optional[str]) -> CancelRequest:
        orders_storage = OrderStorageImplementation()
        cancellation_storage = CancelRequestStorageImplementation()
        sqs_sender = SqsSenderImplementation()
        logger = Logger()

        def __log_flow(text: str):
            logger.log_simple(
                'Cancellation for Order #{} After Payment: {}'.format(
                    order.number.value, text))

        __log_flow('Start - {}'.format({
            'items': [{
                'simple_sku': item['simple_sku'].value,
                'qty': item['qty'].value,
            } for item in items],
            'additional_comment':
            additional_comment
        }))

        # updates
        cancellation_request = CancelRequest(
            CancelRequest.Number(datetime.datetime.now().strftime('%y%j03%f')),
            order.number,
            tuple([
                CancelRequest.Item(item['simple_sku'], item['qty'])
                for item in items
            ]), refund_method,
            CancelRequest.AdditionalComment(additional_comment)
            if additional_comment else None)

        for item in items:
            order.request_cancellation_after_payment(item['simple_sku'],
                                                     item['qty'])

        order_change_event = OrderChangeSqsSenderEvent(order)
        cancel_request_event = CancelRequestPaidOrderSqsSenderEvent(
            cancellation_request)

        # save request
        __log_flow('Cancel Request Saving...')
        cancellation_storage.save(cancellation_request)
        __log_flow('Cancel Request Saved!')

        # save order
        __log_flow('Order Saving...')
        orders_storage.save(order)
        __log_flow('Order Saved!')

        # send request
        __log_flow('Cancel Request SQS Sending...')
        sqs_sender.send(cancel_request_event)
        __log_flow('Cancel Request SQS Sent!')

        # send order
        __log_flow('Order SQS Sending...')
        sqs_sender.send(order_change_event)
        __log_flow('Order SQS Sent!')

        __log_flow('End')

        return cancellation_request