예제 #1
0
파일: seed.py 프로젝트: PressLabs/silver
    def handle(self, *args, **options):
        proforma = ProformaFactory.create(proforma_entries=[DocumentEntryFactory.create()],
                                          state=Proforma.STATES.ISSUED)
        proforma.create_invoice()

        invoice = InvoiceFactory.create(invoice_entries=[DocumentEntryFactory.create()],
                                        state=Invoice.STATES.ISSUED,
                                        related_document=None)
        TransactionFactory.create(state=Transaction.States.Settled,
                                  invoice=invoice,
                                  payment_method__customer=invoice.customer,
                                  proforma=None)
        InvoiceFactory.create_batch(size=3,
                                    invoice_entries=[DocumentEntryFactory.create()],
                                    state=Invoice.STATES.PAID,
                                    related_document=None)
        InvoiceFactory.create_batch(size=3,
                                    invoice_entries=[DocumentEntryFactory.create()],
                                    state=Invoice.STATES.DRAFT,
                                    related_document=None)

        InvoiceFactory.create_batch(size=3,
                                    invoice_entries=[DocumentEntryFactory.create()],
                                    state=Invoice.STATES.CANCELED,
                                    related_document=None)
예제 #2
0
    def test_list_transactions(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        transaction_1 = TransactionFactory.create(payment_method=payment_method)
        invoice_1 = transaction_1.invoice
        proforma_1 = transaction_1.proforma
        provider_1 = invoice_1.provider

        expected_t1 = OrderedDict([
            ('id', unicode(transaction_1.uuid)),
            ('url', reverse('transaction-detail',
                            kwargs={'customer_pk': customer.id, 'transaction_uuid': transaction_1.uuid})),
            ('customer', reverse('customer-detail', args=[customer.pk])),
            ('provider', reverse('provider-detail', args=[provider_1.pk])),
            ('amount', unicode(Decimal('0.00') + transaction_1.amount)),
            ('currency', unicode(transaction_1.currency)),
            ('currency_rate_date', None),
            ('state', unicode(transaction_1.state)),
            ('proforma', reverse('proforma-detail', args=[proforma_1.pk])),
            ('invoice', reverse('invoice-detail', args=[invoice_1.pk])),
            ('can_be_consumed', transaction_1.can_be_consumed),
            ('payment_method', reverse('payment-method-detail', kwargs={'customer_pk': customer.id,
                                                                        'payment_method_id': payment_method.id})),
            ('pay_url', reverse('pay-transaction', kwargs={'transaction_uuid': transaction_1.uuid})),
            ('valid_until', None)
        ])

        transaction_2 = TransactionFactory.create(payment_method=payment_method)
        invoice_2 = transaction_2.invoice
        proforma_2 = transaction_2.proforma
        provider_2 = invoice_2.provider
        expected_t2 = OrderedDict([
            ('id', unicode(transaction_2.uuid)),
            ('url', reverse('transaction-detail',
                            kwargs={'customer_pk': customer.id, 'transaction_uuid': transaction_2.uuid})),
            ('customer', reverse('customer-detail', args=[customer.pk])),
            ('provider', reverse('provider-detail', args=[provider_2.pk])),
            ('amount', unicode(Decimal('0.00') + transaction_2.amount)),
            ('currency', unicode(transaction_2.currency)),
            ('currency_rate_date', None),
            ('state', unicode(transaction_2.state)),
            ('proforma', reverse('proforma-detail', args=[proforma_2.pk])),
            ('invoice', reverse('invoice-detail', args=[invoice_2.pk])),
            ('can_be_consumed', transaction_2.can_be_consumed),
            ('payment_method', reverse('payment-method-detail', kwargs={'customer_pk': customer.id,
                                                                        'payment_method_id': payment_method.id})),
            ('pay_url', reverse('pay-transaction', kwargs={'transaction_uuid': transaction_2.uuid})),
            ('valid_until', None)
        ])

        url = reverse('transaction-list',
                      kwargs={'customer_pk': customer.pk})

        response = self.client.get(url, format='json')
        self.assertEqual(response.data[0], expected_t1)
        self.assertEqual(response.data[1], expected_t2)
예제 #3
0
    def test_skip_transaction_with_unverified_payment_method(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            verified=False
        )

        TransactionFactory.create(payment_method=payment_method)

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            call_command('execute_transactions')

            self.assertEqual(mock_execute.call_count, 0)
예제 #4
0
    def test_transaction_filtering(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            verified=True
        )

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method
        )

        filtered_transactions = [
            transactions[0], transactions[2], transactions[4]
        ]

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            transactions_arg = [
                str(transaction.pk) for transaction in filtered_transactions
            ]
            call_command('execute_transactions',
                         '--transactions=%s' % ','.join(transactions_arg))

            for transaction in filtered_transactions:
                self.assertIn(call(transaction), mock_execute.call_args_list)

            self.assertEqual(mock_execute.call_count, len(filtered_transactions))
예제 #5
0
    def test_documents_list_case_1(self):
        """
            One proforma, one invoice, without related documents
        """
        proforma = ProformaFactory.create()
        invoice_entries = DocumentEntryFactory.create_batch(3)
        invoice = InvoiceFactory.create(invoice_entries=invoice_entries)
        invoice.issue()
        payment_method = PaymentMethodFactory.create(customer=invoice.customer)
        transaction = TransactionFactory.create(payment_method=payment_method,
                                                invoice=invoice)

        url = reverse('document-list')

        with patch('silver.utils.payments._get_jwt_token',
                   new=self._jwt_token):
            response = self.client.get(url)

        # ^ there's a bug where specifying format='json' doesn't work
        response_data = response.data

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response_data), 2)

        self.assertIn(self._get_expected_data(invoice, [transaction]),
                      response_data)

        self.assertIn(self._get_expected_data(proforma), response_data)
예제 #6
0
    def test_patch_transaction_documents(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor='someprocessor'
        )
        transaction = TransactionFactory.create(payment_method=payment_method)
        proforma = ProformaFactory.create()
        invoice = InvoiceFactory.create(proforma=proforma)
        proforma.invoice = invoice
        proforma.save()

        invoice_url = reverse('invoice-detail', args=[invoice.pk])
        proforma_url = reverse('proforma-detail', args=[proforma.pk])
        url = reverse('transaction-detail', args=[transaction.customer.pk,
                                                  transaction.uuid])

        data = {
            'proforma': proforma_url,
            'invoice': invoice_url
        }

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertEqual(response.data, {
            'proforma': [u'This field may not be modified.'],
            'invoice': [u'This field may not be modified.']
        })
