Root cause
decisions.json's entity-level hideDelete: true correctly flows through generate-contract.js (lines ~1270, ~1329) into contract.json's frontendContract/backendContract.apiPrediction.crud.<entity>.delete = false, and the generator correctly bakes that into the generated frontend Page component (UI hides/no-ops the delete action).
However, hideDelete never reaches the NEO Headless backend's actual capability flag:
cli/src/push-to-neo.js's stepPopulateSpec() (~line 562) unconditionally calls populateWindowSpec() in cli/src/neo-writer.js with includeAllMethods: true.
neo-writer.js:485-487 — upsertEntity's default isDelete value is overridden by the spread at neo-writer.js:513, which forces isDelete: 'Y' on every ETGO_SF_ENTITY row derived from AD metadata because of the includeAllMethods: true flag above.
- Nothing downstream in
push-to-neo.js ever reads contract.json's apiPrediction.crud.<entity>.delete per entity to override isDelete accordingly.
Net effect: hideDelete: true only hides delete in the UI. A direct DELETE request against the NEO Headless API (bypassing the frontend) still succeeds server-side — the enforcement mechanism exists (entity.isDelete() is genuinely checked at runtime) but is never wired from the hideDelete decision.
Affected windows
- All 8 windows from ETP-4565:
product-category, business-partner-category, asset-group, tax, financial-account, contacts (x2), warehouse
- Earlier precedents that also used entity-level
hideDelete: ETP-4464 (tax) and ETP-4512 (userRoles on the user window)
Proposed fix
In cli/src/push-to-neo.js, add a new step between renameEntitiesToContractNames and stepUpdateFieldVisibility that:
- Reads
contract.json's apiPrediction.crud.<entity>.delete per entity.
- Syncs
etgo_sf_entity.isdelete accordingly (Y when true/absent, matching existing default; N when explicitly false).
This is a generic pipeline fix (not per-window). Once landed, push-to-neo must be re-run for all previously-affected windows (tax, user/userRoles, and the 8 ETP-4565 windows) to close the gap in already-deployed tenants.
Jira: ETP-4745
Root cause
decisions.json's entity-levelhideDelete: truecorrectly flows throughgenerate-contract.js(lines ~1270, ~1329) intocontract.json'sfrontendContract/backendContract.apiPrediction.crud.<entity>.delete = false, and the generator correctly bakes that into the generated frontend Page component (UI hides/no-ops the delete action).However,
hideDeletenever reaches the NEO Headless backend's actual capability flag:cli/src/push-to-neo.js'sstepPopulateSpec()(~line 562) unconditionally callspopulateWindowSpec()incli/src/neo-writer.jswithincludeAllMethods: true.neo-writer.js:485-487—upsertEntity's defaultisDeletevalue is overridden by the spread atneo-writer.js:513, which forcesisDelete: 'Y'on everyETGO_SF_ENTITYrow derived from AD metadata because of theincludeAllMethods: trueflag above.push-to-neo.jsever readscontract.json'sapiPrediction.crud.<entity>.deleteper entity to overrideisDeleteaccordingly.Net effect:
hideDelete: trueonly hides delete in the UI. A directDELETErequest against the NEO Headless API (bypassing the frontend) still succeeds server-side — the enforcement mechanism exists (entity.isDelete()is genuinely checked at runtime) but is never wired from thehideDeletedecision.Affected windows
product-category,business-partner-category,asset-group,tax,financial-account,contacts(x2),warehousehideDelete: ETP-4464 (tax) and ETP-4512 (userRoleson theuserwindow)Proposed fix
In
cli/src/push-to-neo.js, add a new step betweenrenameEntitiesToContractNamesandstepUpdateFieldVisibilitythat:contract.json'sapiPrediction.crud.<entity>.deleteper entity.etgo_sf_entity.isdeleteaccordingly (Ywhen true/absent, matching existing default;Nwhen explicitlyfalse).This is a generic pipeline fix (not per-window). Once landed,
push-to-neomust be re-run for all previously-affected windows (tax,user/userRoles, and the 8 ETP-4565 windows) to close the gap in already-deployed tenants.Jira: ETP-4745