feat(server): convert view to overridable entity (#21436)

## Context

Every entity created as a side effect of object creation must support
the overridable pattern (`isActive` + `overrides` + override routing)
before we can re-own side effects to their true application. Starting
with View.

viewField, viewFieldGroup, pageLayoutTab and pageLayoutWidget already
extend `OverridableEntity`. This PR brings `view` to the same pattern.

## What this does

- `ViewEntity` now extends `OverridableEntity<ViewOverrides>` (adds
`isActive` boolean + `overrides` jsonb). All editable view properties
are overridable; the 3 fieldMetadata foreign keys are converted to/from
universal identifiers like viewField's `viewFieldGroupId`.
- **Update**: mutations on a view not owned by the caller (e.g. standard
views like "All Companies") are written into `overrides` instead of
mutating the row. Reads merge overrides in
the DTO.
- **Delete/destroy**: views not owned by the caller are deactivated
(`isActive = false`) instead of deleted.
~~- **INDEX invariant**: `key = INDEX` views can only be created via
object-creation side effect. The API now rejects creating, deleting or
destroying INDEX views (object-deletion cascade is unaffected). This was
not really needed for this migration but was flagged during
implementation.~~
- **Front**: views with `isActive = false` are filtered out of the views
selector.
- Fast instance command adds the two columns
(`2-12-instance-command-fast-...-view-overridable-entity.ts`).

## Notes

- Custom (caller-owned) views behave exactly as before: direct updates,
soft delete.
- View-group side effects (kanban groups) are computed on the
override-merged view so overridden `mainGroupByFieldMetadataId` works.
This commit is contained in:
Weiko
2026-06-11 18:00:52 +02:00
committed by GitHub
parent 947a4d5253
commit cfb9772179
35 changed files with 497 additions and 52 deletions
@@ -0,0 +1,17 @@
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('2.12.0', 1781114009075)
export class ViewOverridableEntityFastInstanceCommand implements FastInstanceCommand {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "core"."view" ADD "overrides" jsonb');
await queryRunner.query('ALTER TABLE "core"."view" ADD "isActive" boolean NOT NULL DEFAULT true');
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE "core"."view" DROP COLUMN "isActive"');
await queryRunner.query('ALTER TABLE "core"."view" DROP COLUMN "overrides"');
}
}