Gutschriften Phase 2b: Vertrags-UI + Beleg-Upload + Nummernkreis-Seite
Frontend: - CreditNotesSection im Vertragsdetail (eigene Section wie Rechnungen): Liste + Anlegen/Bearbeiten (Modal). Geld/Sachwert mit passenden Feldern, USt-Block mit Live-Vorschau Netto/USt/Brutto, Datum, Ort, Unterschrift, 'Ware erhalten'. Loeschen mit Bestaetigung. - Ueberweisungsbeleg-Upload/-Anzeige/-Entfernen (nur Geld-Gutschriften). - Nummernkreis-Verwaltung als Settings-Seite mit Live-Vorschau der naechsten Nummer. - Frontend-Typen (CreditNote etc.) + creditNoteApi. Backend: - Ueberweisungsbeleg-Upload-Route /upload/credit-notes/:id/receipt (+ DELETE), Portal geblockt, Ownership ueber Vertrag. - fileDownload: Ownership-Aufloesung fuer credit-note-receipts + credit-notes (Vertrag) ergaenzt. Als Section statt Tab-Balken, weil die Vertragsansicht kartenbasiert ist (kein bestehender Tab-Balken); spaeter umbaubar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import type { ApiResponse, Customer, Contract, ContractTask, ContractTaskSubtask, ContractTaskStatus, SalesPlatform, CancellationPeriod, ContractDuration, ContractCategory, Provider, Tariff, User, Address, BankCard, IdentityDocument, Meter, MeterReading, Invoice, Role, PortalSettings, CustomerRepresentative, CustomerSummary, CustomerReferrals, ContractHistoryEntry, AuditLog, AuditSensitivity, AuditRetentionPolicy, CustomerConsent, ConsentType, ConsentStatus, DataDeletionRequest, DeletionRequestStatus, GDPRDashboardStats, RepresentativeAuthorization } from '../types';
|
||||
import type { ApiResponse, Customer, Contract, ContractTask, ContractTaskSubtask, ContractTaskStatus, SalesPlatform, CancellationPeriod, ContractDuration, ContractCategory, Provider, Tariff, User, Address, BankCard, IdentityDocument, Meter, MeterReading, Invoice, Role, PortalSettings, CustomerRepresentative, CustomerSummary, CustomerReferrals, CreditNote, CreditNoteDefaults, CreditNoteNumberRange, ContractHistoryEntry, AuditLog, AuditSensitivity, AuditRetentionPolicy, CustomerConsent, ConsentType, ConsentStatus, DataDeletionRequest, DeletionRequestStatus, GDPRDashboardStats, RepresentativeAuthorization } from '../types';
|
||||
|
||||
// ============================================================================
|
||||
// In-Memory-Token-Store
|
||||
@@ -263,6 +263,54 @@ export const referralApi = {
|
||||
},
|
||||
};
|
||||
|
||||
// Gutschriften (Subventionen am Vertrag)
|
||||
export const creditNoteApi = {
|
||||
listByContract: async (contractId: number) => {
|
||||
const res = await api.get<ApiResponse<CreditNote[]>>(`/contracts/${contractId}/credit-notes`);
|
||||
return res.data;
|
||||
},
|
||||
getDefaults: async (contractId: number) => {
|
||||
const res = await api.get<ApiResponse<CreditNoteDefaults>>(`/contracts/${contractId}/credit-notes/defaults`);
|
||||
return res.data;
|
||||
},
|
||||
create: async (contractId: number, payload: Record<string, unknown>) => {
|
||||
const res = await api.post<ApiResponse<CreditNote>>(`/contracts/${contractId}/credit-notes`, payload);
|
||||
return res.data;
|
||||
},
|
||||
get: async (id: number) => {
|
||||
const res = await api.get<ApiResponse<CreditNote>>(`/credit-notes/${id}`);
|
||||
return res.data;
|
||||
},
|
||||
update: async (id: number, payload: Record<string, unknown>) => {
|
||||
const res = await api.put<ApiResponse<CreditNote>>(`/credit-notes/${id}`, payload);
|
||||
return res.data;
|
||||
},
|
||||
remove: async (id: number) => {
|
||||
const res = await api.delete<ApiResponse<void>>(`/credit-notes/${id}`);
|
||||
return res.data;
|
||||
},
|
||||
getNumberRange: async () => {
|
||||
const res = await api.get<ApiResponse<CreditNoteNumberRange>>(`/credit-notes/number-range`);
|
||||
return res.data;
|
||||
},
|
||||
updateNumberRange: async (payload: Partial<CreditNoteNumberRange>) => {
|
||||
const res = await api.put<ApiResponse<CreditNoteNumberRange>>(`/credit-notes/number-range`, payload);
|
||||
return res.data;
|
||||
},
|
||||
uploadReceipt: async (id: number, file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('document', file);
|
||||
const res = await api.post<ApiResponse<{ receiptPath: string }>>(`/upload/credit-notes/${id}/receipt`, formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
});
|
||||
return res.data;
|
||||
},
|
||||
deleteReceipt: async (id: number) => {
|
||||
const res = await api.delete<ApiResponse<void>>(`/upload/credit-notes/${id}/receipt`);
|
||||
return res.data;
|
||||
},
|
||||
};
|
||||
|
||||
// Addresses
|
||||
export const addressApi = {
|
||||
getByCustomer: async (customerId: number) => {
|
||||
|
||||
Reference in New Issue
Block a user