Files
hedena_gold/apps/api/src/modules/auth/acl.constants.ts
T
s.esmaeiliandClaude Opus 5.5 699657e483 feat(pricing): the server prices app orders with legacy's making-charge formula (MON-08)
Legacy: final % = variant % + customer % − the largest active promotion
on the product, its category or its brand; rial charge 0. The apps sent
the variant's own % only, so the new app undercharged by the customer's
percent (usually 3) and ignored promotions, and the server trusted
whatever the app sent.

- pricing/wage-formula.ts: one formula, legacy by default, with
  coefficients, promotion pick, constant, floor/cap and rounding so the
  admin screen can change it; validated
- an app order is priced by it; the app's wage, discount and rial charge
  are ignored; an operator's manual numbers are kept
- POST customer/me/wage-quote; both apps show that figure
- admin/pricing/wage-formula (config.wage): read, preview, approve with
  the sample it was checked on, last 20 versions kept; the setting is
  kept off the generic const routes

Owner approval: 2026-10-08.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:26:55 +03:30

207 lines
5.8 KiB
TypeScript

/**
* Every permission id (`rid`) the admin panel actually enforces.
*
* Regenerated mechanically from legacy (see scratchpad/convert_tree.js) as the
* union of two enforcement points:
* - 58 gated by a route's `meta.role` (router/admin.js, router/branch.js)
* - 100 gated inside views via `$hasPermission('...')`
* = 144 distinct rids (the two sets overlap)
*
* An earlier revision of this file listed only the 58 route-gated ones, which
* left the seeded super-admin unable to use the in-view actions. Corrected.
*
* These are LEAVES as written. Ancestors ("order" from "order.list", "persons"
* from "persons.list.client.omde", ...) are NOT stored — they are derived at
* runtime by expandWithParents(), exactly as legacy's checkParent() did.
*/
export const ALL_RIDS = [
'branch.create',
'branch.list',
'buy.order.list',
'cities.create',
'cities.list',
'cities.list.remove',
'cities.list.view',
'cities.list.view.edit',
'collection.create',
'collection.list',
'collection.list.edit',
'collection.list.remove',
'comment',
'comment.remove',
'comment.state',
'comment.view',
'comment.view.edit',
// D27 — «اتصال حسابداری» (Tahesab / Zarrin). Not a legacy rid.
'config.accounting',
'config.wage',
'config.report',
'config.statics',
'config.statics.brand',
'config.statics.brand.create',
'config.statics.brand.edit',
'config.statics.brand.remove',
'config.statics.tags',
'config.statics.tags.create',
'config.statics.tags.edit',
'config.statics.tags.remove',
'config.wallet',
'custom.config.statics.slider',
'custom.config.statics.tags.banner.ids',
'dashboard.view',
'document.create',
'document.list',
'document.list.confirm',
'document.list.deny',
'document.list.edit',
'document.list.get',
'document.list.remove',
'invoice.create',
'invoice.list',
'newsletters',
'notify.create',
'notify.me',
'offers.autopromotion',
'offers.create',
'offers.create.tak',
'offers.list',
'offers.list.edit',
'offers.list.remove',
'offers.list.tak.edit',
'offers.list.tak.remove',
'offersIgnore',
'order.create',
'order.list',
'order.list.pdf',
'order.list.sts',
'order.list.sts.remove',
'order.list.sts.submit',
'order.list.view',
'order.list.view.edit',
'order.makeOrder',
'order.makeOrder.get',
'order.makeOrder.remove',
'order.makeOrder.state',
'order.makeOrder.state.remove',
'order.makeOrder.state.submit',
'order.makeOrder.view',
'order.makeOrder.view.edit',
'order.omde',
'order.omde.get',
'order.omde.remove',
'order.omde.state',
'order.omde.state.remove',
'order.omde.state.submit',
'order.omde.view',
'order.omde.view.edit',
'order.omde.view.remove',
'order.tamin',
'order.tamin.state',
'order.tamin.state.remove',
'order.tamin.state.submit',
'order.tamin.view',
'order.tamin.view.edit',
'persons.create',
'persons.create.client',
'persons.create.customer.tak',
'persons.create.seller',
'persons.list.client.omde',
'persons.list.client.omde.able',
'persons.list.client.omde.able.order',
'persons.list.client.omde.edit',
'persons.list.client.omde.unable',
'persons.list.client.omde.unable.order',
'persons.list.client.omde.view',
'persons.list.client.omde.view.documents',
'persons.list.client.omde.view.orders',
'persons.list.client.single',
'persons.list.client.single.edit',
'persons.list.client.single.view',
'persons.list.sellers',
'persons.list.sellers.able',
'persons.list.sellers.able.order',
'persons.list.sellers.edit',
'persons.list.sellers.unable',
'persons.list.sellers.unable.order',
'persons.list.sellers.view',
'persons.list.sellers.view.documents',
'persons.list.sellers.view.orders',
'product.create',
'product.list',
'product.list.edit',
'product.list.group.edit',
'product.list.remove',
'product.list.variants',
'product.list.variants.add',
'product.list.variants.edit',
'product.list.variants.remove',
'product.list.view',
'product.percent',
'report.order.item',
'report.product',
'sms.create',
'sms.create.all',
'sms.create.multiple',
'sms.create.omde',
'sms.create.reagent',
'sms.create.single',
'sms.list',
'statics',
'user.create',
'user.list',
'user.list.create',
'user.list.edit',
'user.list.remove',
'user.permission',
'user.permission.create',
'user.permission.edit',
'wallet',
'wallet.charge',
'wallet.list',
'wallet.percent',
'workshop.create',
'workshop.list',
] as const;
export type Rid = (typeof ALL_RIDS)[number];
/**
* Port of henza_admin/src/store/users/index.js getParents() — returns every
* ancestor of a dotted rid, longest first.
*
* getParents(['a','b','c']) -> ['a.b', 'a']
*/
function getParents(keys: string[]): string[] {
const work = [...keys];
const parents: string[] = [];
while (work.length > 1) {
work.pop();
parents.push(work.join('.'));
}
return parents;
}
/**
* Port of henza_admin/src/store/users/index.js checkParent().
*
* Granting "persons.list.client.omde" implicitly grants "persons.list.client",
* "persons.list" and "persons", so parent menu nodes stay visible. The legacy
* version mutated a module-scoped `rid` variable; this one is pure, which changes
* nothing observable but makes it safe to call concurrently on the server.
*
* Note this only walks UP. A rid never implies its children — which is why the
* legacy nav item guarded by "wallet.list" stays hidden for a user granted only
* "wallet". That mismatch is a legacy bug, recorded in docs/ADMIN-PORT-PLAN.md,
* and is preserved here rather than silently repaired.
*/
export function expandWithParents(values: string[]): string[] {
let rid: string[] = [...values];
for (const x of values) {
const keys = x.split('.');
if (keys.length > 1) {
rid = [...new Set([...rid, ...getParents(keys)])];
}
}
return rid;
}