예제 #7
0
    def test_patch_transaction_with_initial_status(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor='someprocessor'
        )
        transaction = TransactionFactory.create(payment_method=payment_method)

        url = reverse('transaction-detail', args=[transaction.customer.pk,
                                                  transaction.uuid])

        valid_until = timezone.now()
        currency_rate_date = timezone.now().date()

        data = {
            'valid_until': valid_until,
            'currency': 'RON',
            'currency_rate_date': currency_rate_date,
            'amount': 200
        }

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        transaction.refresh_from_db()
        self.assertEqual(transaction.valid_until, valid_until)
        self.assertEqual(transaction.currency, 'RON')
        self.assertEqual(transaction.currency_rate_date, currency_rate_date)
        self.assertEqual(transaction.amount, 200)
    def test_fetch_transaction_status_transactions_filtering(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor
        )

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method, state=Transaction.States.Pending
        )

        filtered_transactions = [
            transactions[0], transactions[2], transactions[4]
        ]

        mock_fetch_status = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            fetch_transaction_status=mock_fetch_status):
            transactions_arg = [
                str(transaction.pk) for transaction in filtered_transactions
            ]
            call_command('fetch_transactions_status',
                         '--transactions=%s' % ','.join(transactions_arg))

            for transaction in filtered_transactions:
                self.assertIn(call(transaction),
                              mock_fetch_status.call_args_list)

            self.assertEqual(mock_fetch_status.call_count,
                             len(filtered_transactions))
예제 #9
0
    def test_pay_transaction_view_invalid_state(self):
        transaction = TransactionFactory.create(state=Transaction.States.Settled)

        response = self.client.get(get_payment_url(transaction, None))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(force_text(response.content),
                         render_to_string('transactions/complete_payment.html', {
                             'transaction': transaction,
                             'document': transaction.document,
                         }))
    def test_transaction_update_status_exception_logging(self, mock_logger):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor
        )

        TransactionFactory.create(payment_method=payment_method,
                                  state=Transaction.States.Pending)

        mock_fetch_status = MagicMock()
        mock_fetch_status.side_effect = Exception('This happened.')

        with patch.multiple(TriggeredProcessor,
                            fetch_transaction_status=mock_fetch_status):
            call_command('fetch_transactions_status')
            expected_call = call(
                'Encountered exception while updating transaction with id=%s.',
                1, exc_info=True
            )

            self.assertEqual(expected_call, mock_logger.call_args)
예제 #11
0
    def test_exception_logging(self, mock_logger):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            verified=True
        )

        TransactionFactory.create(payment_method=payment_method)

        mock_execute = MagicMock()
        mock_execute.side_effect = Exception('This happened.')

        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            call_command('execute_transactions')
            expected_call = call(
                'Encountered exception while executing transaction with id=%s.',
                1, exc_info=True
            )

            self.assertEqual(expected_call, mock_logger.call_args)
예제 #12
0
    def test_pay_transaction_view_not_consumable_transaction(self):
        last_year = timezone.now() - timedelta(days=365)
        transaction = TransactionFactory.create(state=Transaction.States.Initial,
                                                valid_until=last_year)

        response = self.client.get(get_payment_url(transaction, None))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(force_text(response.content),
                         render_to_string('transactions/expired_payment.html', {
                             'document': transaction.document,
                         }))
예제 #13
0
    def test_cancel_action_failed_void(self):
        payment_method = self.create_payment_method(
            customer=self.customer, payment_processor=failing_void_processor
        )

        transaction_initial = TransactionFactory.create(payment_method=payment_method)
        transaction_pending = TransactionFactory.create(payment_method=payment_method,
                                                        state='pending')

        url = reverse('payment-method-action', kwargs={
            'customer_pk': self.customer.pk,
            'payment_method_id': payment_method.pk,
            'requested_action': 'cancel',
        })

        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        expected_error = "Transaction {} couldn't be voided".format(transaction_pending.uuid)
        self.assertEqual(response.data, {'errors': [expected_error]})
예제 #14
0
    def test_complete_payment_view_with_return_url(self):
        transaction = TransactionFactory.create(state=Transaction.States.Settled)

        return_url = 'http://home.com'
        complete_url = "{}?return_url={}".format(get_payment_complete_url(transaction, None),
                                                 return_url)
        expected_url = "{}?transaction_uuid={}".format(return_url,
                                                       transaction.uuid)

        response = self.client.get(complete_url, follow=False)
        self.assertRedirects(response, expected_url,
                             fetch_redirect_response=False)
    def test_pay_documents_on_transaction_settle(self):
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending
        )
        transaction.settle()
        transaction.save()

        proforma = transaction.proforma
        invoice = transaction.invoice

        self.assertEqual(proforma.state, proforma.STATES.PAID)
        self.assertEqual(invoice.state, invoice.STATES.PAID)
    def test_create_transaction_with_not_allowed_currency(self):
        invoice = InvoiceFactory.create(transaction_currency='EUR',
                                        transaction_xe_rate=Decimal('1.0'),
                                        state=Invoice.STATES.ISSUED)
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=invoice.customer,
            canceled=False,
            verified=False
        )

        expected_exception = ValidationError
        expected_message = 'Currency EUR is not allowed by ' \
                           'the payment method. Allowed currencies are ' \
                           '[\'RON\', \'USD\'].'
        try:
            TransactionFactory.create(payment_method=payment_method,
                                      invoice=invoice)
            self.fail('{} not raised.'.format(str(expected_exception)))
        except expected_exception as e:
            self.assertTrue(expected_message in str(e))
예제 #17
0
    def test_pay_transaction_view_expired(self):
        transaction = TransactionFactory.create()

        with patch('silver.utils.payments.datetime') as mocked_datetime:
            mocked_datetime.utcnow.return_value = datetime.utcnow() - timedelta(days=365)
            url = get_payment_url(transaction, None)

        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(force_text(response.content),
                         render_to_string('transactions/expired_payment.html', {
                             'document': transaction.document,
                         }))
예제 #18
0
    def test_cancel_action(self):
        payment_method = self.create_payment_method(customer=self.customer,
                                                    payment_processor='triggered')
        transaction_initial = TransactionFactory.create(payment_method=payment_method)
        transaction_pending = TransactionFactory.create(payment_method=payment_method,
                                                        state='pending')

        url = reverse('payment-method-action', kwargs={
            'customer_pk': self.customer.pk,
            'payment_method_id': payment_method.pk,
            'requested_action': 'cancel',
        })

        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        payment_method.refresh_from_db()
        transaction_initial.refresh_from_db()
        transaction_pending.refresh_from_db()

        self.assertTrue(payment_method.canceled)
        self.assertEqual(transaction_initial.state, Transaction.States.Canceled)
        self.assertEqual(transaction_pending.state, Transaction.States.Canceled)
예제 #19
0
    def test_filter_payment_method(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(
            payment_processor='someprocessor',
            customer=customer)

        transaction1 = TransactionFactory.create(
            payment_method=payment_method
        )
        transaction_data_1 = self._transaction_data(transaction1)

        transaction2 = TransactionFactory.create(
            payment_method=payment_method
        )
        transaction_data_2 = self._transaction_data(transaction2)

        urls = [
            reverse(
                'payment-method-transaction-list', kwargs={
                    'customer_pk': customer.pk,
                    'payment_method_id': payment_method.pk}),
            reverse(
                'transaction-list', kwargs={'customer_pk': customer.pk})]

        for url in urls:
            url_method_someprocessor = url + '?payment_method=someprocessor'
            url_no_output = url + '?payment_method=Random'

            response = self.client.get(url_method_someprocessor, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data[0], transaction_data_1)
            self.assertEqual(response.data[1], transaction_data_2)

            response = self.client.get(url_no_output, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data, [])
    def test_transaction_settle_with_already_paid_invoice(self):
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending,
        )

        transaction.invoice.pay()

        transaction.settle()
        transaction.save()

        transaction.invoice.refresh_from_db()
        transaction.refresh_from_db()

        assert transaction.state == Transaction.States.Settled
        assert transaction.invoice.state == Invoice.STATES.PAID
예제 #21
0
    def test_patch_transaction_not_allowed_fields(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor)

        transaction = TransactionFactory.create(payment_method=payment_method)

        proforma = ProformaFactory.create(state='issued')
        invoice = InvoiceFactory.create(proforma=proforma, state='issued')
        proforma.invoice = invoice
        proforma.save()

        invoice_url = reverse('invoice-detail', args=[invoice.pk])
        proforma_url = reverse('proforma-detail', args=[proforma.pk])
        url = reverse('transaction-detail',
                      args=[transaction.customer.pk, transaction.uuid])

        new_payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=payment_method.customer)

        new_payment_method_url = reverse('payment-method-detail',
                                         kwargs={
                                             'customer_pk':
                                             new_payment_method.customer.pk,
                                             'payment_method_id':
                                             new_payment_method.pk
                                         })

        data = {
            'proforma': proforma_url,
            'invoice': invoice_url,
            'currency': 'EUR',
            'amount': 1234,
            'payment_method': new_payment_method_url
        }

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertEqual(
            response.data, {
                'proforma': [u'This field may not be modified.'],
                'invoice': [u'This field may not be modified.'],
                'currency': [u'This field may not be modified.'],
                'amount': [u'This field may not be modified.'],
                'payment_method': [u'This field may not be modified.']
            })
예제 #22
0
    def test_pay_transaction_view_expired(self):
        transaction = TransactionFactory.create()

        with patch('silver.utils.payments.datetime') as mocked_datetime:
            mocked_datetime.utcnow.return_value = datetime.utcnow(
            ) - timedelta(days=365)
            url = get_payment_url(transaction, None)

        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.content,
            render_to_string('transactions/expired_payment.html', {
                'document': transaction.document,
            }))
예제 #23
0
    def test_transaction_executing(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor, verified=True)

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method)

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            call_command('execute_transactions')

            for transaction in transactions:
                self.assertIn(call(transaction), mock_execute.call_args_list)

            self.assertEqual(mock_execute.call_count, len(transactions))
예제 #24
0
    def test_get_transaction(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)

        transaction = TransactionFactory.create(payment_method=payment_method)
        expected = self._transaction_data(transaction)

        with patch('silver.utils.payments._get_jwt_token') as mocked_token:
            mocked_token.return_value = 'token'

            url = reverse('transaction-detail',
                          kwargs={'customer_pk': customer.pk,
                                  'transaction_uuid': transaction.uuid})
            response = self.client.get(url, format='json')

            self.assertEqual(response.data, dict(expected))
예제 #25
0
    def test_get_payment_complete_url(self):
        transaction = TransactionFactory()

        expected_url = '/pay/token/complete?return_url=http://google.com'
        mocked_request = MagicMock(GET={'return_url': 'http://google.com'},
                                   versioning_scheme=None)
        mocked_request.build_absolute_uri.return_value = '/pay/token/complete'

        with patch('silver.utils.payments._get_jwt_token') as mocked_token:
            mocked_token.return_value = 'token'

            self.assertEqual(
                get_payment_complete_url(transaction, mocked_request),
                expected_url)

            mocked_token.assert_called_once_with(transaction)
예제 #26
0
    def test_pay_transaction_not_implemented_get_call(self):
        last_year = timezone.now() - timedelta(days=365)
        transaction = TransactionFactory.create(state=Transaction.States.Initial,
                                                valid_until=last_year)

        def get_view(processor, transaction, request):
            return not_implemented_view

        with patch('silver.tests.fixtures.ManualProcessor.get_view',
                   new=get_view):
            response = self.client.get(get_payment_url(transaction, None))

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(force_text(response.content),
                         render_to_string('transactions/expired_payment.html', {
                             'document': transaction.document,
                         }))
예제 #27
0
    def test_invalid_transaction_action(self):
        transaction = TransactionFactory.create(state='settled')

        url = reverse('transaction-action',
                      kwargs={
                          'customer_pk':
                          transaction.payment_method.customer.pk,
                          'transaction_uuid': str(transaction.uuid),
                          'requested_action': 'cancel',
                      })

        response = self.client.post(url)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        expected_error = "Can't execute action because the transaction is in "\
                         "an incorrect state: settled"
        self.assertEqual(response.data, {'errors': expected_error})
예제 #28
0
    def test_not_allowed_methods(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        transaction_1 = TransactionFactory.create(payment_method=payment_method)
        valid_until = datetime.now()
        url = reverse('transaction-detail',
                      kwargs={'customer_pk': customer.id,
                              'transaction_uuid': transaction_1.uuid})
        data = {
            'valid_until': valid_until
        }

        response = self.client.put(url, format='json', data=data)
        self.assertEqual(response.data['detail'], 'Method "PUT" not allowed.')

        response = self.client.post(url, format='json', data=data)
        self.assertEqual(response.data['detail'], 'Method "POST" not allowed.')
    def test_fetch_transaction_status_call(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor)

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method, state=Transaction.States.Pending)

        mock_fetch_status = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            fetch_transaction_status=mock_fetch_status):
            call_command('fetch_transactions_status')

            for transaction in transactions:
                self.assertIn(call(transaction),
                              mock_fetch_status.call_args_list)

            self.assertEqual(mock_fetch_status.call_count, len(transactions))
