@open-mercato/core 0.6.4-develop.3996.1.430e257cfc → 0.6.4-develop.4000.1.450e315cec

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,113 @@
1
1
  const features = [
2
- { id: "sales.orders.view", title: "View sales orders", module: "sales" },
3
- { id: "sales.orders.manage", title: "Manage sales orders", module: "sales" },
4
- { id: "sales.orders.approve", title: "Approve sales orders", module: "sales" },
5
- { id: "sales.widgets.new-orders", title: "View new orders widget", module: "sales" },
6
- { id: "sales.widgets.new-quotes", title: "View new quotes widget", module: "sales" },
7
- { id: "sales.quotes.view", title: "View sales quotes", module: "sales" },
8
- { id: "sales.quotes.manage", title: "Manage sales quotes", module: "sales" },
9
- { id: "sales.documents.number.edit", title: "Edit sales document numbers", module: "sales" },
10
- { id: "sales.shipments.manage", title: "Manage order shipments", module: "sales" },
11
- { id: "sales.payments.manage", title: "Manage order payments", module: "sales" },
12
- { id: "sales.returns.view", title: "View order returns", module: "sales" },
13
- { id: "sales.returns.create", title: "Create order returns", module: "sales" },
14
- { id: "sales.invoices.manage", title: "Manage sales invoices", module: "sales" },
15
- { id: "sales.credit_memos.manage", title: "Manage credit memos", module: "sales" },
16
- { id: "sales.channels.manage", title: "Manage sales channels", module: "sales" },
17
- { id: "sales.settings.manage", title: "Manage sales configuration", module: "sales" }
2
+ {
3
+ id: "sales.orders.view",
4
+ title: "View sales orders",
5
+ module: "sales",
6
+ dependsOn: [
7
+ "sales.channels.view",
8
+ "sales.settings.view",
9
+ "customers.people.view",
10
+ "catalog.products.view",
11
+ "currencies.view"
12
+ ]
13
+ },
14
+ {
15
+ id: "sales.orders.manage",
16
+ title: "Manage sales orders",
17
+ module: "sales",
18
+ dependsOn: ["sales.orders.view"]
19
+ },
20
+ {
21
+ id: "sales.orders.approve",
22
+ title: "Approve sales orders",
23
+ module: "sales",
24
+ dependsOn: ["sales.orders.view"]
25
+ },
26
+ {
27
+ id: "sales.widgets.new-orders",
28
+ title: "View new orders widget",
29
+ module: "sales",
30
+ dependsOn: ["sales.orders.view"]
31
+ },
32
+ {
33
+ id: "sales.widgets.new-quotes",
34
+ title: "View new quotes widget",
35
+ module: "sales",
36
+ dependsOn: ["sales.quotes.view"]
37
+ },
38
+ {
39
+ id: "sales.quotes.view",
40
+ title: "View sales quotes",
41
+ module: "sales",
42
+ dependsOn: [
43
+ "sales.channels.view",
44
+ "sales.settings.view",
45
+ "customers.people.view",
46
+ "catalog.products.view"
47
+ ]
48
+ },
49
+ {
50
+ id: "sales.quotes.manage",
51
+ title: "Manage sales quotes",
52
+ module: "sales",
53
+ dependsOn: ["sales.quotes.view"]
54
+ },
55
+ {
56
+ id: "sales.documents.number.edit",
57
+ title: "Edit sales document numbers",
58
+ module: "sales",
59
+ dependsOn: ["sales.orders.view"]
60
+ },
61
+ {
62
+ id: "sales.shipments.manage",
63
+ title: "Manage order shipments",
64
+ module: "sales",
65
+ dependsOn: ["sales.orders.view", "shipping_carriers.view"]
66
+ },
67
+ {
68
+ id: "sales.payments.manage",
69
+ title: "Manage order payments",
70
+ module: "sales",
71
+ dependsOn: ["sales.orders.view", "payment_gateways.view"]
72
+ },
73
+ {
74
+ id: "sales.returns.view",
75
+ title: "View order returns",
76
+ module: "sales",
77
+ dependsOn: ["sales.orders.view"]
78
+ },
79
+ {
80
+ id: "sales.returns.create",
81
+ title: "Create order returns",
82
+ module: "sales",
83
+ dependsOn: ["sales.returns.view", "sales.orders.manage"]
84
+ },
85
+ {
86
+ id: "sales.invoices.manage",
87
+ title: "Manage sales invoices",
88
+ module: "sales",
89
+ dependsOn: ["sales.orders.view"]
90
+ },
91
+ {
92
+ id: "sales.credit_memos.manage",
93
+ title: "Manage credit memos",
94
+ module: "sales",
95
+ dependsOn: ["sales.invoices.manage"]
96
+ },
97
+ { id: "sales.channels.view", title: "View sales channels", module: "sales" },
98
+ {
99
+ id: "sales.channels.manage",
100
+ title: "Manage sales channels",
101
+ module: "sales",
102
+ dependsOn: ["sales.channels.view"]
103
+ },
104
+ { id: "sales.settings.view", title: "View sales configuration", module: "sales" },
105
+ {
106
+ id: "sales.settings.manage",
107
+ title: "Manage sales configuration",
108
+ module: "sales",
109
+ dependsOn: ["sales.settings.view"]
110
+ }
18
111
  ];
