示例#1
0
 def test_one_invoice_paid_with_discount(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs([(self.job, A(595), A(595), A())],
                 D(595),
                 transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             (
                 "progress",
                 "1000.00",
                 "190.00",
                 "1190.00",
             ),  # discount does not reduced the progress
             (
                 "payment",
                 "-500.00",
                 "-95.00",
                 "-595.00",
             ),  # invoice is not shown because it was 'fully paid'
             ("discount", "-500.00", "-95.00", "-595.00"),  # discount shown
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#2
0
    def test_invoices_with_adjustment_and_payment_project_41_issue_2016_02_24(
            self):
        debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                   transacted_on=days_ago(6))
        credit_jobs([(self.job, A(1190), A(0), A(0))],
                    D(1190),
                    transacted_on=days_ago(5))

        debit_jobs([(self.job, A(1019), Entry.WORK_DEBIT)],
                   transacted_on=days_ago(4))
        adjust_jobs([(self.job, A(-900))], transacted_on=days_ago(3))
        credit_jobs([(self.job, A(119), A(0), A(0))],
                    D(119),
                    transacted_on=days_ago(2))

        txn = debit_jobs([(self.job, A(119), Entry.WORK_DEBIT)],
                         transacted_on=days_ago(1))

        self.assertEqual(
            self.tbl(txn),
            [
                ("", "net", "tax", "gross"),
                ("progress", "1200.00", "228.00", "1428.00"),
                ("payment", "-1000.00", "-190.00", "-1190.00"),
                ("payment", "-100.00", "-19.00", "-119.00"),
                ("debit", "100.00", "19.00", "119.00"),
            ],
        )
示例#3
0
 def test_one_invoice_paid_with_discount_and_adjustment(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs(
         [(self.job, A(595), A(297.50), A(297.50))],
         D(595),
         transacted_on=days_ago(1),
     )
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             (
                 "progress",
                 "750.00",
                 "142.50",
                 "892.50",
             ),  # progress reduced by adjustment
             (
                 "payment",
                 "-500.00",
                 "-95.00",
                 "-595.00",
             ),  # invoice is not shown because it was 'fully paid'
             ("discount", "-250.00", "-47.50",
              "-297.50"),  # also we have a discount
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#4
0
    def test_project_150_issue_2016_01_17(self):
        debit_jobs([(self.job, A(28560), Entry.WORK_DEBIT)],
                   transacted_on=days_ago(4))
        debit_jobs([(self.job, A(10569.95), Entry.WORK_DEBIT)],
                   transacted_on=days_ago(3))
        debit_jobs([(self.job, A(14045.95), Entry.WORK_DEBIT)],
                   transacted_on=days_ago(2))

        credit_jobs([(self.job, A(28560), A(0), A(0))],
                    D(28560),
                    transacted_on=days_ago(3))
        credit_jobs([(self.job, A(8925), A(0), A(1644.95))],
                    D(8925),
                    transacted_on=days_ago(2))

        self.assertEqual(
            self.tbl(),
            [
                ("", "net", "tax", "gross"),
                ("progress", "43303.32", "8227.63", "51530.95"),
                ("payment", "-24000.00", "-4560.00", "-28560.00"),
                ("payment", "-7500.00", "-1425.00", "-8925.00"),
                ("unpaid", "-11803.32", "-2242.63", "-14045.95"),
                ("debit", "0.00", "0.00", "0.00"),
            ],
        )
示例#5
0
 def test_two_invoices_no_payment(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)])
     txn = debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)])
     self.assertEqual(
         self.tbl(txn),
         [
             ("", "net", "tax", "gross"),
             ("progress", "2000.00", "380.00", "2380.00"),
             ("unpaid", "-1000.00", "-190.00", "-1190.00"),
             ("debit", "1000.00", "190.00", "1190.00"),
         ],
     )
示例#6
0
 def test_payment(self):
     """ Best case is when payment matches debit. """
     debit_jobs([(self.job, A(480), Entry.WORK_DEBIT)])
     credit_jobs([(self.job, A(480), A(0), A(0))], D(480))
     self.assert_balances(
         bank=A(480, 0, 0),
         invoiced=A(480),
         paid=A(-480),
         partial=A(480).net_amount,
         tax=A(480).tax_amount,
     )