예제 #30
0
    def test_get_transaction(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)

        transaction = TransactionFactory.create(payment_method=payment_method)
        invoice = transaction.invoice
        proforma = transaction.proforma
        provider = invoice.provider

        expected = OrderedDict([
            ('id', unicode(transaction.uuid)),
            ('url',
             reverse('transaction-detail',
                     kwargs={
                         'customer_pk': customer.id,
                         'transaction_uuid': transaction.uuid
                     })),
            ('customer', reverse('customer-detail', args=[customer.pk])),
            ('provider', reverse('provider-detail', args=[provider.pk])),
            ('amount', unicode(Decimal('0.00') + transaction.amount)),
            ('currency', unicode(transaction.currency)),
            ('currency_rate_date', None),
            ('state', unicode(transaction.state)),
            ('proforma', reverse('proforma-detail', args=[proforma.pk])),
            ('invoice', reverse('invoice-detail', args=[invoice.pk])),
            ('can_be_consumed', transaction.can_be_consumed),
            ('payment_method',
             reverse('payment-method-detail',
                     kwargs={
                         'customer_pk': customer.id,
                         'payment_method_id': payment_method.id
                     })),
            ('pay_url',
             reverse('pay-transaction',
                     kwargs={'transaction_uuid': transaction.uuid})),
            ('valid_until', None)
        ])

        url = reverse('transaction-detail',
                      kwargs={
                          'customer_pk': customer.pk,
                          'transaction_uuid': transaction.uuid
                      })
        response = self.client.get(url, format='json')

        self.assertEqual(response.data, dict(expected))
예제 #31
0
    def test_pay_documents_on_transaction_settle(self):
        proforma = ProformaFactory.create()
        proforma.issue()
        proforma.save()
        invoice = proforma.create_invoice()
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending,
            invoice=invoice,
            proforma=proforma)
        transaction.settle()
        transaction.save()

        proforma.refresh_from_db()
        invoice.refresh_from_db()

        self.assertEqual(proforma.state, proforma.STATES.PAID)
        self.assertEqual(invoice.state, invoice.STATES.PAID)
예제 #32
0
    def test_not_allowed_methods(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        transaction_1 = TransactionFactory.create(payment_method=payment_method)
        valid_until = datetime.now().replace(microsecond=0)
        url = reverse('transaction-detail',
                      kwargs={'customer_pk': customer.id,
                              'transaction_uuid': transaction_1.uuid})
        data = {
            'valid_until': valid_until
        }

        response = self.client.put(url, format='json', data=data)
        self.assertEqual(response.data['detail'], 'Method "PUT" not allowed.')

        response = self.client.post(url, format='json', data=data)
        self.assertEqual(response.data['detail'], 'Method "POST" not allowed.')
예제 #33
0
    def test_create_one_without_required_fields(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        transaction = TransactionFactory.create(payment_method=payment_method)
        valid_until = datetime.now()

        data = {'valid_until': valid_until}

        url = reverse('payment-method-transaction-list',
                      kwargs={
                          'customer_pk': customer.id,
                          'payment_method_id': payment_method.id
                      })

        response = self.client.post(url, format='json', data=data)

        self.assertEqual(response.data['payment_method'],
                         ['This field is required.'])
예제 #34
0
    def test_pay_documents_on_transaction_settle(self):
        proforma = ProformaFactory.create()
        proforma.issue()
        proforma.save()
        invoice = proforma.create_invoice()
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending,
            invoice=invoice,
            proforma=proforma
        )
        transaction.settle()
        transaction.save()

        proforma.refresh_from_db()
        invoice.refresh_from_db()

        self.assertEqual(proforma.state, proforma.STATES.PAID)
        self.assertEqual(invoice.state, invoice.STATES.PAID)
예제 #35
0
    def test_create_one_without_required_fields(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        transaction = TransactionFactory.create(payment_method=payment_method)
        valid_until = datetime.now()

        data = {
            'valid_until': valid_until
        }

        url = reverse('payment-method-transaction-list',
                      kwargs={'customer_pk': customer.id,
                              'payment_method_id': payment_method.id})

        response = self.client.post(url, format='json', data=data)

        self.assertEqual(response.data['payment_method'],
                         ['This field is required.'])
예제 #36
0
    def test_patch_after_initial_state(self):
        transaction = TransactionFactory.create(state=Transaction.States.Pending)

        data = {
            'valid_until': timezone.now()
        }

        url = reverse('transaction-detail', args=[transaction.customer.pk,
                                                  transaction.uuid])

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertEqual(response.data, {
            u'non_field_errors': [
                u'The transaction cannot be modified once it is in pending state.'
            ]
        })
    def test_transaction_invoice_on_transaction_settle(self):
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending, invoice=None)

        # here transaction.proforma is an old version of itself
        # the actual proforma that is saved in db has a related invoice
        # so a refresh_from_db is needed
        # test_no_transaction_settle_with_only_related_proforma would be enough
        # if the transition callbacks would be handled in post_save

        transaction.settle()
        transaction.save()

        transaction.refresh_from_db()

        proforma = transaction.proforma
        invoice = transaction.invoice

        self.assertEqual(proforma.related_document, invoice)
예제 #38
0
    def test_transaction_executing(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            verified=True
        )

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method
        )

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            call_command('execute_transactions')

            for transaction in transactions:
                self.assertIn(call(transaction), mock_execute.call_args_list)

            self.assertEqual(mock_execute.call_count, len(transactions))
예제 #39
0
    def test_filter_min_max_amount(self):
        customer = CustomerFactory.create()
        payment = PaymentFactory.create(customer=customer, amount=100)
        payment_method_ok = PaymentMethodFactory.create(
            payment_processor='someprocessor', customer=customer)

        transaction = TransactionFactory.create(
            payment_method=payment_method_ok, payment=payment)
        transaction_data = self._transaction_data(customer, payment,
                                                  payment_method_ok,
                                                  transaction)

        urls = [
            reverse('payment-method-transaction-list',
                    kwargs={
                        'customer_pk': customer.pk,
                        'payment_method_id': payment_method_ok.pk
                    }),
            reverse('transaction-list', kwargs={'customer_pk': customer.pk})
        ]

        for url in urls:
            url_with_filterable_data = url + '?min_amount=10'
            url_no_output = url + '?min_amount=150'

            response = self.client.get(url_with_filterable_data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data[0], transaction_data)

            response = self.client.get(url_no_output, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data, [])

            url_with_filterable_data = url + '?max_amount=1050'
            url_no_output = url + '?max_amount=10'

            response = self.client.get(url_with_filterable_data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data[0], transaction_data)

            response = self.client.get(url_no_output, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data, [])
