more club funtions, fix for team edit
This commit is contained in:
@@ -299,6 +299,7 @@ export const CLUB_DATA_MODELS = {
|
||||
'invoice_number',
|
||||
'external_reference',
|
||||
'party_id',
|
||||
'account_id',
|
||||
'issued_on',
|
||||
'due_on',
|
||||
'paid_on',
|
||||
|
||||
@@ -42,6 +42,7 @@ const ClubHistoryView = () => import('./views/ClubHistoryView.vue');
|
||||
const ClubStatisticsView = () => import('./views/ClubStatisticsView.vue');
|
||||
const ClubArchiveView = () => import('./views/ClubArchiveView.vue');
|
||||
const ClubAccountsView = () => import('./views/ClubAccountsView.vue');
|
||||
const ClubInvoicesView = () => import('./views/ClubInvoicesView.vue');
|
||||
const ClubConceptModuleView = () => import('./views/ClubConceptModuleView.vue');
|
||||
const Impressum = () => import('./views/Impressum.vue');
|
||||
const Datenschutz = () => import('./views/Datenschutz.vue');
|
||||
@@ -140,6 +141,7 @@ const conceptRoutes = CLUB_CONCEPT_ROUTES
|
||||
.filter((route) => route.path !== '/club-statistics')
|
||||
.filter((route) => route.path !== '/club-archive')
|
||||
.filter((route) => route.path !== '/club-accounts')
|
||||
.filter((route) => route.path !== '/club-invoices')
|
||||
.map((route) => ({
|
||||
path: route.path,
|
||||
name: route.name,
|
||||
@@ -189,6 +191,7 @@ const routes = [
|
||||
{ path: '/club-statistics', name: 'club-statistics', component: ClubStatisticsView, meta: withMeta({ products: clubOnly, permission: ['statistics', 'read'] }) },
|
||||
{ path: '/club-archive', name: 'club-archive', component: ClubArchiveView, meta: withMeta({ products: clubOnly, permission: ['settings', 'read'] }) },
|
||||
{ path: '/club-accounts', name: 'club-accounts', component: ClubAccountsView, meta: withMeta({ products: clubOnly, permission: ['members', 'read'] }) },
|
||||
{ path: '/club-invoices', name: 'club-invoices', component: ClubInvoicesView, meta: withMeta({ products: clubOnly, permission: ['members', 'read'] }) },
|
||||
...conceptRoutes,
|
||||
{ path: '/impressum', name: 'impressum', component: Impressum, meta: withMeta({ public: true, products: allProducts }) },
|
||||
{ path: '/datenschutz', name: 'datenschutz', component: Datenschutz, meta: withMeta({ public: true, products: allProducts }) },
|
||||
|
||||
1061
frontend/src/views/ClubInvoicesView.vue
Normal file
1061
frontend/src/views/ClubInvoicesView.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -124,6 +124,30 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="currentClub && !loading" class="card">
|
||||
<h2>Rechnungsnummern</h2>
|
||||
<p class="hint">Automatische Nummernvergabe für Ausgangs- und Eingangsrechnungen.</p>
|
||||
<div class="field-grid">
|
||||
<div class="field-group">
|
||||
<label>Präfix Ausgangsrechnungen</label>
|
||||
<input v-model="outgoingInvoicePrefix" class="text-input" maxlength="24" placeholder="RE" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>Nächste Nummer Ausgang</label>
|
||||
<input v-model.number="outgoingInvoiceNextNumber" class="text-input" type="number" min="1" step="1" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>Präfix Eingangsrechnungen</label>
|
||||
<input v-model="incomingInvoicePrefix" class="text-input" maxlength="24" placeholder="EI" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>Nächste Nummer Eingang</label>
|
||||
<input v-model.number="incomingInvoiceNextNumber" class="text-input" type="number" min="1" step="1" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">Beispiel heute: {{ invoiceNumberPreview('outgoing') }} / {{ invoiceNumberPreview('incoming') }}</p>
|
||||
</section>
|
||||
|
||||
<section v-if="currentClub && !loading" class="card actions-card">
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" @click="save">{{ $t('clubSettings.save') }}</button>
|
||||
@@ -249,6 +273,10 @@ export default {
|
||||
myTischtennisFedNickname: '',
|
||||
autoFetchRankings: false,
|
||||
memberDataQualityRequirements: defaultMemberDataQualityRequirements(),
|
||||
outgoingInvoicePrefix: 'RE',
|
||||
outgoingInvoiceNextNumber: 1,
|
||||
incomingInvoicePrefix: 'EI',
|
||||
incomingInvoiceNextNumber: 1,
|
||||
saved: false,
|
||||
loading: false,
|
||||
loadError: null,
|
||||
@@ -315,6 +343,10 @@ export default {
|
||||
this.myTischtennisFedNickname = '';
|
||||
this.autoFetchRankings = false;
|
||||
this.memberDataQualityRequirements = defaultMemberDataQualityRequirements();
|
||||
this.outgoingInvoicePrefix = 'RE';
|
||||
this.outgoingInvoiceNextNumber = 1;
|
||||
this.incomingInvoicePrefix = 'EI';
|
||||
this.incomingInvoiceNextNumber = 1;
|
||||
this.loadError = null;
|
||||
return;
|
||||
}
|
||||
@@ -330,6 +362,10 @@ export default {
|
||||
this.myTischtennisFedNickname = club?.myTischtennisFedNickname ?? '';
|
||||
this.autoFetchRankings = !!club?.autoFetchRankings;
|
||||
this.memberDataQualityRequirements = this.normalizeMemberDataQualityRequirements(club?.memberDataQualityRequirements);
|
||||
this.outgoingInvoicePrefix = club?.outgoingInvoicePrefix ?? 'RE';
|
||||
this.outgoingInvoiceNextNumber = Number(club?.outgoingInvoiceNextNumber ?? 1) || 1;
|
||||
this.incomingInvoicePrefix = club?.incomingInvoicePrefix ?? 'EI';
|
||||
this.incomingInvoiceNextNumber = Number(club?.incomingInvoiceNextNumber ?? 1) || 1;
|
||||
} catch (e) {
|
||||
this.loadError = this.$t('clubSettings.loadFailed');
|
||||
this.greeting = '';
|
||||
@@ -339,6 +375,10 @@ export default {
|
||||
this.myTischtennisFedNickname = '';
|
||||
this.autoFetchRankings = false;
|
||||
this.memberDataQualityRequirements = defaultMemberDataQualityRequirements();
|
||||
this.outgoingInvoicePrefix = 'RE';
|
||||
this.outgoingInvoiceNextNumber = 1;
|
||||
this.incomingInvoicePrefix = 'EI';
|
||||
this.incomingInvoiceNextNumber = 1;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -363,6 +403,26 @@ export default {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
normalizeInvoicePrefix(value, fallback) {
|
||||
const normalized = String(value || fallback || '').trim().toUpperCase().slice(0, 24);
|
||||
return normalized || fallback;
|
||||
},
|
||||
normalizeInvoiceNextNumber(value) {
|
||||
const numeric = Number.parseInt(value, 10);
|
||||
return Number.isInteger(numeric) && numeric > 0 ? numeric : 1;
|
||||
},
|
||||
invoiceNumberPreview(direction) {
|
||||
const year = new Date().getFullYear();
|
||||
const isIncoming = direction === 'incoming';
|
||||
const prefix = isIncoming
|
||||
? this.normalizeInvoicePrefix(this.incomingInvoicePrefix, 'EI')
|
||||
: this.normalizeInvoicePrefix(this.outgoingInvoicePrefix, 'RE');
|
||||
const nextNumber = isIncoming
|
||||
? this.normalizeInvoiceNextNumber(this.incomingInvoiceNextNumber)
|
||||
: this.normalizeInvoiceNextNumber(this.outgoingInvoiceNextNumber);
|
||||
const padded = String(nextNumber).padStart(4, '0');
|
||||
return `${prefix}-${year}-${padded}`;
|
||||
},
|
||||
emptyVenueForm() {
|
||||
return { id: null, name: '', address: '', zip: '', city: '' };
|
||||
},
|
||||
@@ -444,6 +504,10 @@ export default {
|
||||
myTischtennisFedNickname: this.myTischtennisFedNickname || null,
|
||||
autoFetchRankings: this.autoFetchRankings,
|
||||
memberDataQualityRequirements: this.normalizeMemberDataQualityRequirements(this.memberDataQualityRequirements),
|
||||
outgoingInvoicePrefix: this.normalizeInvoicePrefix(this.outgoingInvoicePrefix, 'RE'),
|
||||
outgoingInvoiceNextNumber: this.normalizeInvoiceNextNumber(this.outgoingInvoiceNextNumber),
|
||||
incomingInvoicePrefix: this.normalizeInvoicePrefix(this.incomingInvoicePrefix, 'EI'),
|
||||
incomingInvoiceNextNumber: this.normalizeInvoiceNextNumber(this.incomingInvoiceNextNumber),
|
||||
});
|
||||
this.saved = true;
|
||||
setTimeout(() => (this.saved = false), 1500);
|
||||
|
||||
Reference in New Issue
Block a user