Exemple #1
0
    def test_filters_is_overdue(self):
        today = datetime.now(pytz.utc).date()
        yesterday = today - timedelta(days=1)
        tomorrow = today + timedelta(days=1)

        customer = CustomerFactory()

        payment_unpaid_overdue = PaymentFactory.create(
            customer=customer,
            due_date=yesterday,
            status=Payment.Status.Unpaid)
        payment_unpaied_not_due = PaymentFactory.create(
            customer=customer, due_date=tomorrow, status=Payment.Status.Unpaid)
        payment_paid_not_due = PaymentFactory.create(
            customer=payment_unpaid_overdue.customer,
            due_date=tomorrow,
            status=Payment.Status.Paid)
        payment_canceled = PaymentFactory.create(
            customer=customer,
            due_date=yesterday,
            status=Payment.Status.Canceled)

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

        overdue_url = url + '?is_overdue=True'
        not_overdue_url = url + '?is_overdue=False'

        self.assert_get_data(overdue_url, [payment_unpaid_overdue])
        self.assert_get_data(
            not_overdue_url,
            [payment_unpaied_not_due, payment_paid_not_due, payment_canceled])
Exemple #2
0
    def test_create_negative_document(self):
        """ Confirm that an invoice can be issued with a negative value. """

        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0, currency='USD')

        entry = DocumentEntryFactory(quantity=1, unit_price=-150)
        invoice = InvoiceFactory.create(invoice_entries=[entry],
                                        customer=customer)
        invoice.issue()

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

        transaction = TransactionFactory.create(
            invoice=invoice,
            payment_method=payment_method,
            amount=invoice.total_in_transaction_currency,
            state=Transaction.States.Initial)

        assert invoice.transactions.count() == 1
        assert invoice.total_in_transaction_currency == -150
        assert transaction.amount == -150
Exemple #3
0
    def test_filters_min_max_amount(self):
        customer = CustomerFactory()

        payment_below_min = PaymentFactory.create(customer=customer,
                                                  amount=9.99)
        payment_min = PaymentFactory.create(customer=customer, amount=10.00)
        payment_above_min = PaymentFactory.create(customer=customer,
                                                  amount=10.01)
        payment_center = PaymentFactory.create(customer=customer, amount=60)
        payment_below_max = PaymentFactory.create(customer=customer,
                                                  amount=99.99)
        payment_max = PaymentFactory.create(customer=customer, amount=100.00)
        payment_above_max = PaymentFactory.create(customer=customer,
                                                  amount=100.01)

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

        url_range_slice = url + '?min_amount=10&max_amount=100'
        url_range_all_entries = url + '?min_amount=9.99&max_amount=100.01'
        url_exact_amount = url + '?min_amount=60&max_amount=60'
        url_no_intersection = url + '?min_amount=100&max_amount=0'

        self.assert_get_data(url_range_slice, [
            payment_min, payment_above_min, payment_center, payment_below_max,
            payment_max
        ])
        self.assert_get_data(url_range_all_entries, [
            payment_below_min, payment_min, payment_above_min, payment_center,
            payment_below_max, payment_max, payment_above_max
        ])
        self.assert_get_data(url_exact_amount, [payment_center])
        self.assert_get_data(url_no_intersection, [])
Exemple #4
0
    def test_filter_provider(self):
        customer = CustomerFactory()

        payment = PaymentFactory.create(customer=customer,
                                        provider__name='Gigel')

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

        iexact_url = url + '?provider=gigel'
        exact_url = url + '?provider=Gigel'
        url_no_output = url + '?provider=RANDOM'

        self.assert_get_data(iexact_url, [payment])
        self.assert_get_data(exact_url, [payment])
        self.assert_get_data(url_no_output, [])
Exemple #5
0
    def test_filter_visible(self):
        customer = CustomerFactory()

        payment_visible = PaymentFactory.create(customer=customer,
                                                visible=True)
        payment_not_visible = PaymentFactory.create(customer=customer,
                                                    visible=False)

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

        visible_url = url + '?visible=True'
        not_visible_url = url + '?visible=False'

        self.assert_get_data(visible_url, [payment_visible])
        self.assert_get_data(not_visible_url, [payment_not_visible])
Exemple #6
0
    def test_filters_currency(self):
        customer = CustomerFactory()

        payment_usd = PaymentFactory.create(customer=customer, currency='USD')
        payment_std = PaymentFactory.create(customer=customer, currency='STD')

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

        url_usd_iexact = url + '?currency=UsD'
        url_usd_exact = url + '?currency=USD'
        url_std = url + '?currency=STD'
        url_no_output = url + '?currency=RANDOM'

        self.assert_get_data(url_usd_iexact, [payment_usd])
        self.assert_get_data(url_usd_exact, [payment_usd])
        self.assert_get_data(url_std, [payment_std])
        self.assert_get_data(url_no_output, [])
Exemple #7
0
    def test_filter_flow(self):
        customer = CustomerFactory()

        payment_flow_invoice = PaymentFactory.create(
            customer=customer, provider__flow=Provider.FLOWS.INVOICE)
        payment_flow_proforma = PaymentFactory.create(
            customer=customer, provider__flow=Provider.FLOWS.PROFORMA)

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

        invoice_url = url + '?flow=' + Provider.FLOWS.INVOICE
        proforma_url = url + '?flow=' + Provider.FLOWS.PROFORMA
        url_no_output = url + '?flow=RANDOM'

        self.assert_get_data(invoice_url, [payment_flow_invoice])
        self.assert_get_data(proforma_url, [payment_flow_proforma])
        self.assert_get_data(url_no_output, [])
Exemple #8
0
    def test_filter_status(self):
        customer = CustomerFactory()

        payment_paid_status = PaymentFactory.create(customer=customer,
                                                    status=Payment.Status.Paid)
        payment_canceled_status = PaymentFactory.create(
            customer=customer, status=Payment.Status.Canceled)

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

        url_paid_only = url + '?status=' + Payment.Status.Paid
        url_canceled_only = url + '?status=' + Payment.Status.Canceled
        url_no_output = url + '?status=RANDOM'

        self.assert_get_data(url_paid_only, [payment_paid_status])
        self.assert_get_data(url_canceled_only, [payment_canceled_status])
        self.assert_get_data(url_no_output, [])
    def _create_default_payment_method(self):
        # 0 for easy asserting.
        customer = CustomerFactory(sales_tax_percent=0,
                                   currency='USD',
                                   first_name="Captain",
                                   last_name="Hook")

        # Create customer payment method
        payment_method = PaymentMethodFactory.create(
            payment_processor=triggered_processor,
            customer=customer,
            canceled=False,
            verified=True,
            data={
                # Wait until payment day X to begin retry attempts
                'attempt_retries_after': 2,

                # Stop attempts on day X
                'stop_retry_attempts': 5,
            })

        return customer, payment_method
Exemple #10
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)
Exemple #11
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
Exemple #12
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)
Exemple #13
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)
Exemple #14
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