Admin commands for staff/DOC to manage businesses in-game: /firm admin disband, /firm admin rename, /firm admin set attribute, /firm admin set proprietor.
Technical notes — No business.admin.* firm command surface exists (only business.admin.reload). Service methods present: disbandFirm (proprietor-gated), updateProprietor(firmId, playerId) (internal, ungated), updateFirmHq/Discord (ADMIN-gated). Missing: rename — FirmMapper has create/update/archive but no name-change method. Add a FirmCommands @Route("admin …") group gated on a new business.admin.* perm that bypasses proprietor checks; reuse disbandFirm/updateProprietor, add a new renameFirm routed through NameValidator + uniqueness (like createFirm).
Would be nice if we could rename firms with this one?
Implemented on develop (commit 7d7f8ed).
New /firm admin … command group, each route gated on a business.admin.* node (not registered in plugin.yml, like the rest of the fine-grained nodes — grant via LuckPerms). All bypass proprietor/firm-role checks:
| Command | Permission |
|---|---|
admin disband <firm> | business.admin.disband |
admin rename <firm> <newname> | business.admin.rename |
admin set hq <firm> <plot> | business.admin.attribute |
admin set discord <firm> <url> | business.admin.attribute |
admin set proprietor <firm> <player> | business.admin.proprietor |
Service layer
disbandInternal(firm) (drains balances to the proprietor + archives) so the proprietor-gated and admin paths share it; admin path skips only the isProprietor check. Idempotent (already-disbanded → rejected).FirmMapper had no name-change). renameFirm validates via the same rules as creation — extracted validateFirmName(...) shared with createFirm — and rejects collisions with a different firm (self-rename / case-change is allowed). Persists through updateFirm's existing conditional display_name set.The admin firm arg tab-completes from firms with someone online (OnlineFirmName) but resolves any firm by name/id. <newname> is a single token, consistent with create.
Tests: 13 new cases in FirmServiceImplTest (admin disband bypass + guards, rename success/unknown/invalid/duplicate/self-rename, set hq/discord/proprietor, unknown-firm guard). Coverage gate (≥95%) green.
Bonus: dropped the stale "ampersand" wording from the firm-name validation message (PAR-53 removed & from the validator).
Code context — No
business.admin.*firm command surface exists (onlybusiness.admin.reload). Service methods present:disbandFirm(proprietor-gated),updateProprietor(firmId, playerId)(internal, ungated — callers must gate),updateFirmHq/Discord(ADMIN-gated). Missing: rename —FirmMapperhas create/update/archive but no name-change method.Approach: add a
FirmCommands@Route("admin disband|rename|set proprietor …")group gated on a newbusiness.admin.*perm that bypasses the proprietor checks; reusedisbandFirm/updateProprietor, and add a newrenameFirmservice+mapper method routed throughNameValidator+ a uniqueness check (mirroringcreateFirm).