def init_amount(self, name): initial_amount = self.initial.get(name, Amount.zero()) self.initial[name + "_net"] = initial_amount.net self.initial[name + "_tax"] = initial_amount.tax initial_or_updated_amount = Amount( convert_field_to_value(self[name + "_net"]), convert_field_to_value(self[name + "_tax"]), ) setattr(self, name + "_amount", initial_or_updated_amount)
def set_payment(self, splits): self.json["payment"] = Amount.zero() for job in self.json["jobs"]: credit = Amount.zero() for row in splits: if job["job.id"] == row["job.id"]: credit = row["credit"] break job["payment"] = credit self.json["payment"] += credit
def calculate_totals(self, totals, condition=lambda form: True): for total in totals: total_field = ( total[:-4] + "total_diff_amount" if total.endswith("_diff") else total + "_total_amount" ) total_field = total_field.replace(".", "_") setattr(self, total_field, Amount.zero()) for form in self.formset: if condition(form): for total in totals: total_field = ( total[:-4] + "total_diff_amount" if total.endswith("_diff") else total + "_total_amount" ) if "." in total: state_name, attr_name = total.split(".") state = getattr(form, state_name) form_amount = getattr(state, attr_name + "_amount") else: form_amount = getattr(form, total + "_amount") total_field = total_field.replace(".", "_") total_amount = getattr(self, total_field) setattr(self, total_field, total_amount + form_amount)
def calculate_initial_refund(self): refund_total = Amount.zero() for form in self.formset: refund_total += form.refund_amount for form in self.formset: refund_total = form.consume_refund(refund_total) return refund_total
def clean(self): splits = Amount.zero() for form in self.formset: splits += form.split_amount if splits.gross != self.payment_value: raise forms.ValidationError( _("The sum of splits must equal the payment amount.") )
def calculate_accounting_state(self, state): state.paid_amount = self.job.account.paid.negate state.invoiced_amount = self.job.account.invoiced state.invoiced_diff_amount = state.invoiced_amount - state.paid_amount state.progress_amount = Amount.from_net(self.job.progress, TAX_RATE) state.progress_diff_amount = state.progress_amount - state.invoiced_amount
def calculate_accounting_state(self, state): state.estimate_amount = Amount.from_net(self.job.estimate, TAX_RATE) state.progress_amount = Amount.from_net(self.job.progress, TAX_RATE) state.progress_percent = self.job.progress_percent state.invoiced_amount = self.job.account.invoiced state.invoiced_percent = 0 if state.progress_amount.net > 0: state.invoiced_percent = ( state.invoiced_amount.net / state.progress_amount.net * 100 ) state.balance_amount = self.job.account.balance state.itemized_amount = state.progress_amount - state.invoiced_amount if state.itemized_amount.gross < 0: state.itemized_amount = Amount.zero()
def calculate_accounting_state(self, state): # Paid Column state.paid_amount = self.job.account.paid.negate # Invoiced Column state.invoiced_amount = self.job.account.invoiced state.invoiced_diff_amount = state.invoiced_amount - state.paid_amount # Billable Column state.progress_amount = Amount.from_net(self.job.progress, TAX_RATE) state.progress_diff_amount = state.progress_amount - state.invoiced_amount
def set_adjustments(self, adjustments): self.json["adjustment"] = Amount.zero() self.json["corrected"] = Amount.zero() for job in self.json["jobs"]: adjustment = Amount.zero() corrected = Amount.zero() for row in adjustments: if job["job.id"] == row["job.id"]: adjustment = row["adjustment"] corrected = row["corrected"] break job["adjustment"] = adjustment self.json["adjustment"] += adjustment job["corrected"] = corrected self.json["corrected"] += corrected
def migrate_invoices(apps, schema_editor): from systori.apps.company.models import Company from systori.apps.document.models import Invoice from systori.apps.task.models import Job from systori.apps.accounting.report import create_invoice_report from systori.apps.accounting.constants import TAX_RATE from systori.lib.accounting.tools import Amount for company in Company.objects.all(): company.activate() for invoice in Invoice.objects.all(): invoice.json["debit"] = Amount(invoice.json["debit_net"], invoice.json["debit_tax"]) if "debits" not in invoice.json: invoice.json["jobs"] = [] invoice.save() continue invoice.json["jobs"] = invoice.json["debits"] del invoice.json["debits"] jobs = Job.objects.filter( id__in=[job["job.id"] for job in invoice.json["jobs"]]) tdate = date( *map(int, invoice.json["transactions"][-1]["date"].split("-"))) new_json = create_invoice_report(invoice.transaction, jobs, tdate) if (company.schema == "mehr_handwerk" and invoice.id not in [86, 111]) or ( company.schema == "montageservice_grad" and invoice.id not in [1]): assert new_json["debit"].gross == invoice.json["debit"].gross assert new_json["invoiced"].gross == invoice.json[ "debited_gross"] invoice.json.update(new_json) for job in invoice.json["jobs"]: taskgroup_total = Decimal("0.00") for taskgroup in job["taskgroups"]: taskgroup_total += taskgroup["total"] job["progress"] = Amount.from_net(taskgroup_total, TAX_RATE) invoiced_total = Amount.zero() for debit in invoice.json["job_debits"].get(job["job.id"], []): invoiced_total += debit["amount"] job["invoiced"] = invoiced_total job["debit"] = Amount(job["amount_net"], job["amount_tax"]) job["balance"] = Amount(job["balance_net"], job["balance_tax"]) job["estimate"] = Amount.from_net(job["estimate_net"], TAX_RATE) job["is_itemized"] = job["invoiced"].gross == job[ "progress"].gross invoice.save()
def calculate_initial_values(self): if "invoiced" in self.initial: self.pre_txn.invoiced_amount = self.initial["invoiced"] self.pre_txn.invoiced_diff_amount = Amount.zero() if "corrected" not in self.initial: self.initial["corrected"] = self.pre_txn.invoiced_amount self.init_amount("corrected") if "adjustment" not in self.initial: self.initial["adjustment"] = ( self.corrected_amount - self.pre_txn.invoiced_amount ) self.init_amount("adjustment")
def calculate_initial_values(self): self.balance_amount = self.pre_txn.balance_amount if "invoiced" in self.initial: # when paying an invoice, the balance is taken from invoice instead of accouting system self.balance_amount = self.initial["invoiced"] self.init_amount("split") self.init_amount("discount") self.init_amount("adjustment") if self.balance_amount.gross < 0: self.balance_amount = Amount.zero() self.credit_amount = ( self.split_amount + self.discount_amount + self.adjustment_amount )
def __init__(self, *args, **kwargs): super().__init__(*args, formset_class=RefundFormSet, **kwargs) if not self.instance.id: self.calculate_initial_refund() self.calculate_totals( [ "pre_txn.paid", "pre_txn.invoiced", "pre_txn.invoiced_diff", "pre_txn.progress", "pre_txn.progress_diff", "refund", "credit", ] ) self.customer_refund_amount = Amount.zero() if self.refund_total_amount.gross > self.credit_total_amount.gross: self.customer_refund_amount = ( self.refund_total_amount - self.credit_total_amount )
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["status_filter"] = self.status_filter query = self.get_queryset() query = (query.prefetch_related("project").prefetch_related( "parent").filter(document_date__gte=date(2015, 9, 1)).order_by( "-document_date", "invoice_no")) months = OrderedDict() for invoice in query: doc_date = date(invoice.document_date.year, invoice.document_date.month, 1) month = months.setdefault(doc_date, { "invoices": [], "debit": Amount.zero() }) month["debit"] += invoice.json["debit"] month["invoices"].append(invoice) context["invoice_groups"] = months return context
def create_refund_report(txn): refund = {"txn": txn} jobs = {} for entry in txn.entries.all(): if not entry.account.is_receivable: continue job = jobs.setdefault( entry.job.id, { "job.id": entry.job.id, "code": entry.job.code, "name": entry.job.name, "amount": Amount.zero(), }, ) job["amount"] += entry.amount return refund
def calculate_initial_values(self): # on first load BooleanField.value() comes from self.initial and is an actual bool() type # on subsequent submits, value() returns a string from self.data, this makes # testing for truthyness difficult, so lets just always use the string version # unless you have self.cleaned_data available which is best self.is_invoiced_str = str(self["is_invoiced"].value()) self.is_override_str = str(self["is_override"].value()) if self.is_override_str == "False": if self.pre_txn.itemized_amount.net > 0: self.initial["debit"] = self.pre_txn.itemized_amount if "debit" not in self.initial: self.initial["debit"] = Amount.zero() self.init_amount("debit") self.post_txn = SimpleNamespace() self.post_txn.invoiced_amount = self.pre_txn.invoiced_amount + self.debit_amount self.post_txn.balance_amount = self.pre_txn.balance_amount + self.debit_amount self.is_itemized = True if self.post_txn.invoiced_amount != self.pre_txn.progress_amount: self.is_itemized = False
def create_invoice_report(invoice_txn, jobs, transacted_on_or_before=None): """ :param invoice_txn: transaction that should be excluded from 'open claims' (unpaid amount) :param jobs: limit transaction details to specific set of jobs :param transacted_on_or_before: limit transactions up to and including a certain date :return: serializable data structure """ txns_query = ( Transaction.objects.filter(entries__job__in=jobs) .prefetch_related("entries__job__project") .prefetch_related("entries__account") .distinct() ) if transacted_on_or_before: txns_query = txns_query.filter(transacted_on__lte=transacted_on_or_before) transactions = list(txns_query) transactions.sort(key=_transaction_sort_key) report = { "invoiced": Amount.zero(), "paid": Amount.zero(), "payments": [], "job_debits": {}, "unpaid": Amount.zero(), "debit": Amount.zero(), } for txn in transactions: txn_dict = { "id": txn.id, "type": txn.transaction_type, "date": txn.transacted_on, "payment": Amount.zero(), "discount": Amount.zero(), "total": Amount.zero(), "jobs": {}, } job_debits = {} for entry in txn.entries.all(): # we only work with receivable accounts from here on out if not entry.account.is_receivable: continue if entry.job not in jobs: # skip jobs we're not interested in continue job_dict = txn_dict["jobs"].setdefault( entry.job.id, { "job.id": entry.job.id, "code": entry.job.code, "name": entry.job.name, "payment": Amount.zero(), "discount": Amount.zero(), "total": Amount.zero(), }, ) job_debit = job_debits.setdefault( entry.job.id, { "transaction_type": txn.transaction_type, "entry_type": None, "date": txn.transacted_on, "amount": Amount.zero(), }, ) if entry.entry_type == entry.PAYMENT: txn_dict["payment"] += entry.amount job_dict["payment"] += entry.amount elif entry.entry_type == entry.DISCOUNT: txn_dict["discount"] += entry.amount job_dict["discount"] += entry.amount if entry.entry_type in (entry.PAYMENT, entry.DISCOUNT): txn_dict["total"] += entry.amount job_dict["total"] += entry.amount if entry.entry_type in entry.TYPES_FOR_INVOICED_SUM: report["invoiced"] += entry.amount if txn.id == invoice_txn.id: report["debit"] += entry.amount assert job_debit["entry_type"] in ( None, entry.entry_type, entry.ADJUSTMENT, ) job_debit["entry_type"] = entry.entry_type job_debit["amount"] += entry.amount if entry.entry_type in entry.TYPES_FOR_PAID_SUM: report["paid"] += entry.amount if txn.transaction_type == txn.PAYMENT: report["payments"].append(txn_dict) for job_id, debit_dict in job_debits.items(): if debit_dict["amount"].gross != 0: debits = report["job_debits"].setdefault(job_id, []) debits.append(debit_dict) total_unpaid_balance = ( report["invoiced"] + report["paid"] ) # report['paid'] is a negative number report["unpaid"] = Amount.zero() if total_unpaid_balance.gross > report["debit"].gross: # There is more owed on the account than this invoice is trying to solicit. # This means we need to show the difference between what is being sought and what is owed # as unpaid amount above the debit line on invoice. report["unpaid"] = ( report["debit"] - total_unpaid_balance ) # big from small to get negative number return report
def test_project_143_having_open_claim_2016_04_06(self): txn = debit_jobs( [ (self.job, Amount.from_net(D("22721.38"), TAX_RATE), Entry.WORK_DEBIT), (self.job2, Amount.from_net(D("3400.05"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(8), ) self.assertEqual( self.tbl(txn, [self.job, self.job2]), [ ("", "net", "tax", "gross"), ("progress", "26121.43", "4963.07", "31084.50"), ("debit", "26121.43", "4963.07", "31084.50"), ], ) credit_jobs( [ (self.job, A("26227.29"), A("811.15"), A()), (self.job2, A("1572.71"), A("48.64"), A("2424.71")), ], D("27800.00"), transacted_on=days_ago(7), ) txn = debit_jobs( [ (self.job, Amount.from_net(D("8500"), TAX_RATE), Entry.WORK_DEBIT), (self.job2, Amount.from_net(D("4000"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("1000"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(6), ) self.assertEqual( self.tbl(txn, [self.job, self.job2, self.job3]), [ ("", "net", "tax", "gross"), ("progress", "37583.86", "7140.93", "44724.79"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("debit", "13500.00", "2565.00", "16065.00"), ], ) credit_jobs( [ (self.job, A("9811.55"), A("303.45"), A()), (self.job2, A("3188.45"), A("98.61"), A("1472.94")), ], D("13000.00"), transacted_on=days_ago(5), ) txn = debit_jobs( [ (self.job2, Amount.from_net(D("11895.04"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("1628.86"), TAX_RATE), Entry.WORK_DEBIT), (self.job4, Amount.from_net(D("358.31"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(4), ) self.assertEqual( self.tbl(txn, [self.job, self.job2, self.job3, self.job4]), [ ("", "net", "tax", "gross"), ("progress", "50228.31", "9543.37", "59771.68"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("payment", "-10924.37", "-2075.63", "-13000.00"), ("discount", "-337.87", "-64.19", "-402.06"), ("unpaid", "-1000.00", "-190.00", "-1190.00"), ("debit", "13882.21", "2637.62", "16519.83"), ], ) credit_jobs( [ (self.job2, A("6585.27"), A("203.67"), A("7366.16")), (self.job3, A("1938.34"), A("59.95"), A()), (self.job4, A("426.39"), A("13.19"), A()), ], D("8950.00"), transacted_on=days_ago(3), ) # Initial Case: Due to an underpaid job, invoice shows 'open claim'. dtxn = debit_jobs( [ (self.job2, Amount.from_net(D("17790.25"), TAX_RATE), Entry.WORK_DEBIT), (self.job5, Amount.from_net(D("6377.68"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("2034.90"), TAX_RATE), Entry.WORK_DEBIT), (self.job4, Amount.from_net(D("716.62"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(1), ) self.assertEqual( self.tbl(dtxn, [self.job, self.job2, self.job3, self.job4, self.job5]), [ ("", "net", "tax", "gross"), ("progress", "70957.71", "13481.96", "84439.67"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("payment", "-10924.37", "-2075.63", "-13000.00"), ("discount", "-337.87", "-64.19", "-402.06"), ("payment", "-7521.01", "-1428.99", "-8950.00"), ("discount", "-232.61", "-44.20", "-276.81"), ("unpaid", "-938.54", "-178.32", "-1116.86"), # <-- open claim ("debit", "26919.45", "5114.70", "32034.15"), ], ) dtxn.delete() # Adjusted Case: We adjust two jobs, no open claim, but progress is high due to over invoiced job. atxn = adjust_jobs( [ (self.job3, A(n="-949.62", t="-180.43")), (self.job4, A(n="11.08", t="2.11")), ], transacted_on=days_ago(1), ) dtxn = debit_jobs( [ (self.job2, Amount.from_net(D("17790.25"), TAX_RATE), Entry.WORK_DEBIT), (self.job5, Amount.from_net(D("6377.68"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("2984.52"), TAX_RATE), Entry.WORK_DEBIT), (self.job4, Amount.from_net(D("705.54"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(3), ) self.assertEqual( self.tbl(dtxn, [self.job, self.job2, self.job3, self.job4, self.job5]), [ ("", "net", "tax", "gross"), ("progress", "70957.71", "13481.96", "84439.67"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("payment", "-10924.37", "-2075.63", "-13000.00"), ("discount", "-337.87", "-64.19", "-402.06"), ("payment", "-7521.01", "-1428.99", "-8950.00"), ("discount", "-232.61", "-44.20", "-276.81"), # ('unpaid', '-938.54', '-178.32', '-1116.86'), <-- consumed into debit below ("debit", "27857.99", "5293.02", "33151.01"), ], ) atxn.delete() dtxn.delete() # Adjusted Case II: We adjust three jobs, now invoice progress is correct and there is no open claim. atxn = adjust_jobs( [ (self.job, A(n="-4678.55", t="-888.92")), (self.job3, A(n="-949.62", t="-180.43")), (self.job4, A(n="11.08", t="2.11")), ], transacted_on=days_ago(1), ) dtxn = debit_jobs( [ (self.job2, Amount.from_net(D("17790.25"), TAX_RATE), Entry.WORK_DEBIT), (self.job5, Amount.from_net(D("6377.68"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("2984.52"), TAX_RATE), Entry.WORK_DEBIT), (self.job4, Amount.from_net(D("705.54"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(3), ) self.assertEqual( self.tbl(dtxn, [self.job, self.job2, self.job3, self.job4, self.job5]), [ ("", "net", "tax", "gross"), ("progress", "66279.16", "12593.04", "78872.20"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("payment", "-10924.37", "-2075.63", "-13000.00"), ("discount", "-337.87", "-64.19", "-402.06"), ("payment", "-7521.01", "-1428.99", "-8950.00"), ("discount", "-232.61", "-44.20", "-276.81"), ("debit", "27857.99", "5293.02", "33151.01"), ], ) atxn.delete() dtxn.delete() # Adjusted & Refund Case: We adjust three jobs as before but also issue a refund. # Just making sure refund does not change the invoice in anyway. atxn = adjust_jobs( [ (self.job, A(n="-4678.55", t="-888.92")), (self.job3, A(n="-949.62", t="-180.43")), (self.job4, A(n="11.08", t="2.11")), ], transacted_on=days_ago(1), ) rtxn = refund_jobs( [ (self.job, A(n="4678.55", t="888.92"), A()), (self.job2, A(), A(n="4678.55", t="888.92")), ], transacted_on=days_ago(1), ) dtxn = debit_jobs( [ (self.job2, Amount.from_net(D("17790.25"), TAX_RATE), Entry.WORK_DEBIT), (self.job5, Amount.from_net(D("6377.68"), TAX_RATE), Entry.WORK_DEBIT), (self.job3, Amount.from_net(D("2984.52"), TAX_RATE), Entry.WORK_DEBIT), (self.job4, Amount.from_net(D("705.54"), TAX_RATE), Entry.WORK_DEBIT), ], transacted_on=days_ago(3), ) self.assertEqual( self.tbl(dtxn, [self.job, self.job2, self.job3, self.job4, self.job5]), [ ("", "net", "tax", "gross"), ("progress", "66279.16", "12593.04", "78872.20"), ("payment", "-23361.35", "-4438.65", "-27800.00"), ("discount", "-722.51", "-137.28", "-859.79"), ("payment", "-10924.37", "-2075.63", "-13000.00"), ("discount", "-337.87", "-64.19", "-402.06"), ("payment", "-7521.01", "-1428.99", "-8950.00"), ("discount", "-232.61", "-44.20", "-276.81"), ("debit", "27857.99", "5293.02", "33151.01"), ], ) atxn.delete() rtxn.delete() dtxn.delete()
class InvoiceFactory(factory.django.DjangoModelFactory): json = {"debit": Amount.zero()} class Meta: model = Invoice
def migrate_proposals(apps, schema_editor): from systori.apps.company.models import Company from systori.apps.task.models import Job from systori.apps.document.models import Proposal from systori.apps.accounting.constants import TAX_RATE from systori.lib.accounting.tools import Amount for company in Company.objects.all(): company.activate() for proposal in Proposal.objects.all(): try: if "total_base" not in proposal.json and "jobs" not in proposal.json: proposal.json["date"] = proposal.document_date proposal.json["estimate_total"] = Amount( proposal.json["total_net"], proposal.json["total_gross"] - proposal.json["total_net"], ) proposal.json["jobs"] = [] else: proposal.json["estimate_total"] = Amount( proposal.json["total_base"], proposal.json["total_tax"]) del proposal.json["total_base"], proposal.json[ "total_tax"], proposal.json["total_gross"] proposal.json["id"] = proposal.id proposal.json["title"] = _("Proposal") for job in proposal.json["jobs"]: if "id" in job: job["job.id"] = job["id"] del job["id"] else: job_obj = Job.objects.filter(project=proposal.project, name=job["name"]).get() job["job.id"] = job_obj.id job_estimate_net = Decimal("0.00") for group in job["taskgroups"]: group["estimate_net"] = group["total"] del group["total"] job_estimate_net += Decimal(group["estimate_net"]) for task in group["tasks"]: task["estimate_net"] = task["total"] del task["total"] job["estimate"] = Amount.from_net(job_estimate_net, TAX_RATE) proposal.save() except: if proposal.status == proposal.DECLINED: proposal.delete() else: raise
def migrate_payments(apps, schema_editor): from systori.apps.company.models import Company from systori.apps.document.models import Payment, DocumentSettings from systori.apps.task.models import Job from systori.apps.accounting.models import Transaction from systori.lib.accounting.tools import Amount print("payment migration..") for company in Company.objects.all(): company.activate() for t in Transaction.objects.filter( transaction_type=Transaction.PAYMENT): job = None job_dict = None jobs = {} split_t = Amount.zero() discount_t = Amount.zero() adjustment_t = Amount.zero() credit_t = Amount.zero() if t.id in [9, 154, 297, 307]: entry = t.entries.first() print( "Company: ", company.schema, ", Transaction ID:", t.id, ", Date:", t.transacted_on, ", Bank:", entry.account.name, ", Amount:", entry.amount.gross, ", Entries: ", t.entries.count(), ) continue bank_account = None for entry in t.entries.all(): if (entry.account.is_bank or entry.account.name == "VR Bank Rhein-Neckar eG"): bank_account = entry.account continue job = entry.job if job: job_dict = jobs.setdefault( job.id, { "job.id": job.id, "name": job.name, "split": Amount.zero(), "discount": Amount.zero(), "adjustment": Amount.zero(), "credit": Amount.zero(), }, ) if entry.entry_type == entry.PAYMENT: job_dict["split"] += entry.amount.negate split_t += entry.amount elif entry.entry_type == entry.DISCOUNT: job_dict["discount"] += entry.amount.negate discount_t += entry.amount elif entry.entry_type == entry.ADJUSTMENT: job_dict["adjustment"] += entry.amount.negate adjustment_t += entry.amount if entry.entry_type in ( entry.PAYMENT, entry.DISCOUNT, entry.ADJUSTMENT, ): job_dict["credit"] += entry.amount.negate credit_t += entry.amount assert job assert bank_account letterhead = DocumentSettings.get_for_language( "de").invoice_letterhead payment = Payment( project=job.project, document_date=t.transacted_on, transaction=t, letterhead=letterhead, ) payment.json.update({ "bank_account": bank_account.id, "date": t.transacted_on, "payment": credit_t.negate.gross, "discount": Decimal("0.00"), "split_total": split_t.negate, "discount_total": discount_t.negate, "adjustment_total": adjustment_t.negate, "credit_total": credit_t.negate, "jobs": jobs.values(), }) payment.save()
def test_serialize(self): invoice = Invoice.objects.create(project=self.project, letterhead=self.letterhead) invoice.json = {"jobs": [{"job": self.job}], "add_terms": False} pdf_type.invoice.serialize(invoice) self.maxDiff = None self.assertEqual( { "jobs": [{ "tasks": [], "groups": [{ "group.id": 2, "code": "01.01", "name": self.group.name, "description": "", "progress": Decimal("0.00"), "estimate": Decimal("0.0000"), "tasks": [{ "task.id": 1, "code": "01.01.001", "name": self.task.name, "description": "", "is_provisional": False, "variant_group": 0, "variant_serial": 0, "qty": Decimal("0.0000"), "complete": Decimal("0.0000"), "unit": "", "price": Decimal("0.0000"), "progress": Decimal("0.00"), "estimate": Decimal("0.0000"), "lineitems": [{ "lineitem.id": 1, "name": self.lineitem.name, "qty": Decimal("0.0000"), "unit": "", "price": Decimal("0.0000"), "estimate": Decimal("0.0000"), }], }], "groups": [], }], }], "add_terms": False, "debit": Amount.zero(), "invoiced": Amount.zero(), "paid": Amount.zero(), "unpaid": Amount.zero(), "payments": [], "job_debits": {}, }, invoice.json, )
def test_happy_path_create_update_and_render(self): response = self.client.get( reverse("refund.create", args=[self.project.id])) self.assertEqual(200, response.status_code) data = { "title": "Refund #1", "header": "new header", "footer": "new footer", "document_date": "2015-01-01", "job-0-job_id": self.job.id, "job-0-refund_net": "1", "job-0-refund_tax": "1", "job-0-credit_net": "0", "job-0-credit_tax": "0", "job-1-job_id": self.job2.id, "job-1-refund_net": "0", "job-1-refund_tax": "0", "job-1-credit_net": "1", "job-1-credit_tax": "1", } data.update(self.make_management_form()) response = self.client.post( reverse("refund.create", args=[self.project.id]), data) self.assertEqual(302, response.status_code) response = self.client.get( reverse("refund.pdf", args=[self.project.id, "print", Refund.objects.first().id])) self.assertEqual(200, response.status_code) refund = Refund.objects.order_by("id").first() self.assertEqual(refund.document_date, date(2015, 1, 1)) self.assertEqual(refund.json["refund_total"], Amount(Decimal("1.0"), Decimal("1.0"))) self.assertEqual(refund.json["credit_total"], Amount(Decimal("1.0"), Decimal("1.0"))) data = { "title": "Refund #1", "header": "updated header", "footer": "updated footer", "document_date": "2015-07-28", "job-0-job_id": self.job.id, "job-0-refund_net": "2", "job-0-refund_tax": "2", "job-0-credit_net": "0", "job-0-credit_tax": "0", "job-1-job_id": self.job2.id, "job-1-refund_net": "0", "job-1-refund_tax": "0", "job-1-credit_net": "2", "job-1-credit_tax": "2", } data.update(self.make_management_form()) response = self.client.post( reverse("refund.update", args=[self.project.id, refund.id]), data) self.assertEqual(302, response.status_code) self.assertRedirects(response, reverse("project.view", args=[self.project.id])) refund.refresh_from_db() self.assertEqual(refund.document_date, date(2015, 7, 28)) self.assertEqual(refund.json["refund_total"], Amount(Decimal("2.0"), Decimal("2.0"))) self.assertEqual(refund.json["credit_total"], Amount(Decimal("2.0"), Decimal("2.0")))
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.estimate_amount = Amount.from_net(self.job.estimate, TAX_RATE)