/** * Trustee API * * API-Funktionen für das Trustee-Feature. * Alle Endpunkte erfordern eine instanceId für den Feature-Instanz-Kontext. * * URL-Struktur: /api/trustee/{instanceId}/{entity} */ import { ApiRequestOptions } from '../hooks/useApi'; // ============================================================================ // TYPES & INTERFACES // ============================================================================ export interface TrusteeOrganisation { id: string; label: string; enabled: boolean; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface TrusteeRole { id: string; desc: string; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface TrusteeAccess { id: string; organisationId: string; roleId: string; userId: string; contractId?: string | null; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface TrusteeContract { id: string; organisationId: string; label: string; enabled: boolean; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface TrusteeDocument { id: string; organisationId: string; contractId: string; documentName: string; documentMimeType: string; documentData?: any; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface TrusteePosition { id: string; documentId?: string; valuta?: string; transactionDateTime?: number; company: string; desc: string; tags: string; bookingCurrency: string; bookingAmount: number; originalCurrency: string; originalAmount: number; vatPercentage: number; vatAmount: number; debitAccountNumber?: string; creditAccountNumber?: string; taxCode?: string; costCenter?: string; bookingReference?: string; mandateId?: string; _createdAt?: number; _modifiedAt?: number; _createdBy?: string; _modifiedBy?: string; [key: string]: any; } export interface AccountingConnectorInfo { connectorType: string; label: Record; configFields: Array<{ key: string; label: Record; fieldType: string; secret: boolean; required: boolean; placeholder?: string; }>; } export interface AccountingConfig { configured: boolean; id?: string; connectorType?: string; displayLabel?: string; isActive?: boolean; lastSyncAt?: number; lastSyncStatus?: string; /** Error message when lastSyncStatus is "error". */ lastSyncErrorMessage?: string; /** Masked config for form prefill: secret fields are "***", others have saved values. */ configMasked?: Record; } export interface AccountingSyncStatus { id: string; positionId: string; connectorType: string; externalId?: string; syncStatus: string; syncedAt?: number; errorMessage?: string; } export interface PaginationParams { page?: number; pageSize?: number; sort?: Array<{ field: string; direction: 'asc' | 'desc' }>; filters?: Record; search?: string; } export interface PaginatedResponse { items: T[]; pagination?: { currentPage: number; pageSize: number; totalItems: number; totalPages: number; }; } export type ApiRequestFunction = (options: ApiRequestOptions) => Promise; // ============================================================================ // HELPER FUNCTIONS // ============================================================================ function _buildPaginationParams(params?: PaginationParams): Record { const requestParams: any = {}; if (params) { const paginationObj: any = {}; if (params.page !== undefined) paginationObj.page = params.page; if (params.pageSize !== undefined) paginationObj.pageSize = params.pageSize; if (params.sort) paginationObj.sort = params.sort; if (params.filters) paginationObj.filters = params.filters; if (params.search) paginationObj.search = params.search; if (Object.keys(paginationObj).length > 0) { requestParams.pagination = JSON.stringify(paginationObj); } } return requestParams; } /** * Erstellt die Basis-URL für Trustee-Endpunkte */ function _getTrusteeBaseUrl(instanceId: string): string { return `/api/trustee/${instanceId}`; } // ============================================================================ // ORGANISATION API // ============================================================================ export async function fetchOrganisations( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteeOrganisation[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/organisations`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchOrganisationById( request: ApiRequestFunction, instanceId: string, orgId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/organisations/${orgId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching organisation by ID:', error); return null; } } export async function createOrganisation( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/organisations`, method: 'post', data }); } export async function updateOrganisation( request: ApiRequestFunction, instanceId: string, orgId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/organisations/${orgId}`, method: 'put', data }); } export async function deleteOrganisation( request: ApiRequestFunction, instanceId: string, orgId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/organisations/${orgId}`, method: 'delete' }); } // ============================================================================ // ROLE API // ============================================================================ export async function fetchRoles( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteeRole[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/roles`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchRoleById( request: ApiRequestFunction, instanceId: string, roleId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/roles/${roleId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching role by ID:', error); return null; } } export async function createRole( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/roles`, method: 'post', data }); } export async function updateRole( request: ApiRequestFunction, instanceId: string, roleId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/roles/${roleId}`, method: 'put', data }); } export async function deleteRole( request: ApiRequestFunction, instanceId: string, roleId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/roles/${roleId}`, method: 'delete' }); } // ============================================================================ // ACCESS API // ============================================================================ export async function fetchAccess( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteeAccess[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchAccessById( request: ApiRequestFunction, instanceId: string, accessId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access/${accessId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching access by ID:', error); return null; } } export async function fetchAccessByOrganisation( request: ApiRequestFunction, instanceId: string, orgId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access/organisation/${orgId}`, method: 'get' }); } export async function fetchAccessByUser( request: ApiRequestFunction, instanceId: string, userId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access/user/${userId}`, method: 'get' }); } export async function createAccess( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access`, method: 'post', data }); } export async function updateAccess( request: ApiRequestFunction, instanceId: string, accessId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access/${accessId}`, method: 'put', data }); } export async function deleteAccess( request: ApiRequestFunction, instanceId: string, accessId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/access/${accessId}`, method: 'delete' }); } // ============================================================================ // CONTRACT API // ============================================================================ export async function fetchContracts( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteeContract[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchContractById( request: ApiRequestFunction, instanceId: string, contractId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts/${contractId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching contract by ID:', error); return null; } } export async function fetchContractsByOrganisation( request: ApiRequestFunction, instanceId: string, orgId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts/organisation/${orgId}`, method: 'get' }); } export async function createContract( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts`, method: 'post', data }); } export async function updateContract( request: ApiRequestFunction, instanceId: string, contractId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts/${contractId}`, method: 'put', data }); } export async function deleteContract( request: ApiRequestFunction, instanceId: string, contractId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/contracts/${contractId}`, method: 'delete' }); } // ============================================================================ // DOCUMENT API // ============================================================================ export async function fetchDocuments( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteeDocument[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchDocumentById( request: ApiRequestFunction, instanceId: string, documentId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents/${documentId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching document by ID:', error); return null; } } export async function fetchDocumentsByContract( request: ApiRequestFunction, instanceId: string, contractId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents/contract/${contractId}`, method: 'get' }); } export async function createDocument( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { // If documentData is a File, convert to base64 let processedData = { ...data }; if (data.documentData instanceof File) { const file = data.documentData as File; const arrayBuffer = await file.arrayBuffer(); const base64 = btoa( new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), '') ); processedData.documentData = base64 as any; // Auto-set MIME type from file if not provided if (!processedData.documentMimeType && file.type) { processedData.documentMimeType = file.type; } // Auto-set name from file if not provided if (!processedData.documentName && file.name) { processedData.documentName = file.name; } } return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents`, method: 'post', data: processedData }); } export async function updateDocument( request: ApiRequestFunction, instanceId: string, documentId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents/${documentId}`, method: 'put', data }); } export async function deleteDocument( request: ApiRequestFunction, instanceId: string, documentId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/documents/${documentId}`, method: 'delete' }); } // ============================================================================ // POSITION API // ============================================================================ export async function fetchPositions( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteePosition[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchPositionById( request: ApiRequestFunction, instanceId: string, positionId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions/${positionId}`, method: 'get' }); } catch (error: any) { console.error('Error fetching position by ID:', error); return null; } } export async function fetchPositionsByContract( request: ApiRequestFunction, instanceId: string, contractId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions/contract/${contractId}`, method: 'get' }); } export async function fetchPositionsByOrganisation( request: ApiRequestFunction, instanceId: string, orgId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions/organisation/${orgId}`, method: 'get' }); } export async function createPosition( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions`, method: 'post', data }); } export async function updatePosition( request: ApiRequestFunction, instanceId: string, positionId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions/${positionId}`, method: 'put', data }); } export async function deletePosition( request: ApiRequestFunction, instanceId: string, positionId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/positions/${positionId}`, method: 'delete' }); } // ============================================================================ // POSITION-DOCUMENT LINK API // ============================================================================ export interface TrusteePositionDocument { id: string; positionId: string; documentId: string; mandateId?: string; featureInstanceId?: string; _createdAt?: number; _modifiedAt?: number; [key: string]: any; } export async function fetchPositionDocuments( request: ApiRequestFunction, instanceId: string, params?: PaginationParams ): Promise | TrusteePositionDocument[]> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/position-documents`, method: 'get', params: _buildPaginationParams(params) }); } export async function fetchPositionDocumentById( request: ApiRequestFunction, instanceId: string, linkId: string ): Promise { try { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/position-documents/${linkId}`, method: 'get' }); } catch { return null; } } export async function createPositionDocument( request: ApiRequestFunction, instanceId: string, data: Partial ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/position-documents`, method: 'post', data }); } export async function deletePositionDocument( request: ApiRequestFunction, instanceId: string, linkId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/position-documents/${linkId}`, method: 'delete' }); } // ============================================================================ // ACCOUNTING API // ============================================================================ export async function fetchAccountingConnectors( request: ApiRequestFunction, instanceId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/connectors`, method: 'get' }); } export async function fetchAccountingConfig( request: ApiRequestFunction, instanceId: string ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/config`, method: 'get' }); } export async function saveAccountingConfig( request: ApiRequestFunction, instanceId: string, data: { connectorType: string; displayLabel: string; config: Record } ): Promise { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/config`, method: 'post', data }); } export async function deleteAccountingConfig( request: ApiRequestFunction, instanceId: string ): Promise { await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/config`, method: 'delete' }); } export async function testAccountingConnection( request: ApiRequestFunction, instanceId: string ): Promise<{ success: boolean; errorMessage?: string }> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/test-connection`, method: 'post' }); } export async function fetchChartOfAccounts( request: ApiRequestFunction, instanceId: string ): Promise> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/chart-of-accounts`, method: 'get' }); } export async function syncPositionsToAccounting( request: ApiRequestFunction, instanceId: string, positionIds: string[] ): Promise<{ total: number; success: number; errors: number; results: any[] }> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/sync`, method: 'post', data: { positionIds } }); } export async function fetchSyncStatus( request: ApiRequestFunction, instanceId: string ): Promise<{ items: AccountingSyncStatus[] }> { return await request({ url: `${_getTrusteeBaseUrl(instanceId)}/accounting/sync-status`, method: 'get' }); }