예제 #40
0
    def test_patch_after_initial_state(self):
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending)

        data = {'valid_until': timezone.now()}

        url = reverse('transaction-detail',
                      args=[transaction.customer.pk, transaction.uuid])

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertEqual(
            response.data, {
                u'non_field_errors': [
                    u'The transaction cannot be modified once it is in pending state.'
                ]
            })
    def test_no_transaction_creation_for_issued_documents_case3(self):
        """
            There are 1 pending and 1 settled transactions that together cover the document amount.
        """
        entry = DocumentEntryFactory(quantity=1, unit_price=100)
        invoice = InvoiceFactory.create(invoice_entries=[entry])
        invoice.issue()

        customer = invoice.customer
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False,
            verified=False,
        )

        TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=invoice.total_in_transaction_currency / 2,
            state=Transaction.States.Settled)

        TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=invoice.total_in_transaction_currency / 2,
            state=Transaction.States.Pending)

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            execute_transaction=mock_execute):
            expected_exception = ValidationError
            expected_message = "Amount is greater than the amount that should be " \
                               "charged in order to pay the billing document."
            try:
                TransactionFactory.create(invoice=invoice,
                                          payment_method=payment_method,
                                          amount=1)

                self.fail('{} not raised.'.format(str(expected_exception)))
            except expected_exception as e:
                self.assertTrue(expected_message in str(e))

            transactions = Transaction.objects.filter(
                payment_method=payment_method,
                invoice=invoice,
            )
            self.assertEqual(len(transactions), 2)

            self.assertEqual(mock_execute.call_count, 0)
예제 #42
0
    def test_filter_min_max_amount(self):
        customer = CustomerFactory.create()
        payment_method_ok = PaymentMethodFactory.create(
            payment_processor='someprocessor',
            customer=customer)

        transaction = TransactionFactory.create(
            payment_method=payment_method_ok,
            amount=100
        )
        transaction_data = self._transaction_data(transaction)

        urls = [
            reverse(
                'payment-method-transaction-list', kwargs={
                    'customer_pk': customer.pk,
                    'payment_method_id': payment_method_ok.pk}),
            reverse(
                'transaction-list', kwargs={'customer_pk': customer.pk})]

        for url in urls:
            url_with_filterable_data = url + '?min_amount=10'
            url_no_output = url + '?min_amount=150'

            response = self.client.get(url_with_filterable_data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data[0], transaction_data)

            response = self.client.get(url_no_output, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data, [])

            url_with_filterable_data = url + '?max_amount=1050'
            url_no_output = url + '?max_amount=10'

            response = self.client.get(url_with_filterable_data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data[0], transaction_data)

            response = self.client.get(url_no_output, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(response.data, [])
    def test_fetch_transaction_status_call(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor
        )

        transactions = TransactionFactory.create_batch(
            5, payment_method=payment_method, state=Transaction.States.Pending
        )

        mock_fetch_status = MagicMock()
        with patch.multiple(TriggeredProcessor,
                            fetch_transaction_status=mock_fetch_status):
            call_command('fetch_transactions_status')

            for transaction in transactions:
                self.assertIn(call(transaction),
                              mock_fetch_status.call_args_list)

            self.assertEqual(mock_fetch_status.call_count,
                             len(transactions))
    def test_transaction_invoice_on_transaction_settle(self):
        transaction = TransactionFactory.create(
            state=Transaction.States.Pending,
            invoice=None
        )

        # here transaction.proforma is an old version of itself
        # the actual proforma that is saved in db has a related invoice
        # so a refresh_from_db is needed
        # test_no_transaction_settle_with_only_related_proforma would be enough
        # if the transition callbacks would be handled in post_save

        transaction.settle()
        transaction.save()

        transaction.refresh_from_db()

        proforma = transaction.proforma
        invoice = transaction.invoice

        self.assertEqual(proforma.related_document, invoice)
예제 #45
0
    def test_get_transaction_details(self):
        customer = CustomerFactory.create()
        payment_method = PaymentMethodFactory.create(customer=customer)
        payment = PaymentFactory.create(customer=customer)
        transaction_1 = TransactionFactory.create(
            payment_method=payment_method, payment=payment)
        expected_t1 = OrderedDict([
            ('url',
             reverse('transaction-detail',
                     kwargs={
                         'customer_pk': customer.pk,
                         'transaction_uuid': transaction_1.uuid
                     })),
            ('payment_method',
             reverse('payment-method-detail',
                     kwargs={
                         'customer_pk': customer.pk,
                         'payment_method_id': payment_method.id
                     })),
            ('payment',
             reverse('payment-detail',
                     kwargs={
                         'customer_pk': customer.pk,
                         'payment_pk': transaction_1.payment.pk
                     })),
            ('is_usable', True),
            ('pay_url',
             reverse('pay-transaction',
                     kwargs={'transaction_uuid': transaction_1.uuid})),
            ('valid_until', None),
        ])

        url = reverse('transaction-detail',
                      kwargs={
                          'customer_pk': customer.pk,
                          'transaction_uuid': transaction_1.uuid
                      })
        response = self.client.get(url, format='json')
        self.assertEqual(response.data, dict(expected_t1))
    def test_no_transaction_creation_for_issued_documents_case3(self):
        """
            There are 1 pending and 1 settled transactions that together cover the document amount.
        """
        entry = DocumentEntryFactory(quantity=1, unit_price=100)
        invoice = InvoiceFactory.create(invoice_entries=[entry])
        invoice.issue()

        customer = invoice.customer
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor, customer=customer,
            canceled=False,
            verified=False,
        )

        TransactionFactory.create(invoice=invoice,
                                  payment_method=payment_method,
                                  amount=invoice.total_in_transaction_currency / 2,
                                  state=Transaction.States.Settled)

        TransactionFactory.create(invoice=invoice,
                                  payment_method=payment_method,
                                  amount=invoice.total_in_transaction_currency / 2,
                                  state=Transaction.States.Pending)

        mock_execute = MagicMock()
        with patch.multiple(TriggeredProcessor, execute_transaction=mock_execute):
            expected_exception = ValidationError
            expected_message = "Amount is greater than the amount that should be " \
                               "charged in order to pay the billing document."
            try:
                TransactionFactory.create(invoice=invoice,
                                          payment_method=payment_method,
                                          amount=1)

                self.fail('{} not raised.'.format(str(expected_exception)))
            except expected_exception as e:
                self.assertTrue(expected_message in str(e))

            transactions = Transaction.objects.filter(
                payment_method=payment_method, invoice=invoice,
            )
            self.assertEqual(len(transactions), 2)

            self.assertEqual(mock_execute.call_count, 0)
