197 lines
7.0 KiB
HTML
197 lines
7.0 KiB
HTML
<header class="page-header">
|
|
<div>
|
|
<p class="eyebrow">Finanzen</p>
|
|
<h1>Kasse</h1>
|
|
<p>Buchungen erfassen und den vollständigen Verlauf nachvollziehen.</p>
|
|
</div>
|
|
<div class="balance">
|
|
<span>Teamkasse</span>
|
|
<strong>{{ team()?.balance ?? 0 | currency: 'EUR' }}</strong>
|
|
</div>
|
|
</header>
|
|
|
|
<app-context-help
|
|
title="Buchungen verstehen"
|
|
[hints]="[
|
|
'Mitgliederbuchungen verändern persönliche Salden; Teambuchungen erfassen direkte Einnahmen oder Ausgaben der Kasse.',
|
|
'Bei mehreren Mitgliedern bestimmt die Verteilungsoption, ob der Gesamtbetrag geteilt oder je Person gebucht wird.',
|
|
]"
|
|
sectionId="bookings"
|
|
/>
|
|
|
|
@if (canBook()) {
|
|
<section class="booking-grid">
|
|
<mat-card data-testid="player-booking">
|
|
<mat-card-header>
|
|
<mat-icon mat-card-avatar>group</mat-icon>
|
|
<mat-card-title>Mitgliederbuchung</mat-card-title>
|
|
<mat-card-subtitle>Für eine oder mehrere Personen</mat-card-subtitle>
|
|
</mat-card-header>
|
|
<mat-card-content>
|
|
<form [formGroup]="playerForm" (ngSubmit)="submitPlayerBooking()">
|
|
@if (penalties().length > 0) {
|
|
<mat-form-field appearance="outline" class="wide">
|
|
<mat-label>Aus Strafenkatalog übernehmen (optional)</mat-label>
|
|
<mat-select (selectionChange)="onPenaltySelect($event.value)">
|
|
@for (penalty of penalties(); track penalty.id) {
|
|
<mat-option [value]="penalty.id"
|
|
>{{ penalty.description }} · {{ penalty.amount | currency: 'EUR' }}</mat-option
|
|
>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
}
|
|
<mat-form-field appearance="outline" class="wide">
|
|
<mat-label>Mitglieder</mat-label>
|
|
<mat-select formControlName="playerIds" multiple>
|
|
@for (player of activePlayers(); track player.id) {
|
|
<mat-option [value]="player.id"
|
|
>{{ player.firstName }} {{ player.lastName }}</mat-option
|
|
>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<div class="form-row">
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Betrag</mat-label>
|
|
<input
|
|
matInput
|
|
formControlName="amount"
|
|
type="number"
|
|
min="0.01"
|
|
max="10000"
|
|
step="0.01"
|
|
/>
|
|
<span matTextSuffix>€</span>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Art</mat-label>
|
|
<mat-select formControlName="type">
|
|
@for (type of playerTransactionTypes; track type.id) {
|
|
<mat-option [value]="type.id">{{ type.label }}</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Datum</mat-label>
|
|
<input matInput formControlName="date" type="date" />
|
|
</mat-form-field>
|
|
</div>
|
|
<mat-form-field appearance="outline" class="wide">
|
|
<mat-label>Notiz (optional)</mat-label>
|
|
<input matInput formControlName="note" />
|
|
</mat-form-field>
|
|
<mat-checkbox formControlName="total">Betrag gleichmäßig auf alle verteilen</mat-checkbox>
|
|
<button mat-flat-button type="submit" [disabled]="playerForm.invalid || saving()">
|
|
<mat-icon>add_card</mat-icon>
|
|
Buchen
|
|
</button>
|
|
</form>
|
|
</mat-card-content>
|
|
</mat-card>
|
|
|
|
<mat-card data-testid="team-booking">
|
|
<mat-card-header>
|
|
<mat-icon mat-card-avatar>account_balance</mat-icon>
|
|
<mat-card-title>Teambuchung</mat-card-title>
|
|
<mat-card-subtitle>Einnahme oder Ausgabe der Teamkasse</mat-card-subtitle>
|
|
</mat-card-header>
|
|
<mat-card-content>
|
|
<form [formGroup]="teamForm" (ngSubmit)="submitTeamBooking()">
|
|
<div class="form-row">
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Betrag</mat-label>
|
|
<input
|
|
matInput
|
|
formControlName="amount"
|
|
type="number"
|
|
min="0.01"
|
|
max="10000"
|
|
step="0.01"
|
|
/>
|
|
<span matTextSuffix>€</span>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Art</mat-label>
|
|
<mat-select formControlName="type">
|
|
@for (type of teamTransactionTypes; track type.id) {
|
|
<mat-option [value]="type.id">{{ type.label }}</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline">
|
|
<mat-label>Datum</mat-label>
|
|
<input matInput formControlName="date" type="date" />
|
|
</mat-form-field>
|
|
</div>
|
|
<mat-form-field appearance="outline" class="wide">
|
|
<mat-label>Notiz (optional)</mat-label>
|
|
<input matInput formControlName="note" />
|
|
</mat-form-field>
|
|
<button mat-flat-button type="submit" [disabled]="teamForm.invalid || saving()">
|
|
<mat-icon>add_card</mat-icon>
|
|
Teambuchung speichern
|
|
</button>
|
|
</form>
|
|
</mat-card-content>
|
|
</mat-card>
|
|
</section>
|
|
} @else {
|
|
<mat-card class="info-card">
|
|
<mat-icon>visibility</mat-icon>
|
|
<p>
|
|
Du kannst alle Buchungen sehen. Neue Buchungen sind Kassenwart und Teamleitung vorbehalten.
|
|
</p>
|
|
</mat-card>
|
|
}
|
|
|
|
<section class="activity-section">
|
|
<div class="section-heading">
|
|
<div>
|
|
<p class="eyebrow">Journal</p>
|
|
<h2>Alle Buchungen</h2>
|
|
</div>
|
|
@if (journalTotal(); as total) {
|
|
<span>{{ total }} Einträge</span>
|
|
}
|
|
</div>
|
|
|
|
<div class="journal-toolbar">
|
|
<mat-form-field appearance="outline" class="journal-search">
|
|
<mat-label>Suche</mat-label>
|
|
<input
|
|
matInput
|
|
placeholder="Name oder Notiz"
|
|
[value]="journalSearch()"
|
|
(input)="onJournalSearchInput($any($event.target).value)"
|
|
/>
|
|
<mat-icon matTextSuffix>search</mat-icon>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="journal-type">
|
|
<mat-label>Typ</mat-label>
|
|
<mat-select
|
|
[value]="journalTypeFilter()"
|
|
(selectionChange)="onJournalTypeChange($event.value)"
|
|
>
|
|
@for (option of journalTypeOptions; track option.value) {
|
|
<mat-option [value]="option.value">{{ option.label }}</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
</div>
|
|
|
|
<ag-grid-angular
|
|
class="journal-grid"
|
|
[theme]="gridTheme"
|
|
[columnDefs]="journalColumnDefs"
|
|
[rowModelType]="'infinite'"
|
|
[pagination]="true"
|
|
[paginationPageSize]="25"
|
|
[paginationPageSizeSelector]="[10, 25, 50, 100]"
|
|
[cacheBlockSize]="25"
|
|
[getRowId]="journalGetRowId"
|
|
[suppressCellFocus]="true"
|
|
(gridReady)="onJournalGridReady($event)"
|
|
/>
|
|
</section>
|