Delete semantics: hard-delete vs. soft-deactivate — match the legacy SP, don't invent an Eos-side rule
Audited 2026-08-03 across every entity with a delete path. There is
deliberately no single Eos-wide mode — hard-delete and soft-deactivate
coexist because the legacy SPs themselves aren't uniform, and retrofitting
one shape onto all of them would fight the schema, not simplify it. What this
section fixes is that the choice was previously made ad hoc, rediscovered per
entity with no written criterion (see TASKS.md's now-closed "Document/enforce
a hard-delete vs. soft-deactivate rule" item) — this names the criterion so
the next entity's delete path is a decision, not a guess.
:::note No code changes came out of this pass Every existing entity's behavior already matches the rule below (with the two documented exceptions) — this section only makes it explicit. :::
The rule, in priority order
-
If the entity's manage SP only implements one delete mode, use it. Most entities have no choice to make — e.g.
MAS_ManWareHouseDt/MAS_ManUserDtonly ever offer soft-deactivate (Warehouse,User). -
If the SP offers both modes, default to whichever mode matches the entity's category below, unless a specific reason says otherwise — and if you deviate, write the reason down at the call site, the same way
MenuRepositoryandRoleRepositoryalready do (see the two deviations below). -
Two confirmed, deliberate deviations from the category default — both already correct, kept as-is (see below).
-
New entities with no legacy SP at all (the
MAS_CHOICES-style exception, see the Reference CRUD Pattern page) choose per the same category rule, implemented directly against EF Core —Country/Choiceboth hard-delete this way today.
Entity categories
-
Master-data/reference entities (City, Country, Branch, Company, Client, Department, Equipment, ForumType, Queue, TrCode, Address, UserGroup) → hard-delete. This is the existing majority pattern, and it's safe because every one of these SPs already guards the delete with its own referential-integrity check (e.g. Company blocks delete if still referenced elsewhere, TrCode blocks if still used in inventory) — Eos doesn't need to add a second guard, just trust the SP's.
-
Junction/assignment tables (
GroupRoleAssignment,RoleMenuGrant,UserMenuOverride, and any future "X grants/is-assigned-to Y" table) → soft-deactivate (Active=0). This is already the consistent pattern across all three existing ones — "was this ever granted" has audit value, and re-granting later is a status flip, not a re-insert.Scenario(the automation engine's "IF trigger AND conditions THEN action" rule row, see the Automation Engine page) joined this bucket 2026-08-03 — same reasoning (Enabled=0), plus a structural forcing function:COM_SCENARIOS_LOG_DL_V2has a non-cascading FK toMAS_SCENARIOS_V2, so a hard delete would fail outright for any scenario that has ever fired, not just be a worse choice. -
Transactional entities (MasterOrder, WarehouseOrder, and their line tables) → hard-delete, but only by calling into the legacy SP's own cascade — never write a new cascading delete in Eos code.
WarehouseOrder's outbound line unlink (WMS_CancelAllocateWOLine) is the one deliberate soft-delete exception in this category, because unlinking there also reverses an allocation and needs an audit row — a plain hard delete would lose that.
Confirmed deliberate deviations — do not "fix" these
:::note Known deviation — MenuRepository
MAS_ManMenuDt supports both ActionFlag=3 (soft) and =4 (hard-cascade),
but Eos only ever calls soft, by decision: hard-cascade would wipe
MAS_APP_MENU/MAS_ROLE_MENU grouping/permission data that a later
reactivation would need to re-enter by hand.
:::
:::note Known deviation — RoleRepository
MAS_ManRoleDt's own hard-delete branch (Active=-1) is broken against the
real schema (MAS_ROLE.Active is an unsigned tinyint, can't hold -1), so
DeleteAsync routes around it by calling Update with Active=0 instead.
Not a stylistic choice — a workaround for a real SP bug. Flagged in
TASKS.md as the strongest refactor candidate if the SP is ever fixed
upstream (fixing the legacy SP itself is out of scope for Eos).
:::
Reference files
WarehouseRepository.cs (SP-only-mode soft-deactivate), MenuRepository.cs
(deliberate soft-over-available-hard), RoleRepository.cs (soft-as-workaround-
for-broken-SP), GroupRoleAssignmentRepository.cs/RoleMenuGrantRepository.cs/
UserMenuOverrideRepository.cs (junction-table soft-deactivate),
WarehouseOrderRepository.cs (CancelAllocationAsync, the transactional-category
soft exception), ScenarioRepository.cs (junction-table soft-deactivate forced
by a real FK constraint, not just category fit).