Skip to content

[Fact 01] Datamodel & migratie — facturatie-kern #146

Description

@MiniMaxi-user

Onderdeel van epic #145 (Facturatie-module). Fundament-eerst — datamodel + migratie vóór features. Geen afhankelijkheden; blokkeert Fact 02–07.

User Story

Als ontwikkelaar van de facturatie-module
wil ik een datamodel + migratie voor de facturatie-kern (Invoice + InvoiceLine)
zodat latere stories (factuurregels, voorvullen uit contract, PDF, betaalwijze, status & overzicht) op een stabiel, consistent fundament kunnen voortbouwen.

Context

Dit is de eerste story van de facturatie-epic (#145), stap 6 uit de bouwvolgorde in
CLAUDE.md. De keten matching → contract → administratie → facturatie rond het
centrale paardprofiel moet sluitend worden. Facturatie haakt op bestaande modellen:

  • Stable — de uitgevende/afzendende partij (de stal die factureert).
  • User — de ontvanger (paardeigenaar of leaser als geregistreerd account).
  • Contract — de optionele bron van de factuur (stallingscontract met pensionprijs
    en extra diensten, of leasecontract met leasevergoeding; lease-btw is 21%, zie
    src/features/lease/leaseKostenConfig.ts).
  • OwnerBusinessProfile — bevat de zakelijke/factuurgegevens van de te factureren
    klant (bedrijfsnaam, adres, KvK, btw-nummer, afwijkend factuuradres). Wordt in deze
    story niet gewijzigd; latere PDF-story (Fact 05) leest deze gegevens uit.

Dit is bewust een puur fundament-story: alleen het datamodel + de migratie. Geen
UI, geen actions, geen queries, geen factuurnummer-generatie of statusovergangslogica
— die volgen in Fact 02–07.

Scope

Binnen scope:

  • Prisma-model Invoice met:
    • stableId → relatie naar Stable (uitgevende stal), onDelete: Cascade.
    • recipientUserId → relatie naar User (ontvanger: eigenaar/leaser), nullable +
      onDelete: SetNull zodat een factuur de verwijdering van het account overleeft
      (administratie/historie blijft behouden).
    • contractIdoptionele relatie naar Contract, onDelete: SetNull (factuur
      overleeft het verwijderen van zijn bron-contract).
    • invoiceNumber (String?, nullable) — concept-facturen hebben nog geen definitief
      nummer; opvolgende nummering wordt pas in Fact 05 toegekend. Wel @unique zodat
      dubbele nummers op DB-niveau onmogelijk zijn.
    • invoiceDate (DateTime?), dueDate (DateTime?) — nullable tot vastgesteld.
    • status (enum, zie hieronder), default CONCEPT.
    • Totalen als Decimal @db.Decimal(10, 2): subtotal (excl. btw), vatAmount
      (totaal btw), total (incl. btw). Default 0.
    • notes (String?) — vrije opmerking op de factuur.
    • createdAt / updatedAt.
    • Indexen op stableId, recipientUserId, contractId.
  • Prisma-model InvoiceLine met:
    • invoiceId → relatie naar Invoice, onDelete: Cascade.
    • description (String) — omschrijving van de regel.
    • quantity (Decimal @db.Decimal(10, 2)) — aantal (Decimal i.p.v. Int, zodat
      bijv. halve maanden / deelperiodes mogelijk zijn).
    • unitPrice (Decimal @db.Decimal(10, 2)) — stuksprijs excl. btw.
    • vatRate (enum VatRate, zie hieronder) — btw-tarief per regel.
    • lineTotal (Decimal @db.Decimal(10, 2)) — regelbedrag excl. btw.
    • position (Int, default 0) — om de regelvolgorde stabiel te bewaren.
    • createdAt.
    • Index op invoiceId.
  • Enum InvoiceStatus: CONCEPT, VERZONDEN, BETAALD, VERVALLEN,
    GEANNULEERD (consistent met de Nederlandse enum-conventie en de bestaande
    ContractStatus-stijl).
  • Enum VatRate voor de toegestane btw-tarieven 0/9/21% — als enum met sprekende
    waarden, bijv. NUL, LAAG, HOOG (resp. 0%, 9%, 21%). De numerieke percentages
    worden in de app-laag aan de enum gekoppeld (latere story), niet als losse kolom.
  • Back-relations toevoegen op de bestaande modellen Stable, User en Contract
    (invoices Invoice[]).
  • Eén Prisma-migratie die schoon draait op de bestaande database, zonder bestaande
    data te raken (alle nieuwe tabellen/kolommen; geen wijziging aan bestaande modellen
    behalve het toevoegen van de back-relation-velden).

Buiten scope (latere Fact-stories):

  • Autorisatie/inzage-regels op facturen (Fact 02).
  • Concept-factuur opstellen met handmatige regels, UI/actions (Fact 03).
  • Regels voorvullen uit het contract incl. btw-berekening (Fact 04).
  • Factuur-PDF, huisstijl, opvolgende factuurnummering, btw-overzicht (Fact 05).
  • Betaalwijze & SEPA-incassomachtiging (Fact 06).
  • Betaalstatus-overgangen, verzenden, herinneringen, facturatie-overzicht (Fact 07).
  • Wijzigingen aan OwnerBusinessProfile (bestaat al; wordt later uitgelezen).

Acceptatiecriteria

  • Het Prisma-model Invoice bestaat met alle in scope genoemde velden, relaties
    (Stable, User, Contract), onDelete-gedrag en indexen.
  • Het Prisma-model InvoiceLine bestaat met alle in scope genoemde velden, de
    Invoice-relatie (onDelete: Cascade) en de index op invoiceId.
  • De enums InvoiceStatus (CONCEPT/VERZONDEN/BETAALD/VERVALLEN/GEANNULEERD) en
    VatRate (0/9/21%) bestaan; Invoice.status heeft default CONCEPT.
  • Alle geldbedragen (subtotal, vatAmount, total, unitPrice, lineTotal,
    quantity) zijn Decimal @db.Decimal(10, 2) — geen Float/Int voor bedragen.
  • Het btw-tarief staat per InvoiceLine (vatRate), niet alleen op
    factuurniveau.
  • Invoice.invoiceNumber is nullable én @unique.
  • Op Stable, User en Contract is een invoices Invoice[] back-relation
    toegevoegd.
  • npx prisma migrate dev genereert één nieuwe migratie die schoon draait;
    npx prisma generate slaagt; bestaande data blijft ongemoeid (alleen nieuwe
    tabellen/relatievelden).
  • Geen UI, actions, queries of bedrag-/nummerlogica toegevoegd in deze story.

Technische notities

  • Volg de bestaande schema-conventies: UUID-PK's (@default(uuid()) @db.Uuid),
    enkelvoudige PascalCase-modelnamen, Nederlandse enum-waarden waar dat al de stijl is
    (ContractStatus, LeaseStatus), @db.Decimal(10, 2) voor bedragen (zie
    LeaseListing.pricePerMonth).
  • Schemawijzigingen mogen zonder vooraf overleg (zie project memory); houd het strikt
    binnen deze fundament-scope.
  • Prisma CLI draait via npx prisma in C:\Claude\velaro en leest .env.
  • vatAmount/total als opgeslagen kolommen (gedenormaliseerd) zijn bewust: zo legt
    een factuur het bedrag op uitgiftemoment vast. De daadwerkelijke berekening/optelling
    hoort in een latere story (app-laag), niet in deze migratie.

Open vragen

  • VatRate als enum vs. percentage-kolom. Voorgesteld is een enum (NUL/LAAG/
    HOOG). Alternatief is een Int-kolom met het percentage (0/9/21), wat flexibeler is
    als ooit een tarief wijzigt. De story is uitvoerbaar met de voorgestelde enum; de
    bouwer mag bij sterke voorkeur de percentage-variant kiezen mits 0/9/21 wordt
    afgedwongen (CHECK/validatie). Dit is een implementatiedetail, geen blokkade.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions