예제 #1
0
    def test_cancel_line_item_in_order(self):
        original_quantity = 2
        order_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = order_item.current_price().amount * original_quantity
        data = {
            'line_items': [{'item_id': unicode(order_item.id), 'quantity': original_quantity}],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
                }
            }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id), data=json.dumps(data), content_type='application/json', headers=[('X-Requested-With', 'XMLHttpRequest'), ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_json = json.loads(resp.data)['result']
        self.assertEquals(resp_json['final_amount'], total_amount)

        order = Order.query.get(resp_json['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order, online_payment=online_payment, amount=order_amounts.final_amount, currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        refund_amount = total_amount - 1
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data={'id': buid()}))
        pre_refund_transactions_count = order.refund_transactions.count()
        formdata = {
            'amount': refund_amount,
            'internal_note': 'internal reference',
            'refund_description': 'receipt description',
            'note_to_user': '******'
        }
        refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
        partial_refund_args = {
            'order': order,
            'form': refund_form,
            'request_method': 'POST'
        }
        with app.request_context(self.post_env):
            process_partial_refund_for_order(partial_refund_args)
        self.assertEquals(pre_refund_transactions_count + 1, order.refund_transactions.count())

        first_line_item = order.line_items[0]
        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data={'id': buid()}))
        process_line_item_cancellation(first_line_item)
        self.assertEquals(first_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        expected_refund_amount = total_amount - refund_amount
        refund_transaction1 = PaymentTransaction.query.filter_by(order=order, transaction_type=TRANSACTION_TYPE.REFUND).order_by(PaymentTransaction.created_at.desc()).first()
        self.assertEquals(refund_transaction1.amount, expected_refund_amount)
예제 #2
0
def partial_refund(**kwargs):
    """
    Processes a partial refund for an order.
    Params are order_id, amount, internal_note, refund_description, note_to_user
    """
    form_dict = {
        'amount': kwargs['amount'],
        'internal_note': kwargs['internal_note'],
        'refund_description': kwargs['refund_description'],
        'note_to_user': kwargs['note_to_user']
    }
    order = Order.query.get(kwargs['order_id'])

    with app.test_request_context():
        process_partial_refund_for_order(order, form_dict)
예제 #3
0
def partial_refund(**kwargs):
    """
    Processes a partial refund for an order.
    Params are order_id, amount, internal_note, refund_description, note_to_user
    """
    form_dict = {
        'amount': kwargs['amount'],
        'internal_note': kwargs['internal_note'],
        'refund_description': kwargs['refund_description'],
        'note_to_user': kwargs['note_to_user'],
    }
    order = Order.query.get(kwargs['order_id'])

    with app.test_request_context():
        process_partial_refund_for_order(order, form_dict)
예제 #4
0
    def test_partial_refund_in_order(self):
        original_quantity = 5
        discounted_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = discounted_item.current_price(
        ).amount * original_quantity
        data = {
            'line_items': [{
                'item_id': unicode(discounted_item.id),
                'quantity': original_quantity
            }],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
            }
        }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id),
                                data=json.dumps(data),
                                content_type='application/json',
                                headers=[('X-Requested-With',
                                          'XMLHttpRequest'),
                                         ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_data = json.loads(resp.data)['result']
        self.assertEquals(
            resp_data['final_amount'],
            (total_amount - 5 * total_amount / decimal.Decimal(100)))

        order = Order.query.get(resp_data['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment',
                                       order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order,
                                         online_payment=online_payment,
                                         amount=order_amounts.final_amount,
                                         currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        valid_refund_amount = 500
        valid_refund_dict = {
            'amount': valid_refund_amount,
            'internal_note': 'internal reference',
            'note_to_user': '******',
            'refund_description': 'test refund'
        }
        process_partial_refund_for_order(order, valid_refund_dict)
        refund_transactions = order.transactions.filter_by(
            transaction_type=TRANSACTION_TYPE.REFUND).all()
        self.assertIsInstance(refund_transactions[0].refunded_at,
                              datetime.datetime)
        self.assertEquals(refund_transactions[0].amount,
                          decimal.Decimal(valid_refund_amount))
        self.assertEquals(refund_transactions[0].internal_note,
                          valid_refund_dict['internal_note'])
        self.assertEquals(refund_transactions[0].note_to_user,
                          valid_refund_dict['note_to_user'])
        self.assertEquals(refund_transactions[0].refund_description,
                          valid_refund_dict['refund_description'])

        invalid_refund_amount = 100000000
        invalid_refund_dict = {'amount': invalid_refund_amount}
        resp = process_partial_refund_for_order(order, invalid_refund_dict)
        self.assertEquals(resp.status_code, 403)
        refund_transactions = order.transactions.filter_by(
            transaction_type=TRANSACTION_TYPE.REFUND).all()
        self.assertEquals(refund_transactions[0].amount,
                          decimal.Decimal(valid_refund_amount))

        resp = self.make_free_order()
        self.assertEquals(resp.status_code, 201)
        resp_data = json.loads(resp.data)['result']
        order = Order.query.get(resp_data.get('order_id'))
        invalid_refund_amount = 100000000
        invalid_refund_dict = {'amount': invalid_refund_amount}
        refund_resp = process_partial_refund_for_order(order,
                                                       invalid_refund_dict)
        self.assertEquals(refund_resp.status_code, 403)
예제 #5
0
    def test_cancel_line_item_in_bulk_order(self):
        original_quantity = 5
        discounted_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = discounted_item.current_price(
        ).amount * original_quantity
        data = {
            'line_items': [{
                'item_id': unicode(discounted_item.id),
                'quantity': original_quantity
            }],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
            }
        }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id),
                                data=json.dumps(data),
                                content_type='application/json',
                                headers=[('X-Requested-With',
                                          'XMLHttpRequest'),
                                         ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_json = json.loads(resp.data)['result']
        self.assertEquals(
            resp_json['final_amount'],
            (total_amount - 5 * total_amount / decimal.Decimal(100)))

        order = Order.query.get(resp_json['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment',
                                       order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order,
                                         online_payment=online_payment,
                                         amount=order_amounts.final_amount,
                                         currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        first_line_item = order.line_items[0]
        to_be_void_line_items = order.line_items[1:]
        precancellation_order_amount = order.net_amount
        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        process_line_item_cancellation(first_line_item)
        self.assertEquals(first_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        for void_line_item in to_be_void_line_items:
            self.assertEquals(void_line_item.status, LINE_ITEM_STATUS.VOID)
        expected_refund_amount = precancellation_order_amount - order.get_amounts(
            LINE_ITEM_STATUS.CONFIRMED).final_amount
        refund_transaction1 = PaymentTransaction.query.filter_by(
            order=order, transaction_type=TRANSACTION_TYPE.REFUND).first()
        self.assertEquals(refund_transaction1.amount, expected_refund_amount)

        second_line_item = order.get_confirmed_line_items[0]
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        process_line_item_cancellation(second_line_item)
        self.assertEquals(second_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        refund_transaction2 = PaymentTransaction.query.filter_by(
            order=order, transaction_type=TRANSACTION_TYPE.REFUND).order_by(
                'created_at desc').first()
        self.assertEquals(refund_transaction2.amount,
                          second_line_item.final_amount)

        # test failed cancellation
        third_line_item = order.get_confirmed_line_items[0]
        failed_response = make_response('', 400)
        failed_response.content = 'failed'
        razorpay.refund_payment = MagicMock(return_value=failed_response)
        self.assertRaises(
            PaymentGatewayError,
            lambda: process_line_item_cancellation(third_line_item))

        # refund the remaining amount paid, and attempt to cancel a line item
        # this should cancel the line item without resulting in a new refund transaction
        refund_amount = order.net_amount
        refund_dict = {
            'amount': refund_amount,
            'internal_note': 'internal reference',
            'note_to_user': '******'
        }
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        process_partial_refund_for_order(order, refund_dict)
        third_line_item = order.get_confirmed_line_items[0]
        pre_cancellation_transactions_count = order.refund_transactions.count()
        cancelled_refund_amount = process_line_item_cancellation(
            third_line_item)
        self.assertEquals(cancelled_refund_amount, decimal.Decimal(0))
        self.assertEquals(pre_cancellation_transactions_count,
                          order.refund_transactions.count())

        # test free line item cancellation
        free_order_resp = self.make_free_order()
        free_order_resp_data = json.loads(free_order_resp.data)['result']
        free_order = Order.query.get(free_order_resp_data.get('order_id'))
        free_line_item = free_order.line_items[0]
        process_line_item_cancellation(free_line_item)
        self.assertEquals(free_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        self.assertEquals(free_order.transactions.count(), 0)
예제 #6
0
    def test_cancel_line_item_in_order(self):
        original_quantity = 2
        order_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = order_item.current_price().amount * original_quantity
        data = {
            'line_items': [{
                'item_id': unicode(order_item.id),
                'quantity': original_quantity
            }],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
            }
        }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id),
                                data=json.dumps(data),
                                content_type='application/json',
                                headers=[('X-Requested-With',
                                          'XMLHttpRequest'),
                                         ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_json = json.loads(resp.data)['result']
        self.assertEquals(resp_json['final_amount'], total_amount)

        order = Order.query.get(resp_json['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment',
                                       order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order,
                                         online_payment=online_payment,
                                         amount=order_amounts.final_amount,
                                         currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        refund_amount = total_amount - 1
        refund_dict = {
            'amount': refund_amount,
            'internal_note': 'internal reference',
            'note_to_user': '******'
        }
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        pre_refund_transactions_count = order.refund_transactions.count()
        process_partial_refund_for_order(order, refund_dict)
        self.assertEquals(pre_refund_transactions_count + 1,
                          order.refund_transactions.count())

        first_line_item = order.line_items[0]
        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(
            response_data={'id': buid()}))
        process_line_item_cancellation(first_line_item)
        self.assertEquals(first_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        expected_refund_amount = total_amount - refund_amount
        refund_transaction1 = PaymentTransaction.query.filter_by(
            order=order, transaction_type=TRANSACTION_TYPE.REFUND).order_by(
                'created_at desc').first()
        self.assertEquals(refund_transaction1.amount, expected_refund_amount)
예제 #7
0
    def test_partial_refund_in_order(self):
        original_quantity = 5
        discounted_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = discounted_item.current_price().amount * original_quantity
        data = {
            'line_items': [{'item_id': unicode(discounted_item.id), 'quantity': original_quantity}],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
                }
            }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id), data=json.dumps(data), content_type='application/json', headers=[('X-Requested-With', 'XMLHttpRequest'), ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_data = json.loads(resp.data)['result']
        self.assertEquals(resp_data['final_amount'], (total_amount - 5 * total_amount / decimal.Decimal(100)))

        order = Order.query.get(resp_data['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order, online_payment=online_payment, amount=order_amounts.final_amount, currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data={'id': buid()}))
        valid_refund_amount = 500

        formdata = {
            'amount': valid_refund_amount,
            'internal_note': 'internal reference',
            'note_to_user': '******',
            'refund_description': 'test refund'
        }
        refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
        partial_refund_args = {
            'order': order,
            'form': refund_form,
            'request_method': 'POST'
        }
        with app.request_context(self.post_env):
            process_partial_refund_for_order(partial_refund_args)

        refund_transactions = order.transactions.filter_by(transaction_type=TRANSACTION_TYPE.REFUND).all()
        self.assertIsInstance(refund_transactions[0].refunded_at, datetime.datetime)
        self.assertEquals(refund_transactions[0].amount, decimal.Decimal(valid_refund_amount))
        self.assertEquals(refund_transactions[0].internal_note, formdata['internal_note'])
        self.assertEquals(refund_transactions[0].note_to_user, formdata['note_to_user'])
        self.assertEquals(refund_transactions[0].refund_description, formdata['refund_description'])

        invalid_refund_amount = 100000000
        formdata = {
            'amount': invalid_refund_amount,
        }
        refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
        partial_refund_args = {
            'order': order,
            'form': refund_form,
            'request_method': 'POST'
        }
        with app.request_context(self.post_env):
            resp = process_partial_refund_for_order(partial_refund_args)

        self.assertEquals(resp.status_code, 403)
        refund_transactions = order.transactions.filter_by(transaction_type=TRANSACTION_TYPE.REFUND).all()
        self.assertEquals(refund_transactions[0].amount, decimal.Decimal(valid_refund_amount))

        resp = self.make_free_order()
        self.assertEquals(resp.status_code, 201)
        resp_data = json.loads(resp.data)['result']
        order = Order.query.get(resp_data.get('order_id'))
        invalid_refund_amount = 100000000

        formdata = {
            'amount': invalid_refund_amount,
        }
        refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
        partial_refund_args = {
            'order': order,
            'form': refund_form,
            'request_method': 'POST'
        }
        with app.request_context(self.post_env):
            refund_resp = process_partial_refund_for_order(partial_refund_args)

        self.assertEquals(refund_resp.status_code, 403)
예제 #8
0
    def test_cancel_line_item_in_bulk_order(self):
        original_quantity = 5
        discounted_item = Item.query.filter_by(name='t-shirt').first()
        total_amount = discounted_item.current_price().amount * original_quantity
        data = {
            'line_items': [{'item_id': unicode(discounted_item.id), 'quantity': original_quantity}],
            'buyer': {
                'fullname': 'Testing',
                'phone': '9814141414',
                'email': '*****@*****.**',
                }
            }
        ic = ItemCollection.query.first()
        # make a purchase order
        resp = self.client.post('/ic/{ic}/order'.format(ic=ic.id), data=json.dumps(data), content_type='application/json', headers=[('X-Requested-With', 'XMLHttpRequest'), ('Origin', app.config['BASE_URL'])])
        self.assertEquals(resp.status_code, 201)
        resp_json = json.loads(resp.data)['result']
        self.assertEquals(resp_json['final_amount'], (total_amount - 5 * total_amount / decimal.Decimal(100)))

        order = Order.query.get(resp_json['order_id'])
        # Create fake payment and transaction objects
        online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
        online_payment.confirm()
        order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
        transaction = PaymentTransaction(order=order, online_payment=online_payment, amount=order_amounts.final_amount, currency=CURRENCY.INR)
        db.session.add(transaction)
        order.confirm_sale()
        db.session.commit()

        first_line_item = order.line_items[0]
        to_be_void_line_items = order.line_items[1:]
        precancellation_order_amount = order.net_amount
        # Mock Razorpay's API
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data={'id': buid()}))
        process_line_item_cancellation(first_line_item)
        self.assertEquals(first_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        for void_line_item in to_be_void_line_items:
            self.assertEquals(void_line_item.status, LINE_ITEM_STATUS.VOID)
        expected_refund_amount = precancellation_order_amount - order.get_amounts(LINE_ITEM_STATUS.CONFIRMED).final_amount
        refund_transaction1 = PaymentTransaction.query.filter_by(order=order, transaction_type=TRANSACTION_TYPE.REFUND).first()
        self.assertEquals(refund_transaction1.amount, expected_refund_amount)

        second_line_item = order.confirmed_line_items[0]
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data={'id': buid()}))
        process_line_item_cancellation(second_line_item)
        self.assertEquals(second_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        refund_transaction2 = PaymentTransaction.query.filter_by(order=order, transaction_type=TRANSACTION_TYPE.REFUND).order_by(PaymentTransaction.created_at.desc()).first()
        self.assertEquals(refund_transaction2.amount, second_line_item.final_amount)

        # test failed cancellation
        third_line_item = order.confirmed_line_items[0]
        razorpay.refund_payment = MagicMock(
            return_value=MockResponse(
                response_data={
                    "error": {
                        "code": "BAD_REQUEST_ERROR",
                        "description": "The amount is invalid",
                        "field": "amount"
                    }
                },
                status_code=400
            ))
        self.assertRaises(PaymentGatewayError, lambda: process_line_item_cancellation(third_line_item))

        # refund the remaining amount paid, and attempt to cancel a line item
        # this should cancel the line item without resulting in a new refund transaction
        refund_amount = order.net_amount
        refund_dict = {'id': buid(), 'amount': refund_amount, 'internal_note': 'internal reference', 'note_to_user': '******'}
        razorpay.refund_payment = MagicMock(return_value=MockResponse(response_data=refund_dict))

        formdata = {
            'amount': refund_amount,
            'internal_note': 'internal reference',
            'refund_description': 'receipt description',
            'note_to_user': '******'
        }
        refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
        partial_refund_args = {
            'order': order,
            'form': refund_form,
            'request_method': 'POST'
        }
        with app.request_context(self.post_env):
            process_partial_refund_for_order(partial_refund_args)

        third_line_item = order.confirmed_line_items[0]
        pre_cancellation_transactions_count = order.refund_transactions.count()
        cancelled_refund_amount = process_line_item_cancellation(third_line_item)
        self.assertEquals(cancelled_refund_amount, decimal.Decimal(0))
        self.assertEquals(pre_cancellation_transactions_count, order.refund_transactions.count())

        # test free line item cancellation
        free_order_resp = self.make_free_order()
        free_order_resp_data = json.loads(free_order_resp.data)['result']
        free_order = Order.query.get(free_order_resp_data.get('order_id'))
        free_line_item = free_order.line_items[0]
        process_line_item_cancellation(free_line_item)
        self.assertEquals(free_line_item.status, LINE_ITEM_STATUS.CANCELLED)
        self.assertEquals(free_order.transactions.count(), 0)
예제 #9
0
def test_partial_refund_in_order(db_session, client, all_data, post_env):
    original_quantity = 5
    discounted_item = Item.query.filter_by(name='t-shirt').first()
    total_amount = discounted_item.current_price().amount * original_quantity
    data = {
        'line_items': [
            {'item_id': str(discounted_item.id), 'quantity': original_quantity}
        ],
        'buyer': {
            'fullname': 'Testing',
            'phone': '9814141414',
            'email': '*****@*****.**',
        },
    }
    ic = ItemCollection.query.first()
    # make a purchase order
    resp = client.post(
        '/ic/{ic}/order'.format(ic=ic.id),
        data=json.dumps(data),
        content_type='application/json',
        headers=[
            ('X-Requested-With', 'XMLHttpRequest'),
            ('Origin', app.config['BASE_URL']),
        ],
    )
    assert resp.status_code == 201
    resp_data = json.loads(resp.data)['result']
    assert resp_data['final_amount'] == (
        total_amount - 5 * total_amount / decimal.Decimal(100)
    )

    order = Order.query.get(resp_data['order_id'])
    # Create fake payment and transaction objects
    online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
    online_payment.confirm()
    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    transaction = PaymentTransaction(
        order=order,
        online_payment=online_payment,
        amount=order_amounts.final_amount,
        currency=CURRENCY.INR,
    )
    db_session.add(transaction)
    order.confirm_sale()
    db_session.commit()

    # Mock Razorpay's API
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data={'id': buid()})
    )
    valid_refund_amount = 500

    formdata = {
        'amount': valid_refund_amount,
        'internal_note': 'internal reference',
        'note_to_user': '******',
        'refund_description': 'test refund',
    }
    refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
    partial_refund_args = {
        'order': order,
        'form': refund_form,
        'request_method': 'POST',
    }
    with app.request_context(post_env):
        process_partial_refund_for_order(partial_refund_args)

    refund_transactions = order.transactions.filter_by(
        transaction_type=TRANSACTION_TYPE.REFUND
    ).all()
    assert isinstance(refund_transactions[0].refunded_at, datetime.datetime)
    assert refund_transactions[0].amount == decimal.Decimal(valid_refund_amount)
    assert refund_transactions[0].internal_note == formdata['internal_note']
    assert str(refund_transactions[0].note_to_user) == formdata['note_to_user']
    assert refund_transactions[0].refund_description == formdata['refund_description']

    invalid_refund_amount = 100000000
    formdata = {
        'amount': invalid_refund_amount,
    }
    refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
    partial_refund_args = {
        'order': order,
        'form': refund_form,
        'request_method': 'POST',
    }
    with app.request_context(post_env):
        resp = process_partial_refund_for_order(partial_refund_args)

    assert resp.status_code == 403
    refund_transactions = order.transactions.filter_by(
        transaction_type=TRANSACTION_TYPE.REFUND
    ).all()
    assert refund_transactions[0].amount == decimal.Decimal(valid_refund_amount)

    resp = make_free_order(client)
    assert resp.status_code == 201
    resp_data = json.loads(resp.data)['result']
    order = Order.query.get(resp_data.get('order_id'))
    invalid_refund_amount = 100000000

    formdata = {
        'amount': invalid_refund_amount,
    }
    refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
    partial_refund_args = {
        'order': order,
        'form': refund_form,
        'request_method': 'POST',
    }
    with app.request_context(post_env):
        refund_resp = process_partial_refund_for_order(partial_refund_args)

    assert refund_resp.status_code == 403
예제 #10
0
def test_cancel_line_item_in_bulk_order(db_session, client, all_data, post_env):
    original_quantity = 5
    discounted_item = Item.query.filter_by(name='t-shirt').first()
    total_amount = discounted_item.current_price().amount * original_quantity
    data = {
        'line_items': [
            {'item_id': str(discounted_item.id), 'quantity': original_quantity}
        ],
        'buyer': {
            'fullname': 'Testing',
            'phone': '9814141414',
            'email': '*****@*****.**',
        },
    }
    ic = ItemCollection.query.first()
    # make a purchase order
    resp = client.post(
        '/ic/{ic}/order'.format(ic=ic.id),
        data=json.dumps(data),
        content_type='application/json',
        headers=[
            ('X-Requested-With', 'XMLHttpRequest'),
            ('Origin', app.config['BASE_URL']),
        ],
    )
    assert resp.status_code == 201
    resp_json = json.loads(resp.data)['result']
    assert resp_json['final_amount'] == (
        total_amount - 5 * total_amount / decimal.Decimal(100)
    )

    order = Order.query.get(resp_json['order_id'])
    # Create fake payment and transaction objects
    online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
    online_payment.confirm()
    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    transaction = PaymentTransaction(
        order=order,
        online_payment=online_payment,
        amount=order_amounts.final_amount,
        currency=CURRENCY.INR,
    )
    db_session.add(transaction)
    order.confirm_sale()
    db_session.commit()

    first_line_item = order.line_items[0]
    to_be_void_line_items = order.line_items[1:]
    precancellation_order_amount = order.net_amount
    # Mock Razorpay's API
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data={'id': buid()})
    )
    process_line_item_cancellation(first_line_item)
    assert first_line_item.status == LINE_ITEM_STATUS.CANCELLED
    for void_line_item in to_be_void_line_items:
        assert void_line_item.status == LINE_ITEM_STATUS.VOID
    expected_refund_amount = (
        precancellation_order_amount
        - order.get_amounts(LINE_ITEM_STATUS.CONFIRMED).final_amount
    )
    refund_transaction1 = PaymentTransaction.query.filter_by(
        order=order, transaction_type=TRANSACTION_TYPE.REFUND
    ).first()
    assert refund_transaction1.amount == expected_refund_amount

    second_line_item = order.confirmed_line_items[0]
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data={'id': buid()})
    )
    process_line_item_cancellation(second_line_item)
    assert second_line_item.status == LINE_ITEM_STATUS.CANCELLED
    refund_transaction2 = (
        PaymentTransaction.query.filter_by(
            order=order, transaction_type=TRANSACTION_TYPE.REFUND
        )
        .order_by(PaymentTransaction.created_at.desc())
        .first()
    )
    assert refund_transaction2.amount == second_line_item.final_amount

    # test failed cancellation
    third_line_item = order.confirmed_line_items[0]
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(
            response_data={
                'error': {
                    'code': 'BAD_REQUEST_ERROR',
                    'description': "The amount is invalid",
                    'field': 'amount',
                }
            },
            status_code=400,
        )
    )
    with pytest.raises(PaymentGatewayError):
        process_line_item_cancellation(third_line_item)

    # refund the remaining amount paid, and attempt to cancel a line item
    # this should cancel the line item without resulting in a new refund transaction
    refund_amount = order.net_amount
    refund_dict = {
        'id': buid(),
        'amount': refund_amount,
        'internal_note': 'internal reference',
        'note_to_user': '******',
    }
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data=refund_dict)
    )

    formdata = {
        'amount': refund_amount,
        'internal_note': 'internal reference',
        'refund_description': 'receipt description',
        'note_to_user': '******',
    }
    refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
    partial_refund_args = {
        'order': order,
        'form': refund_form,
        'request_method': 'POST',
    }
    with app.request_context(post_env):
        process_partial_refund_for_order(partial_refund_args)

    third_line_item = order.confirmed_line_items[0]
    pre_cancellation_transactions_count = order.refund_transactions.count()
    cancelled_refund_amount = process_line_item_cancellation(third_line_item)
    assert cancelled_refund_amount == decimal.Decimal(0)
    assert pre_cancellation_transactions_count == order.refund_transactions.count()

    # test free line item cancellation
    free_order_resp = make_free_order(client)
    free_order_resp_data = json.loads(free_order_resp.data)['result']
    free_order = Order.query.get(free_order_resp_data.get('order_id'))
    free_line_item = free_order.line_items[0]
    process_line_item_cancellation(free_line_item)
    assert free_line_item.status == LINE_ITEM_STATUS.CANCELLED
    assert free_order.transactions.count() == 0
예제 #11
0
def test_cancel_line_item_in_order(db_session, client, all_data, post_env):
    original_quantity = 2
    order_item = Item.query.filter_by(name='t-shirt').first()
    total_amount = order_item.current_price().amount * original_quantity
    data = {
        'line_items': [{'item_id': str(order_item.id), 'quantity': original_quantity}],
        'buyer': {
            'fullname': 'Testing',
            'phone': '9814141414',
            'email': '*****@*****.**',
        },
    }
    ic = ItemCollection.query.first()
    # make a purchase order
    resp = client.post(
        '/ic/{ic}/order'.format(ic=ic.id),
        data=json.dumps(data),
        content_type='application/json',
        headers=[
            ('X-Requested-With', 'XMLHttpRequest'),
            ('Origin', app.config['BASE_URL']),
        ],
    )
    assert resp.status_code == 201
    resp_json = json.loads(resp.data)['result']
    assert resp_json['final_amount'] == total_amount

    order = Order.query.get(resp_json['order_id'])
    # Create fake payment and transaction objects
    online_payment = OnlinePayment(pg_paymentid='pg_testpayment', order=order)
    online_payment.confirm()
    order_amounts = order.get_amounts(LINE_ITEM_STATUS.PURCHASE_ORDER)
    transaction = PaymentTransaction(
        order=order,
        online_payment=online_payment,
        amount=order_amounts.final_amount,
        currency=CURRENCY.INR,
    )
    db_session.add(transaction)
    order.confirm_sale()
    db_session.commit()

    refund_amount = total_amount - 1
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data={'id': buid()})
    )
    pre_refund_transactions_count = order.refund_transactions.count()
    formdata = {
        'amount': refund_amount,
        'internal_note': 'internal reference',
        'refund_description': 'receipt description',
        'note_to_user': '******',
    }
    refund_form = OrderRefundForm(data=formdata, parent=order, meta={'csrf': False})
    partial_refund_args = {
        'order': order,
        'form': refund_form,
        'request_method': 'POST',
    }
    with app.request_context(post_env):
        process_partial_refund_for_order(partial_refund_args)
    assert pre_refund_transactions_count + 1 == order.refund_transactions.count()

    first_line_item = order.line_items[0]
    # Mock Razorpay's API
    razorpay.refund_payment = MagicMock(
        return_value=MockResponse(response_data={'id': buid()})
    )
    process_line_item_cancellation(first_line_item)
    assert first_line_item.status == LINE_ITEM_STATUS.CANCELLED
    expected_refund_amount = total_amount - refund_amount
    refund_transaction1 = (
        PaymentTransaction.query.filter_by(
            order=order, transaction_type=TRANSACTION_TYPE.REFUND
        )
        .order_by(PaymentTransaction.created_at.desc())
        .first()
    )
    assert refund_transaction1.amount == expected_refund_amount