Functional Weave
Code in Python

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)