from typing import List, Sequence from .finance_credit_note_calculate_types import CreditRequest from .finance_invoice_calculate import calculate_invoice from .finance_invoice_calculate_types import Invoice, InvoiceLine from .finance_invoice_line_total import line_total from .money_amount import money 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)