예제 #47
0
    def test_patch_transaction_with_initial_status(self):
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor)

        transaction = TransactionFactory.create(payment_method=payment_method)

        url = reverse('transaction-detail',
                      args=[transaction.customer.pk, transaction.uuid])

        valid_until = timezone.now().replace(microsecond=0)

        data = {
            'valid_until': valid_until,
        }

        response = self.client.patch(url, format='json', data=data)

        self.assertEqual(
            response.status_code, status.HTTP_200_OK,
            "status %s, data %s" % (response.status_code, response.data))

        transaction.refresh_from_db()
        self.assertEqual(transaction.valid_until, valid_until)
예제 #48
0
    def test_correct_overpayment(self):
        """ An invoice is issued, and it is overpaid. A correction is issued
            """

        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0,
                                   currency='USD',
                                   first_name="Bob",
                                   last_name="Smith")
        customer.save()

        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False)

        # Create a simple invoice
        entry = DocumentEntryFactory(quantity=1, unit_price=150)
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer,
                                        transaction_currency='USD')
        invoice.issue()
        invoice.save()

        assert PaymentMethod.objects.count() == 1
        assert Invoice.objects.count() == 1
        assert BillingDocumentBase.objects.count() == 1

        # Customer overpays by 2x
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            overpayment=True,
            amount=300,
            state=Transaction.States.Initial)
        transaction.settle()
        transaction.save()

        invoice.pay()

        assert invoice.state == Invoice.STATES.PAID
        assert PaymentMethod.objects.count() == 1
        assert Invoice.objects.count() == 1
        # Proforma issued as well
        assert BillingDocumentBase.objects.count() == 2
        assert customer.balance == Decimal(150)

        # # Create a repayment invoice
        entry = DocumentEntryFactory(quantity=1, unit_price=-150)
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer,
                                        state=Invoice.STATES.DRAFT,
                                        transaction_currency='USD')
        invoice.save()
        assert customer.balance == Decimal(150)

        invoice.issue()
        invoice.save()
        assert customer.balance == Decimal(150)

        # This is the transaction to correct the balance. We're using
        # .settle() here, but we will need another method (forthcoming).
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            overpayment=True,
            amount=-150,
            state=Transaction.States.Initial)

        transaction.settle()
        transaction.save()

        assert invoice.state == Invoice.STATES.PAID
        assert customer.balance == Decimal(0)
예제 #49
0
    def test_customer_balance_calculation_with_overpayments(self):
        """ An invoice is issued, and it is paid in two transactions:
            one for half the amount, and another for well over the
            amount. """
        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0,
                                   currency='USD',
                                   first_name="Bob",
                                   last_name="Smith")
        customer.save()

        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False)

        # Create a simple invoice
        entry = DocumentEntryFactory(quantity=1, unit_price=250)
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer)
        invoice.issue()

        # Customer underpays by half
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=125.00,
            state=Transaction.States.Initial)

        transaction.settle()
        transaction.save()

        assert invoice.state != Invoice.STATES.PAID

        # Customer overpays by double
        transaction_over = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=500,
            # NB:
            overpayment=True,
            state=Transaction.States.Initial)

        transaction_over.settle()
        transaction_over.save()

        assert invoice.state != Invoice.STATES.PAID

        invoice.pay()
        assert invoice.state == Invoice.STATES.PAID

        # Payment calculation works even with overpayment.
        assert invoice.amount_paid_in_transaction_currency == \
            ((invoice.total_in_transaction_currency / 2) + \
             (invoice.total_in_transaction_currency * 2))

        # Customer paid 625 total, for an invoice of 250
        # Current balance: 375.00
        overpayment = abs(
            invoice.total_in_transaction_currency - \
                ((invoice.total_in_transaction_currency / 2) + \
                 (invoice.total_in_transaction_currency * 2))
        )

        assert invoice.total_in_transaction_currency == 250.00
        assert overpayment == Decimal(375.00)
        assert customer.balance == overpayment
