BREAKING CHANGE

API and persisted records use PowerOnModel system fields:
- sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy
Removed legacy JSON/DB field names:
- _createdAt, _createdBy, _modifiedAt, _modifiedBy
Frontend (frontend_nyla) and gateway call sites were updated accordingly.
Database:
- Bootstrap runs idempotent backfill (_migrateSystemFieldColumns) from old
  underscore columns and selected business duplicates into sys* where sys* IS NULL.
- Re-run app bootstrap against each PostgreSQL database after deploy.
- Optional: DROP INDEX IF EXISTS "idx_invitation_createdby" if an old index remains;
  new index: idx_invitation_syscreatedby on Invitation(sysCreatedBy).
Tests:
- RBAC integration tests aligned with current GROUP mandate filter and UserMandate-based
  UserConnection GROUP clause; buildRbacWhereClause(..., mandateId=...) must be passed
  explicitly (same as production request context).
This commit is contained in:
ValueOn AG 2026-03-28 18:13:18 +01:00
parent f5f6cad542
commit 77e7eba711
34 changed files with 89 additions and 89 deletions

View file

@ -258,7 +258,7 @@ export interface Automation2Task {
result?: Record<string, unknown>;
/** Workflow label (enriched by API) */
workflowLabel?: string;
/** Unix timestamp ms (from _createdAt) */
/** Unix timestamp ms (from sysCreatedAt) */
createdAt?: number;
/** Optional due date - configurable in future */
dueAt?: number;

View file

@ -18,9 +18,9 @@ export interface Automation {
nextExecution?: number;
executionLogs?: AutomationLog[];
allowedProviders?: string[];
_createdAt?: number;
sysCreatedAt?: number;
_updatedAt?: number;
_createdByUserName?: string;
sysCreatedByUserName?: string;
mandateName?: string;
featureInstanceName?: string;
[key: string]: any;
@ -48,9 +48,9 @@ export interface AutomationTemplate {
label: TextMultilingual;
overview?: TextMultilingual;
template: string; // JSON string with {{KEY:...}} placeholders
_createdAt?: number;
_createdBy?: string;
_createdByUserName?: string;
sysCreatedAt?: number;
sysCreatedBy?: string;
sysCreatedByUserName?: string;
}
// Workflow action definition from backend
@ -301,7 +301,7 @@ export async function fetchAutomationTemplateById(
*/
export async function createAutomationTemplateApi(
request: ApiRequestFunction,
templateData: Omit<AutomationTemplate, 'id' | '_createdAt' | '_createdBy'>
templateData: Omit<AutomationTemplate, 'id' | 'sysCreatedAt' | 'sysCreatedBy'>
): Promise<AutomationTemplate> {
return await request({
url: '/api/automation-templates',

View file

@ -9,7 +9,7 @@ export interface Prompt {
mandateId: string;
content: string;
name: string;
_createdBy?: string;
sysCreatedBy?: string;
_hideDelete?: boolean;
[key: string]: any; // Allow additional properties
}

View file

@ -23,8 +23,8 @@ export interface RealEstateProject {
featureInstanceId?: string;
perimeter?: any;
parzellen?: RealEstateParcel[];
_createdAt?: number;
_modifiedAt?: number;
sysCreatedAt?: number;
sysModifiedAt?: number;
[key: string]: any;
}
@ -38,8 +38,8 @@ export interface RealEstateParcel {
plz?: string;
perimeter?: any;
bauzone?: string;
_createdAt?: number;
_modifiedAt?: number;
sysCreatedAt?: number;
sysModifiedAt?: number;
[key: string]: any;
}

View file

@ -18,10 +18,10 @@ export interface TrusteeOrganisation {
label: string;
enabled: boolean;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -29,10 +29,10 @@ export interface TrusteeRole {
id: string;
desc: string;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -43,10 +43,10 @@ export interface TrusteeAccess {
userId: string;
contractId?: string | null;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -56,10 +56,10 @@ export interface TrusteeContract {
label: string;
enabled: boolean;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -71,10 +71,10 @@ export interface TrusteeDocument {
documentMimeType: string;
documentData?: any;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -98,10 +98,10 @@ export interface TrusteePosition {
costCenter?: string;
bookingReference?: string;
mandateId?: string;
_createdAt?: number;
_modifiedAt?: number;
_createdBy?: string;
_modifiedBy?: string;
sysCreatedAt?: number;
sysModifiedAt?: number;
sysCreatedBy?: string;
sysModifiedBy?: string;
[key: string]: any;
}
@ -696,8 +696,8 @@ export interface TrusteePositionDocument {
documentId: string;
mandateId?: string;
featureInstanceId?: string;
_createdAt?: number;
_modifiedAt?: number;
sysCreatedAt?: number;
sysModifiedAt?: number;
[key: string]: any;
}

View file

@ -165,7 +165,7 @@ export function useMandates() {
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -305,7 +305,7 @@ export function useMandates() {
return false;
}
// Filter out ID fields and other auto-generated fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -206,7 +206,7 @@ export function useRbacRoles() {
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'roleId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'roleId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -346,7 +346,7 @@ export function useRbacRoles() {
return false;
}
// Filter out ID fields and other auto-generated fields
const nonEditableFields = ['id', 'roleId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'roleId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -182,7 +182,7 @@ export function useRbacRules() {
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'ruleId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'ruleId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -322,7 +322,7 @@ export function useRbacRules() {
return false;
}
// Filter out ID fields and other auto-generated fields
const nonEditableFields = ['id', 'ruleId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'ruleId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -536,7 +536,7 @@ export function useAutomationTemplates() {
return await fetchAutomationTemplateById(request, templateId);
}, [request]);
const createTemplate = useCallback(async (data: Omit<AutomationTemplate, 'id' | '_createdAt' | '_createdBy'>) => {
const createTemplate = useCallback(async (data: Omit<AutomationTemplate, 'id' | 'sysCreatedAt' | 'sysCreatedBy'>) => {
return await createAutomationTemplateApi(request, data);
}, [request]);

View file

@ -83,11 +83,11 @@ export function useTablePermission(tableName: string) {
canDelete: hasAccess(permission.delete),
// Record-basierte Prüfungen
canReadRecord: (record: { _createdBy?: string }) =>
canReadRecord: (record: { sysCreatedBy?: string }) =>
canAccessRecord(permission.read, record, userId),
canUpdateRecord: (record: { _createdBy?: string }) =>
canUpdateRecord: (record: { sysCreatedBy?: string }) =>
canAccessRecord(permission.update, record, userId),
canDeleteRecord: (record: { _createdBy?: string }) =>
canDeleteRecord: (record: { sysCreatedBy?: string }) =>
canAccessRecord(permission.delete, record, userId),
};
}
@ -296,7 +296,7 @@ export function useInstancePermissions(): InstancePermissions | undefined {
*/
export function useCanEditRecord(
tableName: string,
record: { _createdBy?: string } | undefined,
record: { sysCreatedBy?: string } | undefined,
userId: string
): boolean {
const { update } = useTablePermission(tableName);
@ -311,7 +311,7 @@ export function useCanEditRecord(
*/
export function useCanDeleteRecord(
tableName: string,
record: { _createdBy?: string } | undefined,
record: { sysCreatedBy?: string } | undefined,
userId: string
): boolean {
const { delete: deleteLevel } = useTablePermission(tableName);
@ -329,7 +329,7 @@ interface PermissionGateProps {
table?: string;
view?: string;
action?: 'view' | 'read' | 'create' | 'update' | 'delete';
record?: { _createdBy?: string };
record?: { sysCreatedBy?: string };
children: React.ReactNode;
fallback?: React.ReactNode;
}

View file

@ -157,7 +157,7 @@ export function usePrompts() {
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -367,7 +367,7 @@ export function usePrompts() {
return false;
}
// Filter out ID fields and other auto-generated fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -530,7 +530,7 @@ export function usePromptOperations() {
try {
// Pass all provided fields (supports partial inline updates like isSystem toggle)
const { id, mandateId, _createdBy, _createdAt, _modifiedAt, _permissions, ...requestBody } = updateData;
const { id, mandateId, sysCreatedBy, sysCreatedAt, sysModifiedAt, _permissions, ...requestBody } = updateData;
const updatedPrompt = await updatePromptApi(request, promptId, requestBody as UpdatePromptData);

View file

@ -165,7 +165,7 @@ function _createRealEstateEntityHook<T extends { id: string }>(config: RealEstat
.filter(attr => {
if (attr.readonly === true || attr.editable === false) return false;
if (attr.name === 'id') return false;
const nonEditable = ['_createdBy', '_createdAt', '_modifiedBy', '_modifiedAt'];
const nonEditable = ['sysCreatedBy', 'sysCreatedAt', 'sysModifiedBy', 'sysModifiedAt'];
return !nonEditable.includes(attr.name);
})
.map(attr => {
@ -210,7 +210,7 @@ function _createRealEstateEntityHook<T extends { id: string }>(config: RealEstat
const generateCreateFieldsFromAttributes = useCallback(() => {
if (!attributes || attributes.length === 0) return [];
return attributes
.filter(attr => !['_createdBy', '_createdAt', '_modifiedBy', '_modifiedAt', 'mandateId', 'featureInstanceId'].includes(attr.name))
.filter(attr => !['sysCreatedBy', 'sysCreatedAt', 'sysModifiedBy', 'sysModifiedAt', 'mandateId', 'featureInstanceId'].includes(attr.name))
.map(attr => {
let fieldType: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'multiselect' | 'readonly' | 'number' = 'string';
let options: Array<{ value: string | number; label: string }> | undefined;

View file

@ -218,7 +218,7 @@ function _createTrusteeEntityHook<T extends { id: string }>(config: TrusteeEntit
if (attr.name === 'id') {
return false;
}
const nonEditableFields = ['_createdBy', '_createdAt', '_modifiedBy', '_modifiedAt'];
const nonEditableFields = ['sysCreatedBy', 'sysCreatedAt', 'sysModifiedBy', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -284,7 +284,7 @@ function _createTrusteeEntityHook<T extends { id: string }>(config: TrusteeEntit
return attributes
.filter(attr => {
const systemFields = ['_createdBy', '_createdAt', '_modifiedBy', '_modifiedAt', 'mandateId'];
const systemFields = ['sysCreatedBy', 'sysCreatedAt', 'sysModifiedBy', 'sysModifiedAt', 'mandateId'];
return !systemFields.includes(attr.name);
})
.map(attr => {

View file

@ -175,7 +175,7 @@ export function useTrusteeAccess() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['id', 'mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['id', 'mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -175,7 +175,7 @@ export function useTrusteeContracts() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['id', 'mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['id', 'mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -176,7 +176,7 @@ export function useTrusteeDocuments() {
return false;
}
// documentData is handled separately (binary upload)
const nonEditableFields = ['id', 'documentData', 'mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['id', 'documentData', 'mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -174,7 +174,7 @@ export function useTrusteeOrganisations() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -163,7 +163,7 @@ export function useTrusteePositionDocuments() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['id', 'mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['id', 'mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -178,7 +178,7 @@ export function useTrusteePositions() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['id', 'mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['id', 'mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -176,7 +176,7 @@ export function useTrusteeRoles() {
if (attr.readonly === true || attr.editable === false) {
return false;
}
const nonEditableFields = ['mandate', '_createdBy', '_modifiedBy', '_createdAt', '_modifiedAt'];
const nonEditableFields = ['mandate', 'sysCreatedBy', 'sysModifiedBy', 'sysCreatedAt', 'sysModifiedAt'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -412,7 +412,7 @@ export function useOrgUsers() {
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {
@ -560,7 +560,7 @@ export function useOrgUsers() {
return false;
}
// Filter out ID fields and other auto-generated fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete', 'authenticationAuthority'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete', 'authenticationAuthority'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -224,7 +224,7 @@ export function useUserWorkflows(options?: { instanceId?: string; featureCode?:
return false; // Don't show readonly fields in edit form
}
// Also filter out common non-editable fields
const nonEditableFields = ['id', 'mandateId', '_createdBy', '_hideDelete'];
const nonEditableFields = ['id', 'mandateId', 'sysCreatedBy', '_hideDelete'];
return !nonEditableFields.includes(attr.name);
})
.map(attr => {

View file

@ -204,7 +204,7 @@ export const ConnectionsPage: React.FC = () => {
// Form attributes for edit modal
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'userId', '_createdBy', '_createdAt', '_modifiedAt', 'connectedAt', 'lastChecked'];
const excludedFields = ['id', 'mandateId', 'userId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'connectedAt', 'lastChecked'];
return (attributes || [])
.filter(attr => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -142,7 +142,7 @@ export const FilesPage: React.FC = () => {
}));
cols.push({
key: '_createdBy',
key: 'sysCreatedBy',
label: 'Created By',
type: 'text' as any,
sortable: true,
@ -289,7 +289,7 @@ export const FilesPage: React.FC = () => {
}, [selectedFolderId, _tableRefetch]);
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'fileHash', '_createdBy', '_createdAt', '_modifiedAt', 'creationDate', 'source'];
const excludedFields = ['id', 'mandateId', 'fileHash', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'creationDate', 'source'];
return (attributes || []).filter(attr => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -53,7 +53,7 @@ export const PromptsPage: React.FC = () => {
// Generate columns from attributes - exclude ID fields from display
const columns = useMemo(() => {
// Fields to hide in table view
const hiddenColumns = ['id', 'mandateId', '_createdAt', '_modifiedAt', '_hideDelete', '_permissions'];
const hiddenColumns = ['id', 'mandateId', 'sysCreatedAt', 'sysModifiedAt', '_hideDelete', '_permissions'];
const cols = (attributes || [])
.filter(attr => !hiddenColumns.includes(attr.name))
@ -71,9 +71,9 @@ export const PromptsPage: React.FC = () => {
fkDisplayField: (attr as any).fkDisplayField,
}));
// Add _createdBy column with FK resolution to show username
// Add sysCreatedBy column with FK resolution to show username
cols.push({
key: '_createdBy',
key: 'sysCreatedBy',
label: 'Created By',
type: 'text' as any,
sortable: true,
@ -148,7 +148,7 @@ export const PromptsPage: React.FC = () => {
// Form attributes for create/edit modal
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'isSystem', '_createdBy', '_createdAt', '_modifiedAt', '_hideDelete', '_permissions'];
const excludedFields = ['id', 'mandateId', 'isSystem', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', '_hideDelete', '_permissions'];
return (attributes || [])
.filter(attr => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -110,9 +110,9 @@ export const AutomationDefinitionsView: React.FC = () => {
const columns = useMemo(() => {
const hiddenColumns = [
'id', 'mandateId', 'featureInstanceId', '_createdBy', '_createdAt', '_modifiedAt',
'id', 'mandateId', 'featureInstanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt',
'template', 'executionLogs', 'placeholders',
'_createdByUserName', 'mandateName', 'featureInstanceName',
'sysCreatedByUserName', 'mandateName', 'featureInstanceName',
];
const attrColumns = (attributes || [])
.filter(attr => !hiddenColumns.includes(attr.name))
@ -130,7 +130,7 @@ export const AutomationDefinitionsView: React.FC = () => {
const enrichedColumns = [
{ key: 'mandateName', label: 'Mandant', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 150, minWidth: 100, maxWidth: 250 },
{ key: 'featureInstanceName', label: 'Feature-Instanz', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 160, minWidth: 100, maxWidth: 250 },
{ key: '_createdByUserName', label: 'Erstellt von', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 150, minWidth: 100, maxWidth: 250 },
{ key: 'sysCreatedByUserName', label: 'Erstellt von', type: 'text' as any, sortable: true, filterable: true, searchable: true, width: 150, minWidth: 100, maxWidth: 250 },
];
return [...attrColumns, ...enrichedColumns];
}, [attributes]);

View file

@ -52,7 +52,7 @@ export const AutomationTemplatesView: React.FC = () => {
value ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: '0.75rem', padding: '0.125rem 0.5rem', borderRadius: 10, background: 'var(--info-color, #3182ce)', color: '#fff' }}><FaLock style={{ fontSize: '0.625rem' }} /> System</span>
: <span style={{ fontSize: '0.75rem', padding: '0.125rem 0.5rem', borderRadius: 10, background: 'var(--success-color, #38a169)', color: '#fff' }}>Instanz</span>
},
{ key: '_createdByUserName', label: 'Erstellt von', type: 'string' as const, width: 150 },
{ key: 'sysCreatedByUserName', label: 'Erstellt von', type: 'string' as const, width: 150 },
], []);
const handleEditClick = async (template: AutomationTemplate) => {

View file

@ -110,7 +110,7 @@ export const RealEstateParcelsView: React.FC = () => {
};
const formAttributes = useMemo(() => {
const excluded = ['id', 'mandateId', 'instanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excluded = ['id', 'mandateId', 'instanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return (attributes || []).filter(attr => !excluded.includes(attr.name));
}, [attributes]);

View file

@ -106,7 +106,7 @@ export const RealEstateProjectsView: React.FC = () => {
};
const formAttributes = useMemo(() => {
const excluded = ['id', 'mandateId', 'instanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excluded = ['id', 'mandateId', 'instanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return (attributes || []).filter(attr => !excluded.includes(attr.name));
}, [attributes]);

View file

@ -150,7 +150,7 @@ export const TrusteeDocumentsView: React.FC = () => {
// Form attributes (exclude system fields)
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'instanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excludedFields = ['id', 'mandateId', 'instanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return (attributes || []).filter(attr => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -52,7 +52,7 @@ export const TrusteePositionDocumentsView: React.FC = () => {
if (!attributes || attributes.length === 0) return [];
// Exclude system fields from table columns
const excludedFields = ['id', 'mandateId', 'featureInstanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excludedFields = ['id', 'mandateId', 'featureInstanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return attributes
.filter((attr: any) => !excludedFields.includes(attr.name))
@ -127,7 +127,7 @@ export const TrusteePositionDocumentsView: React.FC = () => {
// Form attributes (exclude system fields)
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'featureInstanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excludedFields = ['id', 'mandateId', 'featureInstanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return (attributes || []).filter((attr: any) => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -257,7 +257,7 @@ export const TrusteePositionsView: React.FC = () => {
const positionColumnOrder = [
'_documentRefs', // Belege (download icons)
'_syncStatus', // Sync-Status
'_createdAt', // Erstellt am
'sysCreatedAt', // Erstellt am
'valuta', // Valuta date
'tags',
'company',
@ -372,7 +372,7 @@ export const TrusteePositionsView: React.FC = () => {
// Form attributes (exclude system fields)
const formAttributes = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'instanceId', '_createdBy', '_createdAt', '_modifiedAt', '_modifiedBy'];
const excludedFields = ['id', 'mandateId', 'instanceId', 'sysCreatedBy', 'sysCreatedAt', 'sysModifiedAt', 'sysModifiedBy'];
return (attributes || []).filter(attr => !excludedFields.includes(attr.name));
}, [attributes]);

View file

@ -60,7 +60,7 @@ const NeutralizationPanel: React.FC<NeutralizationPanelProps> = ({ instanceId })
patternType: m.patternType || 'unknown',
fileId: m.fileId,
fileName: m.fileName,
createdAt: m.createdAt || m._createdAt,
createdAt: m.createdAt || m.sysCreatedAt,
})));
} catch (err) {
console.error('Failed to load mappings:', err);

View file

@ -322,14 +322,14 @@ export function hasAccess(level: AccessLevel): boolean {
*/
export function canAccessRecord(
level: AccessLevel,
record: { _createdBy?: string },
record: { sysCreatedBy?: string },
userId: string
): boolean {
switch (level) {
case 'n':
return false;
case 'm':
return record._createdBy === userId;
return record.sysCreatedBy === userId;
case 'g':
case 'a':
return true;