19
112
  var acl_default = features;
20
113
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/sales/acl.ts"],
4
- "sourcesContent": ["export const features = [\n { id: 'sales.orders.view', title: 'View sales orders', module: 'sales' },\n { id: 'sales.orders.manage', title: 'Manage sales orders', module: 'sales' },\n { id: 'sales.orders.approve', title: 'Approve sales orders', module: 'sales' },\n { id: 'sales.widgets.new-orders', title: 'View new orders widget', module: 'sales' },\n { id: 'sales.widgets.new-quotes', title: 'View new quotes widget', module: 'sales' },\n { id: 'sales.quotes.view', title: 'View sales quotes', module: 'sales' },\n { id: 'sales.quotes.manage', title: 'Manage sales quotes', module: 'sales' },\n { id: 'sales.documents.number.edit', title: 'Edit sales document numbers', module: 'sales' },\n { id: 'sales.shipments.manage', title: 'Manage order shipments', module: 'sales' },\n { id: 'sales.payments.manage', title: 'Manage order payments', module: 'sales' },\n { id: 'sales.returns.view', title: 'View order returns', module: 'sales' },\n { id: 'sales.returns.create', title: 'Create order returns', module: 'sales' },\n { id: 'sales.invoices.manage', title: 'Manage sales invoices', module: 'sales' },\n { id: 'sales.credit_memos.manage', title: 'Manage credit memos', module: 'sales' },\n { id: 'sales.channels.manage', title: 'Manage sales channels', module: 'sales' },\n { id: 'sales.settings.manage', title: 'Manage sales configuration', module: 'sales' },\n]\n\nexport default features\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,qBAAqB,OAAO,qBAAqB,QAAQ,QAAQ;AAAA,EACvE,EAAE,IAAI,uBAAuB,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EAC3E,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,QAAQ;AAAA,EAC7E,EAAE,IAAI,4BAA4B,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACnF,EAAE,IAAI,4BAA4B,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACnF,EAAE,IAAI,qBAAqB,OAAO,qBAAqB,QAAQ,QAAQ;AAAA,EACvE,EAAE,IAAI,uBAAuB,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EAC3E,EAAE,IAAI,+BAA+B,OAAO,+BAA+B,QAAQ,QAAQ;AAAA,EAC3F,EAAE,IAAI,0BAA0B,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACjF,EAAE,IAAI,yBAAyB,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EAC/E,EAAE,IAAI,sBAAsB,OAAO,sBAAsB,QAAQ,QAAQ;AAAA,EACzE,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,QAAQ;AAAA,EAC7E,EAAE,IAAI,yBAAyB,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EAC/E,EAAE,IAAI,6BAA6B,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EACjF,EAAE,IAAI,yBAAyB,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EAC/E,EAAE,IAAI,yBAAyB,OAAO,8BAA8B,QAAQ,QAAQ;AACtF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["export const features = [\n {\n id: 'sales.orders.view',\n title: 'View sales orders',\n module: 'sales',\n dependsOn: [\n 'sales.channels.view',\n 'sales.settings.view',\n 'customers.people.view',\n 'catalog.products.view',\n 'currencies.view',\n ],\n },\n {\n id: 'sales.orders.manage',\n title: 'Manage sales orders',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.orders.approve',\n title: 'Approve sales orders',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.widgets.new-orders',\n title: 'View new orders widget',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.widgets.new-quotes',\n title: 'View new quotes widget',\n module: 'sales',\n dependsOn: ['sales.quotes.view'],\n },\n {\n id: 'sales.quotes.view',\n title: 'View sales quotes',\n module: 'sales',\n dependsOn: [\n 'sales.channels.view',\n 'sales.settings.view',\n 'customers.people.view',\n 'catalog.products.view',\n ],\n },\n {\n id: 'sales.quotes.manage',\n title: 'Manage sales quotes',\n module: 'sales',\n dependsOn: ['sales.quotes.view'],\n },\n {\n id: 'sales.documents.number.edit',\n title: 'Edit sales document numbers',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.shipments.manage',\n title: 'Manage order shipments',\n module: 'sales',\n dependsOn: ['sales.orders.view', 'shipping_carriers.view'],\n },\n {\n id: 'sales.payments.manage',\n title: 'Manage order payments',\n module: 'sales',\n dependsOn: ['sales.orders.view', 'payment_gateways.view'],\n },\n {\n id: 'sales.returns.view',\n title: 'View order returns',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.returns.create',\n title: 'Create order returns',\n module: 'sales',\n dependsOn: ['sales.returns.view', 'sales.orders.manage'],\n },\n {\n id: 'sales.invoices.manage',\n title: 'Manage sales invoices',\n module: 'sales',\n dependsOn: ['sales.orders.view'],\n },\n {\n id: 'sales.credit_memos.manage',\n title: 'Manage credit memos',\n module: 'sales',\n dependsOn: ['sales.invoices.manage'],\n },\n { id: 'sales.channels.view', title: 'View sales channels', module: 'sales' },\n {\n id: 'sales.channels.manage',\n title: 'Manage sales channels',\n module: 'sales',\n dependsOn: ['sales.channels.view'],\n },\n { id: 'sales.settings.view', title: 'View sales configuration', module: 'sales' },\n {\n id: 'sales.settings.manage',\n title: 'Manage sales configuration',\n module: 'sales',\n dependsOn: ['sales.settings.view'],\n },\n]\n\nexport default features\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,qBAAqB,wBAAwB;AAAA,EAC3D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,qBAAqB,uBAAuB;AAAA,EAC1D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,sBAAsB,qBAAqB;AAAA,EACzD;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,uBAAuB;AAAA,EACrC;AAAA,EACA,EAAE,IAAI,uBAAuB,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EAC3E;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,qBAAqB;AAAA,EACnC;AAAA,EACA,EAAE,IAAI,uBAAuB,OAAO,4BAA4B,QAAQ,QAAQ;AAAA,EAChF;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,CAAC,qBAAqB;AAAA,EACnC;AACF;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }
@@ -42,6 +42,8 @@ const setup = {
42
42
  defaultRoleFeatures: {
43
43
  admin: ["sales.*", "sales.documents.number.edit"],
44
44
  employee: [
45
+ "sales.channels.view",
46
+ "sales.settings.view",
45
47
  "sales.orders.view",
46
48
  "sales.orders.manage",
47
49
  "sales.orders.approve",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/sales/setup.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { SalesSettings, SalesDocumentSequence, SalesTaxRate } from './data/entities'\nimport { DEFAULT_ORDER_NUMBER_FORMAT, DEFAULT_QUOTE_NUMBER_FORMAT } from './lib/documentNumberTokens'\nimport { seedSalesStatusDictionaries, seedSalesAdjustmentKinds } from './lib/dictionaries'\nimport { ensureExampleShippingMethods, ensureExamplePaymentMethods } from './seed/examples-data'\nimport { seedSalesExamples } from './seed/examples'\n\ntype SeedScope = { tenantId: string; organizationId: string }\n\nconst DEFAULT_TAX_RATES = [\n { code: 'vat-23', name: '23% VAT', rate: '23' },\n { code: 'vat-0', name: '0% VAT', rate: '0' },\n] as const\n\nasync function seedSalesTaxRates(em: EntityManager, scope: SeedScope): Promise<void> {\n await em.transactional(async (tem) => {\n const existing = await tem.find(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n const existingCodes = new Set(existing.map((rate) => rate.code))\n const hasDefault = existing.some((rate) => rate.isDefault)\n const now = new Date()\n let isFirst = !hasDefault\n\n for (const seed of DEFAULT_TAX_RATES) {\n if (existingCodes.has(seed.code)) continue\n tem.persist(\n tem.create(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n code: seed.code,\n name: seed.name,\n rate: seed.rate,\n priority: 0,\n isCompound: false,\n isDefault: isFirst,\n createdAt: now,\n updatedAt: now,\n })\n )\n isFirst = false\n }\n })\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['sales.*', 'sales.documents.number.edit'],\n employee: [\n 'sales.orders.view',\n 'sales.orders.manage',\n 'sales.orders.approve',\n 'sales.widgets.new-orders',\n 'sales.widgets.new-quotes',\n 'sales.quotes.view',\n 'sales.quotes.manage',\n 'sales.shipments.manage',\n 'sales.payments.manage',\n 'sales.returns.view',\n 'sales.returns.create',\n 'sales.invoices.manage',\n 'sales.credit_memos.manage',\n ],\n },\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n const exists = await em.findOne(SalesSettings, { tenantId, organizationId })\n if (!exists) {\n em.persist(\n em.create(SalesSettings, {\n tenantId,\n organizationId,\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n\n for (const kind of ['order', 'quote', 'return', 'invoice', 'credit_memo'] as const) {\n const seq = await em.findOne(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n })\n if (!seq) {\n em.persist(\n em.create(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n currentValue: 0,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n }\n\n await em.flush()\n },\n\n async seedDefaults({ em, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesTaxRates(em, scope)\n await seedSalesStatusDictionaries(em, scope)\n await seedSalesAdjustmentKinds(em, scope)\n await ensureExampleShippingMethods(em, scope)\n await ensureExamplePaymentMethods(em, scope)\n },\n\n async seedExamples({ em, container, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesExamples(em, container, scope)\n },\n}\n\nexport default setup\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { SalesSettings, SalesDocumentSequence, SalesTaxRate } from './data/entities'\nimport { DEFAULT_ORDER_NUMBER_FORMAT, DEFAULT_QUOTE_NUMBER_FORMAT } from './lib/documentNumberTokens'\nimport { seedSalesStatusDictionaries, seedSalesAdjustmentKinds } from './lib/dictionaries'\nimport { ensureExampleShippingMethods, ensureExamplePaymentMethods } from './seed/examples-data'\nimport { seedSalesExamples } from './seed/examples'\n\ntype SeedScope = { tenantId: string; organizationId: string }\n\nconst DEFAULT_TAX_RATES = [\n { code: 'vat-23', name: '23% VAT', rate: '23' },\n { code: 'vat-0', name: '0% VAT', rate: '0' },\n] as const\n\nasync function seedSalesTaxRates(em: EntityManager, scope: SeedScope): Promise<void> {\n await em.transactional(async (tem) => {\n const existing = await tem.find(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n const existingCodes = new Set(existing.map((rate) => rate.code))\n const hasDefault = existing.some((rate) => rate.isDefault)\n const now = new Date()\n let isFirst = !hasDefault\n\n for (const seed of DEFAULT_TAX_RATES) {\n if (existingCodes.has(seed.code)) continue\n tem.persist(\n tem.create(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n code: seed.code,\n name: seed.name,\n rate: seed.rate,\n priority: 0,\n isCompound: false,\n isDefault: isFirst,\n createdAt: now,\n updatedAt: now,\n })\n )\n isFirst = false\n }\n })\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['sales.*', 'sales.documents.number.edit'],\n employee: [\n 'sales.channels.view',\n 'sales.settings.view',\n 'sales.orders.view',\n 'sales.orders.manage',\n 'sales.orders.approve',\n 'sales.widgets.new-orders',\n 'sales.widgets.new-quotes',\n 'sales.quotes.view',\n 'sales.quotes.manage',\n 'sales.shipments.manage',\n 'sales.payments.manage',\n 'sales.returns.view',\n 'sales.returns.create',\n 'sales.invoices.manage',\n 'sales.credit_memos.manage',\n ],\n },\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n const exists = await em.findOne(SalesSettings, { tenantId, organizationId })\n if (!exists) {\n em.persist(\n em.create(SalesSettings, {\n tenantId,\n organizationId,\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n\n for (const kind of ['order', 'quote', 'return', 'invoice', 'credit_memo'] as const) {\n const seq = await em.findOne(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n })\n if (!seq) {\n em.persist(\n em.create(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n currentValue: 0,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n }\n\n await em.flush()\n },\n\n async seedDefaults({ em, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesTaxRates(em, scope)\n await seedSalesStatusDictionaries(em, scope)\n await seedSalesAdjustmentKinds(em, scope)\n await ensureExampleShippingMethods(em, scope)\n await ensureExamplePaymentMethods(em, scope)\n },\n\n async seedExamples({ em, container, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesExamples(em, container, scope)\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.4-develop.3996.1.430e257cfc",
3
+ "version": "0.6.4-develop.4000.1.450e315cec",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -243,16 +243,16 @@
243
243
  "zod": "^4.4.3"
244
244
  },
245
245
  "peerDependencies": {
246
- "@open-mercato/ai-assistant": "0.6.4-develop.3996.1.430e257cfc",
247
- "@open-mercato/shared": "0.6.4-develop.3996.1.430e257cfc",
248
- "@open-mercato/ui": "0.6.4-develop.3996.1.430e257cfc",
246
+ "@open-mercato/ai-assistant": "0.6.4-develop.4000.1.450e315cec",
247
+ "@open-mercato/shared": "0.6.4-develop.4000.1.450e315cec",
248
+ "@open-mercato/ui": "0.6.4-develop.4000.1.450e315cec",
249
249
  "react": "^19.0.0",
250
250
  "react-dom": "^19.0.0"
251
251
  },
252
252
  "devDependencies": {
253
- "@open-mercato/ai-assistant": "0.6.4-develop.3996.1.430e257cfc",
254
- "@open-mercato/shared": "0.6.4-develop.3996.1.430e257cfc",
255
- "@open-mercato/ui": "0.6.4-develop.3996.1.430e257cfc",
253
+ "@open-mercato/ai-assistant": "0.6.4-develop.4000.1.450e315cec",
254
+ "@open-mercato/shared": "0.6.4-develop.4000.1.450e315cec",
255
+ "@open-mercato/ui": "0.6.4-develop.4000.1.450e315cec",
256
256
  "@testing-library/dom": "^10.4.1",
257
257
  "@testing-library/jest-dom": "^6.9.1",
258
258
  "@testing-library/react": "^16.3.1",
@@ -8,15 +8,44 @@ export const metadata = {
8
8
  GET: { requireAuth: true, requireFeatures: ['auth.acl.manage'] },
9
9
  }
10
10
 
11
+ type FeatureItem = {
12
+ id: string
13
+ title: string
14
+ module: string
15
+ dependsOn?: string[]
16
+ }
17
+
18
+ function normalizeDependsOn(value: unknown): string[] | undefined {
19
+ if (!Array.isArray(value)) return undefined
20
+ const out: string[] = []
21
+ for (const entry of value) {
22
+ if (typeof entry !== 'string') continue
23
+ const trimmed = entry.trim()
24
+ if (!trimmed) continue
25
+ out.push(trimmed)
26
+ }
27
+ if (out.length === 0) return undefined
28
+ return Array.from(new Set(out))
29
+ }
30
+
11
31
  export async function GET(req: Request) {
12
32
  const auth = await getAuthFromRequest(req)
13
33
  if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
14
34
  const modules = getModules()
15
- const items = (modules || []).flatMap((m: any) =>
16
- (m.features || []).map((f: any) => ({ id: String(f.id), title: String(f.title || f.id), module: String(f.module || m.id) }))
35
+ const items: FeatureItem[] = (modules || []).flatMap((m: any) =>
36
+ (m.features || []).map((f: any) => {
37
+ const deps = normalizeDependsOn(f?.dependsOn)
38
+ const base: FeatureItem = {
39
+ id: String(f.id),
40
+ title: String(f.title || f.id),
41
+ module: String(f.module || m.id),
42
+ }
43
+ if (deps) base.dependsOn = deps
44
+ return base
45
+ })
17
46
  )
18
- // Deduplicate by id
19
- const byId = new Map<string, { id: string; title: string; module: string }>()
47
+ // Deduplicate by id (keep first occurrence)
48
+ const byId = new Map<string, FeatureItem>()
20
49
  for (const it of items) if (!byId.has(it.id)) byId.set(it.id, it)
21
50
  const list = Array.from(byId.values()).sort((a, b) => a.module.localeCompare(b.module) || a.id.localeCompare(b.id))
22
51
 
@@ -35,6 +64,7 @@ const featureItemSchema = z.object({
35
64
  id: z.string(),
36
65
  title: z.string(),
37
66
  module: z.string(),
67
+ dependsOn: z.array(z.string()).optional(),
38
68
  })
39
69
 
40
70
  const featureModuleSchema = z.object({
@@ -0,0 +1,173 @@
1
+ "use client"
2
+ import * as React from 'react'
3
+ import { Button } from '@open-mercato/ui/primitives/button'
4
+ import { Alert } from '@open-mercato/ui/primitives/alert'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import {
7
+ applyAddMissingDependency,
8
+ applyRemoveDependents,
9
+ applyRestoreDependency,
10
+ resolveAclDependencyDiagnostics,
11
+ type FeatureDescriptor,
12
+ } from '@open-mercato/shared/security/aclDependencies'
13
+
14
+ export type AclDependencyDiagnosticsPanelProps = {
15
+ granted: readonly string[]
16
+ catalog: readonly FeatureDescriptor[]
17
+ onGrantedChange: (updater: (prev: string[]) => string[]) => void
18
+ hideUnknownReferences?: boolean
19
+ }
20
+
21
+ export function AclDependencyDiagnosticsPanel({
22
+ granted,
23
+ catalog,
24
+ onGrantedChange,
25
+ hideUnknownReferences,
26
+ }: AclDependencyDiagnosticsPanelProps) {
27
+ const t = useT()
28
+ const diagnostics = React.useMemo(
29
+ () => resolveAclDependencyDiagnostics(granted, catalog),
30
+ [granted, catalog],
31
+ )
32
+ const titleById = React.useMemo(() => {
33
+ const map = new Map<string, string>()
34
+ for (const entry of catalog) {
35
+ if (entry?.title && !map.has(entry.id)) map.set(entry.id, entry.title)
36
+ }
37
+ return map
38
+ }, [catalog])
39
+ const featureLabel = React.useCallback((id: string) => titleById.get(id) ?? id, [titleById])
40
+
41
+ const hasMissing = diagnostics.missingDependencies.length > 0
42
+ const hasOrphaned = diagnostics.orphanedDependents.length > 0
43
+ const showUnknown = !hideUnknownReferences && diagnostics.unknownReferences.length > 0
44
+ if (!hasMissing && !hasOrphaned && !showUnknown) return null
45
+
46
+ const handleAdd = (dep: string) => onGrantedChange((prev) => applyAddMissingDependency(prev, dep))
47
+ const handleRestore = (dep: string) => onGrantedChange((prev) => applyRestoreDependency(prev, dep))
48
+ const handleDropDependents = (dependents: readonly string[]) =>
49
+ onGrantedChange((prev) => applyRemoveDependents(prev, dependents))
50
+
51
+ return (
52
+ <div className="space-y-3" data-testid="acl-dependency-diagnostics">
53
+ {hasMissing && (
54
+ <Alert status="warning" style="lighter">
55
+ <div className="space-y-2">
56
+ <div className="text-sm font-medium">
57
+ {t(
58
+ 'auth.acl.deps.missing.title',
59
+ 'Some granted permissions need other permissions to work:',
60
+ )}
61
+ </div>
62
+ <ul className="space-y-1 text-sm">
63
+ {diagnostics.missingDependencies.map((row) => (
64
+ <li
65
+ key={row.feature}
66
+ className="flex flex-wrap items-center gap-x-2 gap-y-1"
67
+ data-testid={`missing-${row.feature}`}
68
+ >
69
+ <span>
70
+ {t('auth.acl.deps.missing.item', '"{feature}" needs:', {
71
+ feature: featureLabel(row.feature),
72
+ })}
73
+ </span>
74
+ {row.missing.map((dep) => (
75
+ <span key={dep} className="inline-flex items-center gap-1">
76
+ <span className="font-mono text-xs text-muted-foreground">
77
+ {featureLabel(dep)}
78
+ </span>
79
+ <Button
80
+ type="button"
81
+ size="sm"
82
+ variant="outline"
83
+ onClick={() => handleAdd(dep)}
84
+ data-testid={`add-missing-${row.feature}-${dep}`}
85
+ >
86
+ {t('auth.acl.deps.missing.add', 'Add "{dep}"', {
87
+ dep: featureLabel(dep),
88
+ })}
89
+ </Button>
90
+ </span>
91
+ ))}
92
+ </li>
93
+ ))}
94
+ </ul>
95
+ </div>
96
+ </Alert>
97
+ )}
98
+
99
+ {hasOrphaned && (
100
+ <Alert status="warning" style="lighter">
101
+ <div className="space-y-2">
102
+ <div className="text-sm font-medium">
103
+ {t(
104
+ 'auth.acl.deps.orphaned.title',
105
+ 'Removing a permission that other granted permissions need:',
106
+ )}
107
+ </div>
108
+ <ul className="space-y-1 text-sm">
109
+ {diagnostics.orphanedDependents.map((row) => (
110
+ <li
111
+ key={row.dependency}
112
+ className="flex flex-wrap items-center gap-x-2 gap-y-1"
113
+ data-testid={`orphaned-${row.dependency}`}
114
+ >
115
+ <span>
116
+ {t('auth.acl.deps.orphaned.item', '"{dependency}" is required by:', {
117
+ dependency: featureLabel(row.dependency),
118
+ })}
119
+ </span>
120
+ <span className="font-mono text-xs text-muted-foreground">
121
+ {row.dependents
122
+ .map((dependent) => featureLabel(dependent))
123
+ .join(', ')}
124
+ </span>
125
+ <Button
126
+ type="button"
127
+ size="sm"
128
+ variant="outline"
129
+ onClick={() => handleRestore(row.dependency)}
130
+ data-testid={`restore-${row.dependency}`}
131
+ >
132
+ {t('auth.acl.deps.orphaned.restore', 'Restore "{dependency}"', {
133
+ dependency: featureLabel(row.dependency),
134
+ })}
135
+ </Button>
136
+ <Button
137
+ type="button"
138
+ size="sm"
139
+ variant="outline"
140
+ onClick={() => handleDropDependents(row.dependents)}
141
+ data-testid={`drop-dependents-${row.dependency}`}
142
+ >
143
+ {t('auth.acl.deps.orphaned.drop', 'Drop dependents')}
144
+ </Button>
145
+ </li>
146
+ ))}
147
+ </ul>
148
+ </div>
149
+ </Alert>
150
+ )}
151
+
152
+ {showUnknown && (
153
+ <Alert status="warning" style="lighter">
154
+ <div className="space-y-1">
155
+ <div className="text-sm font-medium">
156
+ {t(
157
+ 'auth.acl.deps.unknown.title',
158
+ 'Some declared dependencies do not match any known permission:',
159
+ )}
160
+ </div>
161
+ <ul className="space-y-1 text-sm font-mono text-xs text-muted-foreground">
162
+ {diagnostics.unknownReferences.map((row) => (
163
+ <li key={row.feature} data-testid={`unknown-${row.feature}`}>
164
+ {row.feature} → {row.missing.join(', ')}
165
+ </li>
166
+ ))}
167
+ </ul>
168
+ </div>
169
+ </Alert>
170
+ )}
171
+ </div>
172
+ )
173
+ }
@@ -5,6 +5,8 @@ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
5
5
  import Link from 'next/link'
6
6
  import { hasFeature, matchFeature } from '@open-mercato/shared/security/features'
7
7
  import { useT } from '@open-mercato/shared/lib/i18n/context'
8
+ import type { FeatureDescriptor } from '@open-mercato/shared/security/aclDependencies'
9
+ import { AclDependencyDiagnosticsPanel } from './AclDependencyDiagnosticsPanel'
8
10
 
9
11
  function toTitleCase(value: string): string {
10
12
  return value.replace(/[-_.]/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase())
@@ -36,7 +38,7 @@ function formatWildcardLabel(moduleId: string, wildcard: string): string {
36
38
  return `All ${suffix.split('.').map(toTitleCase).join(' / ')}`
37
39
  }
38
40
 
39
- type Feature = { id: string; title: string; module: string }
41
+ type Feature = { id: string; title: string; module: string; dependsOn?: string[] }
40
42
  type ModuleInfo = { id: string; title: string }
41
43
  type RoleListItem = { id?: string | null; name?: string | null }
42
44
  type RoleListResponse = { items?: RoleListItem[] }
@@ -364,9 +366,9 @@ export function AclEditor({
364
366
  <div className="rounded border border-blue-200 bg-blue-50 p-3">
365
367
  <div className="text-sm font-medium text-blue-900">Global wildcard (*) enabled</div>
366
368
  <div className="text-xs text-blue-700 mt-1">This grants access to all features in the system.</div>
367
- <Button
368
- variant="outline"
369
- size="sm"
369
+ <Button
370
+ variant="outline"
371
+ size="sm"
370
372
  className="mt-2"
371
373
  onClick={() => updateGranted((prev) => prev.filter((x) => x !== '*'))}
372
374
  >
@@ -374,6 +376,14 @@ export function AclEditor({
374
376
  </Button>
375
377
  </div>
376
378
  )}
379
+ {!hasGlobalWildcard && (
380
+ <AclDependencyDiagnosticsPanel
381
+ granted={granted}
382
+ catalog={features as readonly FeatureDescriptor[]}
383
+ onGrantedChange={updateGranted}
384
+ hideUnknownReferences={process.env.NODE_ENV === 'production'}
385
+ />
386
+ )}
377
387
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
378
388
  {grouped.map((group) => {
379
389
  const moduleWildcard = isModuleWildcardEnabled(group.moduleId)
@@ -4,6 +4,14 @@
4
4
  "auth.accessDenied.message": "Du hast keine Berechtigung, diese Seite anzuzeigen. Bitte wende dich an deinen Administrator.",
5
5
  "auth.accessDenied.title": "Zugriff verweigert",
6
6
  "auth.acl.allowAllOrganizations": "Alle Organisationen zulassen",
7
+ "auth.acl.deps.missing.add": "\"{dep}\" hinzufügen",
8
+ "auth.acl.deps.missing.item": "\"{feature}\" benötigt:",
9
+ "auth.acl.deps.missing.title": "Einige gewährte Berechtigungen benötigen weitere Berechtigungen, um zu funktionieren:",
10
+ "auth.acl.deps.orphaned.drop": "Abhängige entfernen",
11
+ "auth.acl.deps.orphaned.item": "\"{dependency}\" wird benötigt von:",
12
+ "auth.acl.deps.orphaned.restore": "\"{dependency}\" wiederherstellen",
13
+ "auth.acl.deps.orphaned.title": "Entfernung einer Berechtigung, die andere gewährte Berechtigungen benötigen:",
14
+ "auth.acl.deps.unknown.title": "Einige deklarierte Abhängigkeiten passen zu keiner bekannten Berechtigung:",
7
15
  "auth.acl.organizationWarning": "Organisationseinschränkungen werden nur gespeichert, wenn mindestens eine Berechtigungsüberschreibung ausgewählt ist. Füge eine Berechtigung hinzu oder aktiviere einen Modul-Wildcard vor dem Speichern.",
8
16
  "auth.acl.organizationsScope": "Organisationsbereich",
9
17
  "auth.acl.restricted": "Eingeschränkt",
@@ -4,6 +4,14 @@
4
4
  "auth.accessDenied.message": "You do not have permission to view this page. Please contact your administrator.",
5
5
  "auth.accessDenied.title": "Access Denied",
6
6
  "auth.acl.allowAllOrganizations": "Allow all organizations",
7
+ "auth.acl.deps.missing.add": "Add \"{dep}\"",
8
+ "auth.acl.deps.missing.item": "\"{feature}\" needs:",
9
+ "auth.acl.deps.missing.title": "Some granted permissions need other permissions to work:",
10
+ "auth.acl.deps.orphaned.drop": "Drop dependents",
11
+ "auth.acl.deps.orphaned.item": "\"{dependency}\" is required by:",
12
+ "auth.acl.deps.orphaned.restore": "Restore \"{dependency}\"",
13
+ "auth.acl.deps.orphaned.title": "Removing a permission that other granted permissions need:",
14
+ "auth.acl.deps.unknown.title": "Some declared dependencies do not match any known permission:",
7
15
  "auth.acl.organizationWarning": "Organization restrictions are saved only when at least one feature override is selected. Add a feature or enable a module wildcard before saving.",
8
16
  "auth.acl.organizationsScope": "Organizations scope",
9
17
  "auth.acl.restricted": "Restricted",
@@ -4,6 +4,14 @@
4
4
  "auth.accessDenied.message": "No tienes permiso para ver esta página. Por favor, contacta con tu administrador.",
5
5
  "auth.accessDenied.title": "Acceso denegado",
6
6
  "auth.acl.allowAllOrganizations": "Permitir todas las organizaciones",
7
+ "auth.acl.deps.missing.add": "Agregar \"{dep}\"",
8
+ "auth.acl.deps.missing.item": "\"{feature}\" necesita:",
9
+ "auth.acl.deps.missing.title": "Algunos permisos otorgados necesitan otros permisos para funcionar:",
10
+ "auth.acl.deps.orphaned.drop": "Eliminar dependientes",
11
+ "auth.acl.deps.orphaned.item": "\"{dependency}\" es requerido por:",
12
+ "auth.acl.deps.orphaned.restore": "Restaurar \"{dependency}\"",
13
+ "auth.acl.deps.orphaned.title": "Eliminando un permiso que otros permisos otorgados necesitan:",
14
+ "auth.acl.deps.unknown.title": "Algunas dependencias declaradas no coinciden con ningún permiso conocido:",
7
15
  "auth.acl.organizationWarning": "Las restricciones de organización se guardan solo cuando se selecciona al menos una anulación de función. Agrega una función o habilita un comodín de módulo antes de guardar.",
8
16
  "auth.acl.organizationsScope": "Alcance de organizaciones",
9
17
  "auth.acl.restricted": "Restringido",
@@ -4,6 +4,14 @@
4
4
  "auth.accessDenied.message": "Nie masz uprawnień do wyświetlenia tej strony. Skontaktuj się z administratorem.",
5
5
  "auth.accessDenied.title": "Brak dostępu",
6
6
  "auth.acl.allowAllOrganizations": "Zezwól na wszystkie organizacje",
7
+ "auth.acl.deps.missing.add": "Dodaj \"{dep}\"",
8
+ "auth.acl.deps.missing.item": "\"{feature}\" wymaga:",
9
+ "auth.acl.deps.missing.title": "Niektóre przyznane uprawnienia wymagają innych uprawnień do działania:",
10
+ "auth.acl.deps.orphaned.drop": "Usuń zależne",
11
+ "auth.acl.deps.orphaned.item": "\"{dependency}\" jest wymagane przez:",
12
+ "auth.acl.deps.orphaned.restore": "Przywróć \"{dependency}\"",
13
+ "auth.acl.deps.orphaned.title": "Usuwasz uprawnienie wymagane przez inne przyznane uprawnienia:",
14
+ "auth.acl.deps.unknown.title": "Niektóre zadeklarowane zależności nie pasują do żadnego znanego uprawnienia:",
7
15
  "auth.acl.organizationWarning": "Ograniczenia organizacyjne są zapisywane tylko wtedy, gdy wybrano co najmniej jedno nadpisanie uprawnień. Dodaj uprawnienie lub włącz wildcard modułu przed zapisaniem.",
8
16
  "auth.acl.organizationsScope": "Zakres organizacji",
9
17
  "auth.acl.restricted": "Ograniczone",