예제 #50
0
    def test_get_invoice(self, mocked_settings):
        InvoiceFactory.reset_sequence(1)
        TransactionFactory.reset_sequence(1)

        customer = CustomerFactory.create()
        invoice = InvoiceFactory.create(customer=customer,
                                        state=Invoice.STATES.ISSUED)

        invoice.generate_pdf()

        with mute_signals(pre_save):
            transactions = [
                TransactionFactory.create(
                    state=state,
                    invoice=invoice,
                    payment_method=PaymentMethodFactory(customer=customer))
                for state in Transaction.States.as_list() if state not in [
                    Transaction.States.Canceled, Transaction.States.Refunded,
                    Transaction.States.Failed
                ]
            ]
        expected_transactions = [{
            "id":
            str(transaction.uuid),
            "url":
            build_absolute_test_url(
                reverse('transaction-detail',
                        [transaction.customer.pk, transaction.uuid])),
            "customer":
            build_absolute_test_url(
                reverse('customer-detail', [transaction.customer.id])),
            "provider":
            build_absolute_test_url(
                reverse('provider-detail', [transaction.provider.id])),
            "amount":
            "%.2f" % transaction.amount,
            "currency":
            "RON",
            "state":
            transaction.state,
            "proforma":
            build_absolute_test_url(
                reverse('proforma-detail', [transaction.proforma.id])),
            "invoice":
            build_absolute_test_url(
                reverse('invoice-detail', [transaction.invoice.id])),
            "can_be_consumed":
            transaction.can_be_consumed,
            "payment_processor":
            transaction.payment_processor,
            "payment_method":
            build_absolute_test_url(
                reverse(
                    'payment-method-detail',
                    [transaction.customer.pk, transaction.payment_method.pk])),
            "pay_url":
            (build_absolute_test_url(reverse('payment', ['token']))
             if transaction.state == Transaction.States.Initial else None),
        } for transaction in transactions]

        with patch('silver.utils.payments._get_jwt_token') as mocked_token:
            mocked_token.return_value = 'token'
            url = reverse('invoice-detail', kwargs={'pk': invoice.pk})

            for show_pdf_storage_url, pdf_url in [
                (True, build_absolute_test_url(invoice.pdf.url)),
                (False,
                 build_absolute_test_url(reverse('pdf',
                                                 args=[invoice.pdf.pk])))
            ]:
                mocked_settings.SILVER_SHOW_PDF_STORAGE_URL = show_pdf_storage_url

                response = self.client.get(url)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                expected_response = {
                    "id":
                    invoice.pk,
                    "series":
                    "InvoiceSeries",
                    "number":
                    1,
                    "provider":
                    build_absolute_test_url(
                        reverse('provider-detail', [invoice.provider.pk])),
                    "customer":
                    build_absolute_test_url(
                        reverse('customer-detail', [invoice.customer.pk])),
                    "archived_provider":
                    '{}',
                    "archived_customer":
                    '{}',
                    "due_date":
                    None,
                    "issue_date":
                    invoice.issue_date.strftime('%Y-%m-%d'),
                    "paid_date":
                    None,
                    "cancel_date":
                    None,
                    "sales_tax_name":
                    "VAT",
                    "sales_tax_percent":
                    '1.00',
                    "currency":
                    "RON",
                    "transaction_currency":
                    invoice.transaction_currency,
                    "transaction_xe_rate":
                    ("%.4f" % invoice.transaction_xe_rate
                     if invoice.transaction_xe_rate else None),
                    "transaction_xe_date":
                    invoice.transaction_xe_date,
                    "state":
                    "issued",
                    "proforma":
                    build_absolute_test_url(
                        reverse('proforma-detail',
                                [invoice.related_document.pk])),
                    "invoice_entries": [],
                    "pdf_url":
                    pdf_url,
                    "total":
                    0
                }
                for field in expected_response:
                    self.assertEqual(
                        expected_response[field],
                        response.data[field],
                        msg=("Expected %s, actual %s for field %s" %
                             (expected_response[field], response.data[field],
                              field)))

                self.assertEqual(len(response.data["transactions"]),
                                 len(expected_transactions))

                for actual_transaction in response.data["transactions"]:
                    expected_transaction = [
                        transaction for transaction in expected_transactions
                        if transaction["id"] == actual_transaction["id"]
                    ]
                    self.assertTrue(expected_transaction)
                    expected_transaction = expected_transaction[0]
                    for field in expected_transaction:
                        self.assertEqual(
                            expected_transaction[field],
                            actual_transaction[field],
                            msg=("Expected %s, actual %s for field %s" %
                                 (expected_transaction[field],
                                  actual_transaction[field], field)))
예제 #51
0
    def test_get_invoice(self):
        InvoiceFactory.reset_sequence(1)
        TransactionFactory.reset_sequence(1)

        customer = CustomerFactory.create()
        invoice = InvoiceFactory.create(customer=customer,
                                        state=Invoice.STATES.ISSUED)
        with mute_signals(pre_save):
            transactions = [
                TransactionFactory.create(
                    state=state,
                    invoice=invoice,
                    payment_method=PaymentMethodFactory(customer=customer))
                for state in Transaction.States.as_list() if state not in [
                    Transaction.States.Canceled, Transaction.States.Refunded,
                    Transaction.States.Failed
                ]
            ]
        expected_transactions = [{
            "id":
            str(transaction.uuid),
            "url":
            "http://testserver/customers/%s/transactions/%s/" %
            (invoice.customer.pk, transaction.uuid),
            "customer":
            "http://testserver/customers/%s/" % invoice.customer.pk,
            "provider":
            "http://testserver/providers/%s/" % invoice.provider.pk,
            "amount":
            "%s.00" % str(transaction.amount),
            "currency":
            "USD",
            "state":
            transaction.state,
            "proforma":
            "http://testserver/proformas/%s/" % transaction.proforma.pk,
            "invoice":
            "http://testserver/invoices/%s/" % transaction.invoice.pk,
            "can_be_consumed":
            transaction.can_be_consumed,
            "payment_processor":
            transaction.payment_processor,
            "payment_method":
            "http://testserver/customers/%s/payment_methods/%s/" %
            (invoice.customer.pk, transaction.payment_method.pk),
            "pay_url":
            "http://testserver/pay/token/"
            if transaction.state == Transaction.States.Initial else None,
        } for transaction in transactions]

        with patch('silver.utils.payments._get_jwt_token') as mocked_token:
            mocked_token.return_value = 'token'

            url = reverse('invoice-detail', kwargs={'pk': invoice.pk})
            response = self.client.get(url)

            self.assertEqual(response.status_code, status.HTTP_200_OK)
            expected_response = {
                "id":
                invoice.pk,
                "series":
                "InvoiceSeries",
                "number":
                1,
                "provider":
                "http://testserver/providers/%s/" % invoice.provider.pk,
                "customer":
                "http://testserver/customers/%s/" % invoice.customer.pk,
                "archived_provider":
                '{}',
                "archived_customer":
                '{}',
                "due_date":
                None,
                "issue_date":
                str(invoice.issue_date),
                "paid_date":
                None,
                "cancel_date":
                None,
                "sales_tax_name":
                "VAT",
                "sales_tax_percent":
                '1.00',
                "currency":
                "RON",
                "transaction_currency":
                invoice.transaction_currency,
                "transaction_xe_rate": ("%.4f" % invoice.transaction_xe_rate if
                                        invoice.transaction_xe_rate else None),
                "transaction_xe_date":
                invoice.transaction_xe_date,
                "state":
                "issued",
                "proforma":
                "http://testserver/proformas/%s/" % invoice.proforma.pk,
                "invoice_entries": [],
                "pdf_url":
                invoice.pdf.url,
                "total":
                0
            }
            for field in expected_response:
                self.assertEqual(expected_response[field],
                                 response.data[field],
                                 msg=("Expected %s, actual %s for field %s" %
                                      (expected_response[field],
                                       response.data[field], field)))

            self.assertEqual(len(response.data["transactions"]),
                             len(expected_transactions))

            for actual_transaction in response.data["transactions"]:
                expected_transaction = [
                    transaction for transaction in expected_transactions
                    if transaction["id"] == actual_transaction["id"]
                ]
                self.assertTrue(expected_transaction)
                expected_transaction = expected_transaction[0]

                self.assertEqual(expected_transaction[field],
                                 actual_transaction[field],
                                 msg=("Expected %s, actual %s for field %s" %
                                      (expected_response[field],
                                       response.data[field], field)))
