Skip to content

BEITRÄGE: Beitragsberechnung + Sammelrechnungsposten #24

@carlobeltrame

Description

@carlobeltrame

Ausgangslage

Basierend auf der ausgewählten Beitragsart und den Beitragssätzen und dem Sammelrechnungs-Feature aus dem Core sollen Verbandsabrechnungen gestellt werden können. Dafür sind custom Rechnungsposten für die pfadi_de-spezifische Beitragsberechnung nötig, welche auf der Grundlage im Core aufbauen.

Anforderungen

Siehe Dokument Detailbeschreibung zum Datenmodell “Beitragsstruktur” und besonders das Kapitel "Rechnungslauf Gruppierung zu Gruppierung"

  • Bei der Mitgliedschafts-Abrechnung wird für jede verarbeitete Person ein Stichdatum berechnet, und die Beitragsart und Beitragssätze an diesem Datum gelten für diese Person. Das Stichdatum wird vorerst als das erste Datum der Mitgliedschaft in der Abrechnungsperiode definiert (bis wir auf Probleme damit stossen, dann könnten wir auch vereinfachen auf pauschal den ersten Tag der Abrechnungsperiode, egal wann die Person eingetreten ist).

Abgrenzungen

Der Teil "Rechnungslauf Ebene zu Mitglied" wird im separaten Ticket #25 behandelt.

Die Berechnung der Mitgliedschaftsdauer wird im separaten Ticket #21 behandelt.

Offene Fragen

  • Wird auf der Sammelrechnung ein Rechnungsposten pro Beitragsart eingefügt? Oder ein einziger Rechnungsposten, der alles zusammen berechnet? -> in der Sammelrechnung soll nur ein Posten eingefügt werden, der dann beim Generieren der Rechnungen automatisch einen Rechnungsposten für jeden aktiven Beitragssatz einfügt.

Mockup

Image

Tech-Spec

  • Umsetzung im pfadi_de Wagon
  • Finden aller relevanten Mitglieds-Rollen (erste Rolle die in der Sammelrechnungs-Periode aktiv ist, falls mehrere dann diejenige mit der tiefsten ID) und deren verknüpftem FeeKind
  • Berechnung des Eintrags in FeeKind und seiner parent-Hierarchie, welcher auf dem rechnungsstellenden Layer oder am nächsten darüber liegt
  • Berechnung des Alters zu Beginn der Sammelrechnungsperiode
  • Berechnung des günstigsten Beitragssatzes, bei dem alle Bedingungen erfüllt sind (Mitgliedschaftsdauer wird erst in BEITRÄGE: Probemitgliedschaften #21 ergänzt)

ToDo

  • Sammelrechnungsposten PeriodInvoiceTemplate::FeeCalculationItem < PeriodInvoiceTemplate::Item anlegen (enthält keine Berechnungslogik, ist nur ein Platzhalter der in Sammelrechnungen eingefügt werden kann)
    • views/period_invoice_template/_fee_calculation_item_fields.html.haml leer anlegen (allenfalls mit Erklärung ähnlich Mockup)
    • Eingabefelder für Preis und MWSt. werden nicht gebraucht. Kostenstelle und Konto dürfen angezeigt werden oder nicht, je nach dem was einfacher ist. (Könnte auch Core-Änderung und damit SWB-Wagon-Änderung nötig machen)
    • Allenfalls hidden inputs mit dummy Werten für die versteckten Felder einfügen, welche in der DB obligatorisch sind
  • Rechnungsposten Invoice::FeeCalculationItem < Invoice::PeriodItem anlegen mit einem dynamic_cost_parameter fee_rate_id
  • Im Core in PeriodInvoiceTemplates::InvoiceRunsController#assign_invoice_items das map zu flat_map ändern, sodass Wagons auch aus einem Sammelrechnungsposten mehrere oder keine Rechnungsposten generieren können.
  • In PeriodInvoiceTemplate::FeeCalculationItem#to_invoice_item alle aktiven FeeRates der Ebene berechnen und pro FeeRate ein Invoice::FeeCalculationItem mit der entsprechenden fee_rate_id generieren.
  • In Invoice::FeeCalculationItem die Methode unit_cost überschreiben und den Betrag des verlinkten Beitragssatzes zurückgeben
  • In #subject_type Person (als Klasse, nicht als String) zurückgeben
  • In #base_scope Person.joins(:roles) zurückgeben
  • #scope überschreiben und an super eine fee_rate_condition anhängen, welche nur Personen akzeptiert, welche gemäss der Domainklasse FeeCalculator (siehe unten) die richtige fee_kind_id für den Rechnungsempfänger recipient_id_expression haben
  • Domainklasse FeeCalculator anlegen, welche mit einen Zeitraum und einem Beitragssatz eine Liste von Personen berechnen kann, bei welchen dieser Beitragssatz gilt. Wenn irgendwie möglich als SQL Query ohne etwas in Ruby Memory zu laden. Logik sollte wiederverwendbar sein, sodass auch in die andere Richtung berechnet werden kann: Gegeben eine Person, finde die dazugehörige fee_kind_id
    • Aus dem Beitragssatz wird das Ziel-Layer berechnet
    • Mitgliedsrolle suchen: Person <-> Rolle (nur Rollen im oder unterhalb dem Ziel-Layer, nur beitragspflichtige Rollen, nur die erste in der Rechnungsperiode aktive Rolle, tiefere Rollen-ID gewinnt)
    • FeeKind der Mitgliedsrolle finden
    • parent-Hierarchie des FeeKinds durchsuchen nach einem Ziel-FeeKind auf dem Ziel-Layer
    • Auf Ziel-FeeKind den #total_yearly_amount(person, MAX(membership_role.start_on, period_start_on)) / (duration_in_years) berechnen, wobei duration_in_years = (period_end_on - period_start_on) / 1 year ist, gerundet auf das nächste Vielfache von 0.5.
  • Specs schreiben
  • Kunde wegen Übersetzungen informieren
  • Mit angemessener Rolle "durchklicken"
  • DoD geprüft und erfüllt?
  • CHANGELOG-Eintrag unter "unreleased" unten hinzufügen

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions