finance.credit-note.calculate@1.0.0
impl/python.py
3,142 bytes · the Python implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import List, Sequence
from .finance_credit_note_calculate_types import CreditRequest
from .finance_invoice_calculate import calculate_invoice ← from finance.invoice.calculate ^1.0.0 · built alongside by fune
from .finance_invoice_calculate_types import Invoice, InvoiceLine
from .finance_invoice_line_total import line_total ← from finance.invoice.line-total ^1.0.0 · built alongside by fune
from .money_amount import money ← from money.amount ^1.0.0 · built alongside by fune
def calculate_credit_note(
original_lines: Sequence[InvoiceLine],
credits: Sequence[CreditRequest],
jurisdiction: str,
original_tax_point: str,
) -> Invoice:
"""A credit note against an invoice, with VAT at the original tax point.
Every credit becomes a negative invoice line and the whole note is
calculated by finance.invoice.calculate at the original date, so the VAT
rates, rounding and grouping are the invoice's own.
"""
if len(credits) == 0:
raise ValueError("a credit note needs at least one line")
credited = [0] * len(original_lines)
lines: List[InvoiceLine] = []
for credit in credits:
n = credit.line
if isinstance(n, bool) or not isinstance(n, int) or n < 1 or n > len(original_lines):
raise ValueError("credit refers to line %s, but the invoice has %d lines" % (n, len(original_lines)))
original = original_lines[n - 1]
if original.quantity <= 0:
raise ValueError("line %d of the invoice is not a sale that can be credited" % (n,))
if (credit.quantity is None) == (credit.net is None):
raise ValueError("credit for line %d needs exactly one of quantity and net" % (n,))
if credit.quantity is not None:
if isinstance(credit.quantity, bool) or not isinstance(credit.quantity, int) or credit.quantity <= 0:
raise ValueError("credit quantity for line %d must be greater than zero" % (n,))
line = InvoiceLine(
description=original.description,
unit_price=original.unit_price,
quantity=-credit.quantity,
discount_basis_points=original.discount_basis_points,
tax_category=original.tax_category,
)
else:
net = credit.net
if net.currency != original.unit_price.currency:
raise ValueError("credit for line %d must be in %s" % (n, original.unit_price.currency))
if net.minor <= 0:
raise ValueError("credit amount for line %d must be greater than zero" % (n,))
line = InvoiceLine(
description=original.description,
unit_price=money(-net.minor, net.currency),
quantity=1,
discount_basis_points=0,
tax_category=original.tax_category,
)
credited[n - 1] -= line_total(line.unit_price, line.quantity, line.discount_basis_points).minor
invoiced = line_total(original.unit_price, original.quantity, original.discount_basis_points).minor
if credited[n - 1] > invoiced:
raise ValueError("credits against line %d exceed what it was invoiced for" % (n,))
lines.append(line)
return calculate_invoice(lines, jurisdiction, original_tax_point)