예제 #52
0
    def test_overpayment_checker_process(self):
        from silver.overpayment_checker import OverpaymentChecker

        customer = CustomerFactory(
            sales_tax_percent=0,  # 0 for easy asserting.
            currency='USD',
            first_name="Bob",
            last_name="Smith")
        customer.save()

        # Create customer payment method
        #
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False)
        payment_method.save()

        # Create a simple invoice.
        #
        entry = DocumentEntryFactory(quantity=1, unit_price=150)
        entry.save()
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer,
                                        transaction_currency='USD')
        invoice.issue()
        invoice.save()

        # Customer overpays by 2x
        #
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            overpayment=True,
            amount=300,
            state=Transaction.States.Initial)
        transaction.settle()
        transaction.save()

        invoice.pay()

        assert invoice.state == Invoice.STATES.PAID
        assert customer.balance == Decimal(150)

        # Grab the overpayment defaults
        op = OverpaymentChecker()

        # Run the overpayment process
        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        # An invoice has been issued a for the correct amount.
        repayment = Invoice.objects.filter(
            provider=op.default_provider).first()
        assert Invoice.objects.filter(
            provider=op.default_provider).count() == 1
        assert repayment.total_in_transaction_currency == -150
        assert repayment.state == Invoice.STATES.ISSUED

        # Customer balance is still the same; payment has not occurred
        # yet.
        #
        assert customer.balance == Decimal(150)

        # Create the repayment transaction, this is supposed to happen
        # somewhere automatically.
        #
        repayment_tx = Transaction.objects.create(
            invoice=repayment,
            amount=repayment.total_in_transaction_currency,
            overpayment=True,
            state=Transaction.States.Initial,
            payment_method=payment_method)
        repayment_tx.settle()
        repayment_tx.save()

        # There's one transaction issued for this doc, and it has set
        # the state of the invoice to PAID. The customer's balance is
        # now 0.
        #
        assert repayment.state == Invoice.STATES.PAID
        assert repayment.transactions.count() == 1
        assert repayment.amount_paid_in_transaction_currency == -150
        assert repayment.customer.balance == Decimal(0)
예제 #53
0
    def test_customer_balance_gte_zero(self):
        """ Create a zero and positive balance, and rerun the
        overpayment process: no invoices should be issued. """

        from silver.overpayment_checker import OverpaymentChecker

        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0,
                                   currency='USD',
                                   first_name="Bob",
                                   last_name="Smith")
        customer.save()

        # Create customer payment method
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False)
        payment_method.save()

        # Create a simple invoice
        entry = DocumentEntryFactory(quantity=1, unit_price=150)
        entry.save()
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer,
                                        transaction_currency='USD')
        invoice.issue()
        invoice.save()

        # Customer pays an accurate amount.
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=150,
            state=Transaction.States.Initial)
        transaction.settle()
        transaction.save()

        assert invoice.state == Invoice.STATES.PAID
        assert Invoice.objects.filter(customer=customer).count() == 1
        assert customer.balance == Decimal(0)

        # Run the overpayment process
        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        provider = OverpaymentChecker().default_provider
        assert Invoice.objects.filter(provider=provider).count() == 0

        # Run the overpayment process a couple more times, does it duplicate
        # things?
        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        assert Invoice.objects.filter(provider=provider).count() == 0
예제 #54
0
    def test_balance_on_date(self):
        import pytz

        start_date = dt.datetime(2019, 1, 1, 0, 0, 0, 0, tzinfo=pytz.UTC)
        invoice_date = dt.datetime(2019, 1, 15, 0, 0, 0, 0, tzinfo=pytz.UTC)
        payment_date = dt.datetime(2019, 1, 17, 0, 0, 0, 0, tzinfo=pytz.UTC)
        mid_date = dt.datetime(2019, 1, 20, 0, 0, 0, 0, tzinfo=pytz.UTC)

        from silver.overpayment_checker import OverpaymentChecker

        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0,
                                   currency='USD',
                                   first_name="Bob",
                                   last_name="Smith")
        customer.save()

        assert customer.balance_on_date(date=start_date) == Decimal(0)

        ## Now we create an invoice situation after the start date...

        # Create customer payment method
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False)
        payment_method.save()

        # Create a simple invoice
        entry = DocumentEntryFactory(quantity=1, unit_price=150)
        entry.save()
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        due_date=invoice_date,
                                        customer=customer,
                                        transaction_currency='USD')
        invoice.issue()
        invoice.save()

        # Customer overpays by 2x
        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            created_at=payment_date,
            updated_at=payment_date,
            overpayment=True,
            amount=300,
            state=Transaction.States.Initial)
        transaction.settle()
        transaction.save()

        invoice.pay()

        assert invoice.state == Invoice.STATES.PAID
        assert customer.balance_on_date(date=start_date) == Decimal(0)
        assert customer.balance_on_date(date=payment_date) == Decimal(0)
        # This balance should be 150, but it's not yet.
        assert customer.balance_on_date(date=mid_date) == Decimal(150)
        return

        # Grab the overpayment defaults
        op = OverpaymentChecker()

        # Run the overpayment process
        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        # An invoice has been issued a for the correct amount.
        repayment = Invoice.objects.filter(
            provider=op.default_provider).first()
        assert Invoice.objects.filter(
            provider=op.default_provider).count() == 1
        assert repayment.total_in_transaction_currency == -150
        assert repayment.state == Invoice.STATES.ISSUED

        # Customer balance is still the same; payment has not occurred
        # yet.
        assert customer.balance == Decimal(150)

        # Run the overpayment process a couple more times, does it duplicate
        # things?
        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        call_command('check_overpayments',
                     billing_date=timezone.now().date(),
                     stdout=self.output)

        assert Invoice.objects.filter(
            provider=op.default_provider).count() == 1
        assert repayment.total_in_transaction_currency == -150
        assert repayment.state == Invoice.STATES.ISSUED

        # Customer balance is still the same; payment has not occurred
        # yet.
        assert customer.balance == Decimal(150)

        # Create the repayment transaction, this is supposed to happen
        # somewhere automatically.
        amt = repayment.total_in_transaction_currency
        repayment_tx = Transaction.objects.create(
            invoice=repayment,
            amount=amt,
            overpayment=True,
            state=Transaction.States.Initial,
            payment_method=payment_method)
        repayment_tx.settle()
        repayment_tx.save()

        # There's one transaction issued for this doc, and it has set
        # the state of the invoice to PAID. The customer's balance is
        # now 0.
        assert repayment.state == Invoice.STATES.PAID
        assert repayment.transactions.count() == 1
        assert repayment.amount_paid_in_transaction_currency == -150
        assert repayment.customer.balance == Decimal(0)