示例#7
0
 def test_one_fully_paid_invoice(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs([(self.job, A(1190), A(), A())],
                 D(1190),
                 transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             ("progress", "1000.00", "190.00", "1190.00"),
             ("payment", "-1000.00", "-190.00", "-1190.00"),
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#8
0
 def test_discounted_payment_matching_debit_with_recognized_revenue(self):
     """ Payment entered to revenue recognized account along with a cash discount
         to exactly match the debit.
     """
     debit_jobs([(self.job, A(500), Entry.WORK_DEBIT)],
                recognize_revenue=True)
     credit_jobs([(self.job, A(480), A(20), A(0))], D(480))
     self.assert_balances(
         bank=A(480, 0, 0),
         invoiced=A(500),
         paid=A(-500),
         income=A(500).net_amount,
         tax=A(480).tax_amount,
         discounts=A(-20).net_amount,
     )
示例#9
0
 def test_one_invoice_paid_with_adjustment(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs([(self.job, A(595), A(), A(595))],
                 D(595),
                 transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             ("progress", "500.00", "95.00", "595.00"),
             ("payment", "-500.00", "-95.00", "-595.00"),
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#10
0
 def test_stale_debit_gets_updated(self):
     # just like above test, but now is_override == False
     job_json = {"job.id": self.job.id, "debit": A(119.00)}
     form = self.make_form(json={"jobs": [job_json]}).formset[0]
     self.assertTrue(form.is_itemized)
     self.assertEqual(D("480.00"), form["debit_net"].value())
     self.assertEqual(D("571.20"), form.debit_amount.gross)
示例#11
0
 def test_initial_form_no_debit(self):
     debit_jobs([(self.job, A(571.20), Entry.WORK_DEBIT)])
     # now there is nothing new to invoice, we invoiced the full amount already
     form = self.make_form().formset[0]
     self.assertTrue(form.is_itemized)
     self.assertEqual(D("0.00"), form["debit_net"].value())
     self.assertEqual(D("0.00"), form.debit_amount.gross)
示例#12
0
 def test_two_invoices_both_fully_paid(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(3))
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs([(self.job, A(2380), A(), A())],
                 D(2380),
                 transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             ("progress", "2000.00", "380.00", "2380.00"),
             ("payment", "-2000.00", "-380.00", "-2380.00"),
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#13
0
 def test_transaction_gets_rolled_back_to_calculate_debit(self):
     # just like test_initial_no_debit_no_txn, but with transaction present
     txn = debit_jobs([(self.job, A(571.20), Entry.WORK_DEBIT)])
     # debit will get deleted and rollback to get previous accounting state
     form = self.make_form(txn=txn).formset[0]
     self.assertTrue(form.is_itemized)
     self.assertEqual(D("480.00"), form["debit_net"].value())
     self.assertEqual(D("571.20"), form.debit_amount.gross)
示例#14
0
 def test_two_invoices_with_tiny_payment(self):
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(3))
     debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     credit_jobs([(self.job, A(119), A(), A())],
                 D(119),
                 transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             ("progress", "2000.00", "380.00", "2380.00"),
             ("payment", "-100.00", "-19.00", "-119.00"),
             ("unpaid", "-1900.00", "-361.00", "-2261.00"),
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#15
0
 def tbl(self, invoice_txn=None, jobs=None):
     if not invoice_txn:
         invoice_txn = debit_jobs([(self.job, A(0), Entry.WORK_DEBIT)],
                                  transacted_on=days_ago(0))
     if not jobs:
         jobs = [self.job]
     report = create_invoice_report(invoice_txn, jobs)
     table = create_invoice_table(report)
     return [tuple(str(cell) for cell in row[:-1]) for row in table]
示例#16
0
 def test_one_invoice_with_adjustment_and_unpaid_portion(self):
     debit_jobs([(self.job, A(1019), Entry.WORK_DEBIT)],
                transacted_on=days_ago(2))
     adjust_jobs([(self.job, A(-900))], transacted_on=days_ago(1))
     self.assertEqual(
         self.tbl(),
         [
             ("", "net", "tax", "gross"),
             (
                 "progress",
                 "100.00",
                 "19.00",
                 "119.00",
             ),  # progress reduced by adjustment
             ("unpaid", "-100.00", "-19.00",
              "-119.00"),  # reduced invoice is shown
             ("debit", "0.00", "0.00", "0.00"),
         ],
     )
示例#17
0
 def test_one_invoice_no_payment(self):
     txn = debit_jobs([(self.job, A(1190), Entry.WORK_DEBIT)])
     self.assertEqual(
         self.tbl(txn),
         [
             ("", "net", "tax", "gross"),
             ("progress", "1000.00", "190.00", "1190.00"),
             ("debit", "1000.00", "190.00", "1190.00"),
         ],
     )
示例#18
0
 def test_override_debit(self):
     job_json = {
         "job.id": self.job.id,
         "is_override": "True",
         "debit": A(119.00)
     }
     form = self.make_form(json={"jobs": [job_json]}).formset[0]
     self.assertFalse(form.is_itemized)
     self.assertEqual(D("100.00"), form["debit_net"].value())
     self.assertEqual(D("119.00"), form.debit_amount.gross)
示例#19
0
 def test_revenue_debits(self):
     """ Enter a debit and enable revenue recognition, then perform another debit but this time
         without explicit revenue recognition since job already knows it's in revenue recognition mode.
     """
     debit_jobs([(self.job, A(480), Entry.WORK_DEBIT)],
                recognize_revenue=True)
     self.assert_balances(
         balance=A(480),
         invoiced=A(480),
         income=A(480).net_amount,
         tax=A(480).tax_amount,
     )
     debit_jobs([(self.job, A(480), Entry.WORK_DEBIT)
                 ])  # this job is already in revenue recognition mode
     self.assert_balances(
         balance=A(960),
         invoiced=A(960),
         income=A(960).net_amount,
         tax=A(960).tax_amount,
     )  # <- gross we're owed
示例#20
0
 def caseA_test(self, case):
     """ Covers the case where either net or tax are zero but the other part is greater than zero.
     """
     debit_jobs([(self.job, case, Entry.FLAT_DEBIT)])
     self.assert_balances(balance=case, invoiced=case, promised=case)
     debit_jobs([(self.job, A(0), Entry.FLAT_DEBIT)],
                recognize_revenue=True)
     self.assert_balances(
         balance=case,
         invoiced=case,
         credited=case.
         negate,  # the recognized revenue debit first clears the oustanding balance
         debited=case +
         case,  # the recognized revenue debit then re-debits the outstanding balance
         paid=A(
             0
         ),  # there is income due to revenue recognition, but nothing actually paid yet
         income=case.net_amount,
         tax=case.tax_amount,
     )
示例#21
0
 def caseB_test(self, payment, debit):
     """ Covers the case where one part is negative and the other is positive.
         Because it's not actually possible to end up with a negative net or tax on
         on the invoice (don't want negative invoice) we have to zero-out the negative
         part in the final debit.
     """
     credit_jobs(
         [(self.job, payment, A(0), A(0))],
         payment.gross)  # this creates the 'negative' part of balance
     debit_jobs([(self.job, debit, Entry.FLAT_DEBIT)
                 ])  # this creates the 'positive' part of balance
     case = (payment.negate + debit
             )  # this is either net:-0.01,tax:0.01 or net:0.01,tax:-0.01
     self.assert_balances(
         bank=A(payment.gross, 0, 0),
         balance=case,
         invoiced=debit,
         promised=case,
         partial=payment.net_amount,
         tax=payment.tax_amount,
         paid=payment.negate,
     )
     zero_out_payment = A(
         n=payment.net, t=payment.tax
     )  # we can't create final invoice with negative net/tax
     debit_jobs([(self.job, zero_out_payment, Entry.FLAT_DEBIT)],
                recognize_revenue=True)
     self.assert_balances(
         bank=A(payment.gross, 0, 0),
         balance=debit,
         invoiced=payment + debit,
         credited=A(n="0.01", t="0.01").
         negate,  # the recognized revenue debit first clears the oustanding balance
         debited=debit + debit +
         zero_out_payment,  # the recognized revenue debit then re-debits the outstanding balance
         paid=payment.negate,
         income=A(n="0.01"),
         tax=A(t="0.01"),
     )
示例#22
0
 def test_case4(self):
     self.caseB_test(A(t="0.01"), A(n="0.01"))
示例#23
0
 def test_adjusted_payment_below_debit_with_recognized_revenue(self):
     """ Payment entered to revenue recognized account along with an adjustment
         but still not enough to cover the debit.
     """
     debit_jobs([(self.job, A(600), Entry.WORK_DEBIT)],
                recognize_revenue=True)
     credit_jobs([(self.job, A(480), A(0), A(20))], D(480))
     self.assert_balances(
         bank=A(480, 0, 0),
         balance=A(100),  # <- job still has some balance
         invoiced=A(580),
         paid=A(-480),  # <- 20.00 adjusted
         debited=A(600),
         credited=A(-500),
         income=A(580).net_amount,
         tax=A(580).tax_amount,
     )  # <- income is higher than bank balance
示例#24
0
 def test_case2(self):
     self.caseA_test(A(n="0.01", t="0.00"))
示例#25
0
    def assert_balances(
            self,
            bank=A(),
            balance=A(),
            invoiced=None,
            debited=None,
            paid=None,
            credited=None,
            promised=A(),
            discounts=A(),
            partial=A(),
            income=A(),
            tax=A(),
            switch_to_job=None,
    ):

        if switch_to_job is not None:
            original_job = self.job
            self.job = switch_to_job

        if invoiced and not debited:
            debited = invoiced
        elif not invoiced and debited:
            invoiced = debited
        elif not invoiced and not debited:
            invoiced = A()
            debited = A()

        if paid and not credited:
            credited = paid
        elif not paid and credited:
            paid = credited
        elif not paid and not credited:
            paid = A()
            credited = A()

        self.assertEquals(balance, self.job.account.balance)
        self.assertEquals(invoiced, self.job.account.invoiced)
        self.assertEquals(debited, self.job.account.debits.sum)
        self.assertEquals(paid, self.job.account.paid)
        self.assertEquals(credited, self.job.account.credits.sum)

        self.assertEquals(promised, promised_payments().balance)
        self.assertEquals(partial, partial_payments().balance)
        self.assertEquals(income, income_account().balance)
        self.assertEquals(tax, tax_account().balance)
        self.assertEquals(discounts, discount_account().balance)
        self.assertEquals(bank, bank_account().balance)

        if switch_to_job:
            self.job = original_job
示例#26
0
    def test_refund_with_applied_refund_and_bank_refund_and_recognized_revenue(
            self):
        """ Customer overpays final invoice and the overpayment is further incorrectly applied.
            Issue a partial refund to first job, apply that to the second job, refund to customer the rest.
        """

        # Invoice 600.00
        debit_jobs(
            [
                (self.job, A(580), Entry.WORK_DEBIT),
                (self.job2, A(20), Entry.WORK_DEBIT),
            ],
            recognize_revenue=True,
        )

        # Payment of 700.00 is incorrectly applied to first job
        credit_jobs([(self.job, A(700), A(0), A(0))], D(700))

        one = A(n="-0.01", t="0.01")

        self.assert_balances(
            bank=A(700, 0, 0),
            balance=A("-120") + one,
            debited=A(
                580
            ),  # invoice debit (680) + refund debit (0) = total debited (680)
            invoiced=A(
                580
            ),  # invoice debit (680) + adjustment (0) = total invoiced (680)
            paid=A(-700
                   ),  # payment credit (-700) + refund debit (0) = paid (-700)
            credited=A(
                -700
            ),  # payment credit (-700) + adjustment (0) = total credited (-700)
            income=A(600).net_amount,
            tax=A(600).tax_amount,
        )

        self.assert_balances(
            bank=A(700, 0, 0),
            balance=A(20),
            debited=A(
                20
            ),  # invoice debit (20) + refund debit (0) = total debited (20)
            invoiced=A(
                20
            ),  # invoice debit (20) + adjustment (0) = total invoiced (20)
            paid=A(0),  # payment credit (0) + refund debit (0) = paid (0)
            credited=A(
                0),  # payment credit (0) + adjustment (0) = total credited (0)
            income=A(600).net_amount,
            tax=A(600).tax_amount,
            switch_to_job=self.job2,
        )

        # Refund 20.00 from first job and apply to second job
        refund_jobs([(self.job, A(120) - one, A(0)), (self.job2, A(0), A(20))])

        self.assert_balances(
            bank=A(600, 0, 0),
            balance=A(0),
            debited=A(
                700
            ),  # invoice debit (680) + refund debit (20) = total debited (700)
            invoiced=A(
                580
            ),  # invoice debit (680) + adjustment (0) = total invoiced (680)
            paid=A(
                -580
            ),  # payment credit (-700) + refund debit (20) = paid (-680)
            credited=A(
                -700
            ),  # payment credit (-700) + adjustment (0) = total credited (-700)
            income=A(600).net_amount,
            tax=A(600).tax_amount,
        )

        self.assert_balances(
            bank=A(600, 0, 0),
            balance=A(0),
            debited=A(
                20
            ),  # invoice debit (20) + refund debit (20) = total debited (70)
            invoiced=A(
                20
            ),  # invoice debit (20) + adjustment (0) = total invoiced (20)
            paid=A(
                -20),  # payment credit (-20) + refund debit (0) = paid (-20)
            credited=A(
                -20
            ),  # payment credit (-20) + adjustment (0) = total credited (-20)
            income=A(600).net_amount,
            tax=A(600).tax_amount,
            switch_to_job=self.job2,
        )
示例#27
0
    def test_happy_path_scenario(self):
        """ Tests the simple job life cycle from progress invoice to final payment.
        """
        debit_jobs([(self.job, A(480), Entry.FLAT_DEBIT)])  # progress invoice
        credit_jobs([(self.job, A(100), A(0), A(0))],
                    D(100))  # progress payment
        debit_jobs([(self.job, A(480), Entry.FLAT_DEBIT)],
                   recognize_revenue=True)  # final invoice
        credit_jobs([(self.job, A(800), A(60), A(0))], D(800))  # final payment

        self.assert_balances(
            bank=A(900, 0, 0),
            invoiced=A(960),
            paid=A(-960),
            debited=A(480 * 2 + 380),
            credited=A(-480 * 2 - 380),
            income=A(960).net_amount,
            tax=A(900).tax_amount,
            discounts=A(-60).net_amount,
        )

        total_income = income_account().balance + discount_account().balance
        self.assertEqual(total_income, A(900).net_amount)
示例#28
0
 def test_discounted_payment_below_debit_with_recognized_revenue(self):
     """ Payment entered to revenue recognized account along with a cash discount
         but still not enough to cover the debit.
     """
     debit_jobs([(self.job, A(600), Entry.WORK_DEBIT)],
                recognize_revenue=True)
     credit_jobs([(self.job, A(480), A(20), A(0))], D(480))
     self.assert_balances(
         bank=A(480, 0, 0),
         balance=A(100),
         invoiced=A(600),
         paid=A(-500),
         income=A(600).net_amount,
         tax=A(580).tax_amount,
         discounts=A(-20).net_amount,
     )
示例#29
0
    def test_previous_revenue_gets_recognized(self):
        """ Create a debit and an underpayment then transition this job into revenue recognition mode
            without actually entering any new work debits. This should move all previously promised and
            cash revenues into the main income account.
        """
        debit_jobs([(self.job, A(480), Entry.FLAT_DEBIT)
                    ])  # some promised revenue
        credit_jobs([(self.job, A(200), A(0), A(0))],
                    D(200))  # some cash revenue
        self.assert_balances(
            bank=A(200, 0, 0),
            balance=A(280),
            invoiced=A(480),
            paid=A(-200),
            partial=A(200).net_amount,
            tax=A(200).tax_amount,
            promised=A(280),
        )

        # trick debit_jobs() into running a zero debit in order to transition job to recognized revenue mode
        debit_jobs([(self.job, A(0), Entry.FLAT_DEBIT)],
                   recognize_revenue=True)

        # income now includes the previous promised revenue and cash revenue
        self.assert_balances(
            bank=A(200, 0, 0),
            balance=A(280),
            invoiced=A(480),
            paid=A(-200),
            debited=A(480) + A(
                280
            ),  # invoice debit (480) + reset debit (280) = total debits (760)
            credited=A(
                -480
            ),  # payment credit (-200) + unpaid balance (-280) = total credits (-480)
            income=A(480).net_amount,
            tax=A(480).tax_amount,
        )
示例#30
0
 def test_overpayment_then_new_debit_with_recognized_revenue(self):
     """ Create a debit transitioning this job into revenue recognition mode then
         enter an overpayment. This tests that the extra portion of the overpayment
         is not taxed and that it takes another debit equaling the overpaid amount
         to get all taxes and income properly recognized.
     """
     debit_jobs([(self.job, A(480), Entry.FLAT_DEBIT)],
                recognize_revenue=True)
     credit_jobs([(self.job, A(960), A(0), A(0))], D(960))
     self.assert_balances(
         bank=A(960, 0, 0),
         balance=A(-480),
         invoiced=A(480),
         paid=A(-960),
         income=A(480).net_amount,
         tax=A(480).tax_amount,
     )
     debit_jobs([(self.job, A(480), Entry.FLAT_DEBIT)])
     self.assert_balances(
         bank=A(960, 0, 0),
         invoiced=A(960),
         paid=A(-960),
         income=A(960).net_amount,
         tax=A(960).tax_amount,
     )