feat(billing): introduce billing domain and service infrastructure

- **Billing Domain:**
  - Added Kotlin Multiplatform project with domain models (`TeilnehmerKonto`, `Buchung`, `BuchungsTyp`) to represent billing entities.
  - Defined serialization strategies using `InstantSerializer`.

- **Service Implementation:**
  - Introduced `BillingServiceApplication` as the main entry point for the billing service.
  - Developed `TeilnehmerKontoService` for account management and transactions.

- **Persistence Layer:**
  - Implemented Exposed repositories (`ExposedTeilnehmerKontoRepository`, `ExposedBillingRepositories`) for database interaction.
  - Added table definitions (`TeilnehmerKontoTable`, `BuchungTable`) with indexes for efficient querying.

- **Build Configuration:**
  - Setup Gradle build files for billing domain and service modules with dependencies for Kotlin, Serialization, Spring Boot, and Exposed.

- **Test Additions:**
  - Extended ZNS importer tests with new scenarios for qualification parsing
This commit is contained in:
2026-04-10 12:18:00 +02:00
parent bab95d14f4
commit 21f3a57e6e
12 changed files with 1237 additions and 5 deletions
@@ -8,15 +8,20 @@ servers:
- url: http://localhost:8091
description: Lokaler Entwicklungs-Server
paths:
/reiter/search:
/reiter:
get:
summary: Sucht Reiter
summary: Alle Reiter abrufen (paginiert)
parameters:
- name: q
- name: limit
in: query
required: true
schema:
type: string
type: integer
default: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: Liste von Reitern
@@ -26,6 +31,450 @@ paths:
type: array
items:
$ref: '#/components/schemas/Reiter'
post:
summary: Neuen Reiter erstellen
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReiterCreateRequest'
responses:
'201':
description: Reiter erstellt
content:
application/json:
schema:
$ref: '#/components/schemas/Reiter'
/reiter/{id}:
get:
summary: Reiter nach ID abrufen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Reiter Details
content:
application/json:
schema:
$ref: '#/components/schemas/Reiter'
'404':
description: Nicht gefunden
put:
summary: Reiter aktualisieren
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReiterUpdateRequest'
responses:
'200':
description: Reiter aktualisiert
content:
application/json:
schema:
$ref: '#/components/schemas/Reiter'
'404':
description: Nicht gefunden
delete:
summary: Reiter löschen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Erfolgreich gelöscht
'404':
description: Nicht gefunden
/reiter/search:
get:
summary: Sucht Reiter nach Satznummer
parameters:
- name: q
in: query
required: true
schema:
type: string
responses:
'200':
description: Liste von Reitern (Satznummer Match)
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Reiter'
/reiter/satznummer/{nr}:
get:
summary: Reiter nach Satznummer suchen
parameters:
- name: nr
in: path
required: true
schema:
type: string
responses:
'200':
description: Reiter gefunden
content:
application/json:
schema:
$ref: '#/components/schemas/Reiter'
'404':
description: Nicht gefunden
/horse:
get:
summary: Alle Pferde abrufen (paginiert)
parameters:
- name: jahrgang
in: query
schema:
type: integer
- name: limit
in: query
schema:
type: integer
default: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: Liste von Pferden
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Horse'
post:
summary: Neues Pferd erstellen
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/HorseCreateRequest'
responses:
'201':
description: Pferd erstellt
content:
application/json:
schema:
$ref: '#/components/schemas/Horse'
/horse/{id}:
get:
summary: Pferd nach ID abrufen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Pferd Details
content:
application/json:
schema:
$ref: '#/components/schemas/Horse'
'404':
description: Nicht gefunden
put:
summary: Pferd aktualisieren
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/HorseUpdateRequest'
responses:
'200':
description: Pferd aktualisiert
content:
application/json:
schema:
$ref: '#/components/schemas/Horse'
delete:
summary: Pferd löschen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Gelöscht
/horse/search:
get:
summary: Sucht Pferde nach Lebensnummer
parameters:
- name: q
in: query
required: true
schema:
type: string
responses:
'200':
description: Liste von Pferden
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Horse'
/verein:
get:
summary: Alle Vereine abrufen (paginiert)
parameters:
- name: limit
in: query
schema:
type: integer
default: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: Liste von Vereinen
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Verein'
post:
summary: Neuen Verein erstellen
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VereinCreateRequest'
responses:
'201':
description: Verein erstellt
content:
application/json:
schema:
$ref: '#/components/schemas/Verein'
/verein/{id}:
get:
summary: Verein nach ID abrufen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Verein Details
content:
application/json:
schema:
$ref: '#/components/schemas/Verein'
'404':
description: Nicht gefunden
put:
summary: Verein aktualisieren
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VereinUpdateRequest'
responses:
'200':
description: Verein aktualisiert
content:
application/json:
schema:
$ref: '#/components/schemas/Verein'
delete:
summary: Verein löschen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Gelöscht
/funktionaer:
get:
summary: Alle Funktionäre abrufen (paginiert)
parameters:
- name: limit
in: query
schema:
type: integer
default: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: Liste von Funktionären
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Funktionaer'
post:
summary: Neuen Funktionär erstellen
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FunktionaerCreateRequest'
responses:
'201':
description: Funktionär erstellt
content:
application/json:
schema:
$ref: '#/components/schemas/Funktionaer'
/funktionaer/{id}:
get:
summary: Funktionär nach ID abrufen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Funktionär Details
content:
application/json:
schema:
$ref: '#/components/schemas/Funktionaer'
'404':
description: Nicht gefunden
put:
summary: Funktionär aktualisieren
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FunktionaerUpdateRequest'
responses:
'200':
description: Funktionär aktualisiert
content:
application/json:
schema:
$ref: '#/components/schemas/Funktionaer'
delete:
summary: Funktionär löschen
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Gelöscht
/funktionaer/search:
get:
summary: Sucht Funktionäre nach SatzNummer
parameters:
- name: q
in: query
required: true
schema:
type: integer
responses:
'200':
description: Liste von Funktionären
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Funktionaer'
/funktionaer/satz/{satzId}/{satzNummer}:
get:
summary: Funktionär nach Satz-ID und Nummer suchen
parameters:
- name: satzId
in: path
required: true
schema:
type: string
- name: satzNummer
in: path
required: true
schema:
type: integer
responses:
'200':
description: Funktionär gefunden
content:
application/json:
schema:
$ref: '#/components/schemas/Funktionaer'
'404':
description: Nicht gefunden
/rules/turnierklassen:
get:
summary: Alle Turnierklassen abrufen
@@ -58,9 +507,327 @@ components:
reiterId:
type: string
format: uuid
satznummer:
type: string
nachname:
type: string
vorname:
type: string
geburtsdatum:
type: string
format: date
bundeslandNummer:
type: integer
vereinsName:
type: string
nation:
type: string
reiterLizenz:
type: string
startkarte:
type: string
fahrLizenz:
type: string
mitgliedsNummer:
type: integer
telefonNummer:
type: string
lastPayYear:
type: integer
feiId:
type: string
lizenzKlasse:
type: string
istAktiv:
type: boolean
updatedAt:
type: string
format: date-time
ReiterCreateRequest:
type: object
required:
- satznummer
- nachname
- vorname
properties:
satznummer:
type: string
nachname:
type: string
vorname:
type: string
geburtsdatum:
type: string
format: date
bundeslandNummer:
type: integer
vereinsName:
type: string
nation:
type: string
reiterLizenz:
type: string
startkarte:
type: string
fahrLizenz:
type: string
mitgliedsNummer:
type: integer
telefonNummer:
type: string
lastPayYear:
type: integer
feiId:
type: string
lizenzKlasse:
type: string
istAktiv:
type: boolean
ReiterUpdateRequest:
type: object
properties:
nachname:
type: string
vorname:
type: string
geburtsdatum:
type: string
format: date
bundeslandNummer:
type: integer
vereinsName:
type: string
nation:
type: string
reiterLizenz:
type: string
startkarte:
type: string
fahrLizenz:
type: string
mitgliedsNummer:
type: integer
telefonNummer:
type: string
lastPayYear:
type: integer
feiId:
type: string
lizenzKlasse:
type: string
istAktiv:
type: boolean
Horse:
type: object
properties:
pferdId:
type: string
format: uuid
kopfnummer:
type: string
pferdeName:
type: string
lebensnummer:
type: string
geschlecht:
type: string
geburtsjahr:
type: integer
farbe:
type: string
satznummer:
type: string
istAktiv:
type: boolean
updatedAt:
type: string
format: date-time
HorseCreateRequest:
type: object
required:
- pferdeName
- geschlecht
properties:
kopfnummer:
type: string
pferdeName:
type: string
lebensnummer:
type: string
geschlecht:
type: string
geburtsjahr:
type: integer
farbe:
type: string
satznummer:
type: string
istAktiv:
type: boolean
HorseUpdateRequest:
type: object
properties:
kopfnummer:
type: string
pferdeName:
type: string
lebensnummer:
type: string
geschlecht:
type: string
geburtsjahr:
type: integer
farbe:
type: string
istAktiv:
type: boolean
Verein:
type: object
properties:
vereinId:
type: string
format: uuid
vereinsNummer:
type: string
name:
type: string
bundesland:
type: string
ort:
type: string
plz:
type: string
strasse:
type: string
email:
type: string
telefon:
type: string
website:
type: string
istVeranstalter:
type: boolean
istAktiv:
type: boolean
imageUrl:
type: string
bemerkungen:
type: string
updatedAt:
type: string
format: date-time
VereinCreateRequest:
type: object
required:
- vereinsNummer
- name
properties:
vereinsNummer:
type: string
name:
type: string
bundesland:
type: string
ort:
type: string
plz:
type: string
strasse:
type: string
email:
type: string
telefon:
type: string
website:
type: string
istVeranstalter:
type: boolean
istAktiv:
type: boolean
imageUrl:
type: string
bemerkungen:
type: string
VereinUpdateRequest:
type: object
properties:
name:
type: string
bundesland:
type: string
ort:
type: string
plz:
type: string
strasse:
type: string
email:
type: string
telefon:
type: string
website:
type: string
istVeranstalter:
type: boolean
istAktiv:
type: boolean
imageUrl:
type: string
bemerkungen:
type: string
Funktionaer:
type: object
properties:
funktionaerId:
type: string
format: uuid
satzId:
type: string
satzNummer:
type: integer
name:
type: string
qualifikationen:
type: array
items:
type: string
istAktiv:
type: boolean
bemerkungen:
type: string
updatedAt:
type: string
format: date-time
FunktionaerCreateRequest:
type: object
required:
- satzId
- satzNummer
properties:
satzId:
type: string
satzNummer:
type: integer
name:
type: string
qualifikationen:
type: array
items:
type: string
istAktiv:
type: boolean
bemerkungen:
type: string
FunktionaerUpdateRequest:
type: object
properties:
name:
type: string
qualifikationen:
type: array
items:
type: string
istAktiv:
type: boolean
bemerkungen:
type: string