@open-mercato/core 0.6.6-develop.6404.1.e2f8d5025a → 0.6.6-develop.6407.1.9a4c26cf6a

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.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/attachments/api/route.js +10 -4
  3. package/dist/modules/attachments/api/route.js.map +2 -2
  4. package/dist/modules/catalog/api/settings/route.js +142 -0
  5. package/dist/modules/catalog/api/settings/route.js.map +7 -0
  6. package/dist/modules/catalog/backend/config/catalog/page.js +6 -2
  7. package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
  8. package/dist/modules/catalog/components/UnitPriceDisplaySettings.js +89 -0
  9. package/dist/modules/catalog/components/UnitPriceDisplaySettings.js.map +7 -0
  10. package/dist/modules/catalog/components/products/ProductUomSection.js +4 -2
  11. package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
  12. package/dist/modules/catalog/components/products/hooks/useUnitPriceDisplayEnabled.js +25 -0
  13. package/dist/modules/catalog/components/products/hooks/useUnitPriceDisplayEnabled.js.map +7 -0
  14. package/dist/modules/catalog/lib/settings.js +9 -0
  15. package/dist/modules/catalog/lib/settings.js.map +7 -0
  16. package/dist/modules/customers/api/pipeline-stages/route.js +6 -4
  17. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  18. package/dist/modules/customers/api/pipelines/route.js +5 -3
  19. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  20. package/dist/modules/staff/api/team-members/assignable/route.js +7 -11
  21. package/dist/modules/staff/api/team-members/assignable/route.js.map +2 -2
  22. package/package.json +7 -7
  23. package/src/modules/attachments/api/route.ts +20 -4
  24. package/src/modules/attachments/i18n/de.json +1 -0
  25. package/src/modules/attachments/i18n/en.json +1 -0
  26. package/src/modules/attachments/i18n/es.json +1 -0
  27. package/src/modules/attachments/i18n/pl.json +1 -0
  28. package/src/modules/catalog/api/settings/route.ts +165 -0
  29. package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
  30. package/src/modules/catalog/components/UnitPriceDisplaySettings.tsx +106 -0
  31. package/src/modules/catalog/components/products/ProductUomSection.tsx +4 -0
  32. package/src/modules/catalog/components/products/hooks/useUnitPriceDisplayEnabled.ts +39 -0
  33. package/src/modules/catalog/i18n/de.json +8 -0
  34. package/src/modules/catalog/i18n/en.json +8 -0
  35. package/src/modules/catalog/i18n/es.json +8 -0
  36. package/src/modules/catalog/i18n/pl.json +8 -0
  37. package/src/modules/catalog/lib/settings.ts +8 -0
  38. package/src/modules/customers/api/pipeline-stages/route.ts +6 -4
  39. package/src/modules/customers/api/pipelines/route.ts +5 -3
  40. package/src/modules/staff/api/team-members/assignable/route.ts +8 -12
@@ -614,6 +614,14 @@
614
614
  "catalog.search.priceKind.promotion": "Promoción",
615
615
  "catalog.search.status.inactive": "Inactivo",
616
616
  "catalog.search.variant.default": "Predeterminado",
617
+ "catalog.settings.loading": "Cargando…",
618
+ "catalog.settings.messages.saved": "Ajustes guardados.",
619
+ "catalog.settings.unitPriceDisplay.description": "Controla si la función de presentación del precio unitario de la UE está disponible en el formulario de producto.",
620
+ "catalog.settings.unitPriceDisplay.errors.load": "No se pudieron cargar los ajustes del catálogo.",
621
+ "catalog.settings.unitPriceDisplay.errors.save": "No se pudieron guardar los ajustes del catálogo.",
622
+ "catalog.settings.unitPriceDisplay.title": "Presentación del precio unitario",
623
+ "catalog.settings.unitPriceDisplay.toggleDescription": "Cuando está desactivado, los ajustes del precio unitario de la UE se ocultan en todos los formularios de producto. Desactívalo para fabricantes u otros inquilinos que no venden a consumidores.",
624
+ "catalog.settings.unitPriceDisplay.toggleLabel": "Activar la presentación del precio unitario de la UE",
617
625
  "catalog.stats.active": "Activos",
618
626
  "catalog.stats.categories": "Categorías",
619
627
  "catalog.stats.products": "Productos",
@@ -614,6 +614,14 @@
614
614
  "catalog.search.priceKind.promotion": "Promocja",
615
615
  "catalog.search.status.inactive": "Nieaktywny",
616
616
  "catalog.search.variant.default": "Domyślny",
617
+ "catalog.settings.loading": "Ładowanie…",
618
+ "catalog.settings.messages.saved": "Ustawienia zapisane.",
619
+ "catalog.settings.unitPriceDisplay.description": "Określa, czy funkcja prezentacji ceny jednostkowej UE jest dostępna w formularzu produktu.",
620
+ "catalog.settings.unitPriceDisplay.errors.load": "Nie udało się wczytać ustawień katalogu.",
621
+ "catalog.settings.unitPriceDisplay.errors.save": "Nie udało się zapisać ustawień katalogu.",
622
+ "catalog.settings.unitPriceDisplay.title": "Prezentacja ceny jednostkowej",
623
+ "catalog.settings.unitPriceDisplay.toggleDescription": "Gdy wyłączone, ustawienia ceny jednostkowej UE są ukryte w każdym formularzu produktu. Wyłącz tę opcję dla firm produkcyjnych lub innych najemców, którzy nie sprzedają konsumentom.",
624
+ "catalog.settings.unitPriceDisplay.toggleLabel": "Włącz prezentację ceny jednostkowej UE",
617
625
  "catalog.stats.active": "Aktywne",
618
626
  "catalog.stats.categories": "Kategorie",
619
627
  "catalog.stats.products": "Produkty",
@@ -0,0 +1,8 @@
1
+ export const CATALOG_SETTINGS_MODULE_ID = 'catalog'
2
+
3
+ // Controls whether the EU unit-price presentation feature is exposed at all.
4
+ // Default ON so existing tenants keep the current behavior; manufacturers or
5
+ // other non-retail tenants can turn it off to remove the settings from the
6
+ // product form entirely.
7
+ export const UNIT_PRICE_DISPLAY_ENABLED_KEY = 'unit_price_display_enabled'
8
+ export const UNIT_PRICE_DISPLAY_ENABLED_DEFAULT = true
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
6
+ import { resolveOrganizationScopeFilter } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'
6
7
  import type { EntityManager } from '@mikro-orm/postgresql'
7
8
  import type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'
8
9
  import { CustomerPipelineStage, CustomerDictionaryEntry } from '../../data/entities'
@@ -56,15 +57,16 @@ async function buildContext(
56
57
 
57
58
  export async function GET(req: Request) {
58
59
  try {
59
- const { ctx, organizationId, tenantId, translate } = await buildContext(req)
60
- if (!organizationId || !tenantId) {
60
+ const { ctx, tenantId, translate } = await buildContext(req)
61
+ if (!tenantId) {
61
62
  return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })
62
63
  }
64
+ const orgFilter = resolveOrganizationScopeFilter(ctx.organizationScope, ctx.auth)
63
65
  const url = new URL(req.url)
64
66
  const pipelineId = url.searchParams.get('pipelineId')
65
67
 
66
68
  const em = (ctx.container.resolve('em') as EntityManager)
67
- const where: Record<string, unknown> = { organizationId, tenantId }
69
+ const where: Record<string, unknown> = { tenantId, ...orgFilter.where }
68
70
  if (pipelineId) where.pipelineId = pipelineId
69
71
 
70
72
  const stages = await em.find(CustomerPipelineStage, where, { orderBy: { order: 'ASC' } })
@@ -72,8 +74,8 @@ export async function GET(req: Request) {
72
74
  const stageLabels = stages.map((s) => s.label.trim().toLowerCase())
73
75
  const dictEntries = stageLabels.length
74
76
  ? await em.find(CustomerDictionaryEntry, {
75
- organizationId,
76
77
  tenantId,
78
+ ...orgFilter.where,
77
79
  kind: 'pipeline_stage',
78
80
  normalizedValue: { $in: stageLabels },
79
81
  })
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
6
+ import { resolveOrganizationScopeFilter } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'
6
7
  import type { EntityManager } from '@mikro-orm/postgresql'
7
8
  import type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'
8
9
  import { CustomerPipeline } from '../../data/entities'
@@ -56,15 +57,16 @@ async function buildContext(
56
57
 
57
58
  export async function GET(req: Request) {
58
59
  try {
59
- const { ctx, organizationId, tenantId, translate } = await buildContext(req)
60
- if (!organizationId || !tenantId) {
60
+ const { ctx, tenantId, translate } = await buildContext(req)
61
+ if (!tenantId) {
61
62
  return NextResponse.json({ error: translate('customers.errors.context_required', 'Organization and tenant context required') }, { status: 400 })
62
63
  }
64
+ const orgFilter = resolveOrganizationScopeFilter(ctx.organizationScope, ctx.auth)
63
65
  const url = new URL(req.url)
64
66
  const isDefaultParam = url.searchParams.get('isDefault')
65
67
 
66
68
  const em = (ctx.container.resolve('em') as EntityManager)
67
- const where: Record<string, unknown> = { organizationId, tenantId }
69
+ const where: Record<string, unknown> = { tenantId, ...orgFilter.where }
68
70
  if (isDefaultParam === 'true') where.isDefault = true
69
71
  if (isDefaultParam === 'false') where.isDefault = false
70
72
 
@@ -6,6 +6,7 @@ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
7
  import { createPagedListResponseSchema as createSharedPagedListResponseSchema } from '@open-mercato/shared/lib/openapi/crud'
8
8
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
9
+ import { resolveOrganizationScopeFilter } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'
9
10
  import { User } from '@open-mercato/core/modules/auth/data/entities'
10
11
  import {
11
12
  resolveAuthActorId,
@@ -57,7 +58,7 @@ export const metadata = {
57
58
  async function canAccessAssignableStaff(
58
59
  rbac: RbacService | undefined,
59
60
  userId: string,
60
- scope: { tenantId: string; organizationId: string },
61
+ scope: { tenantId: string; organizationId: string | null },
61
62
  ): Promise<boolean> {
62
63
  if (!rbac) return false
63
64
  if (
@@ -72,18 +73,13 @@ export async function GET(request: Request) {
72
73
  const { translate } = await resolveTranslations()
73
74
  try {
74
75
  const query = querySchema.parse(Object.fromEntries(new URL(request.url).searchParams))
75
- const { container, em, auth, selectedOrganizationId } = await resolveCustomersRequestContext(request)
76
+ const { container, em, auth, scope: organizationScope } = await resolveCustomersRequestContext(request)
76
77
 
77
- if (!selectedOrganizationId) {
78
- throw new CrudHttpError(
79
- 400,
80
- { error: translate('customers.errors.organization_required', 'Organization context is required') },
81
- )
82
- }
78
+ const orgFilter = resolveOrganizationScopeFilter(organizationScope, auth)
83
79
 
84
80
  const actorId = resolveAuthActorId(auth)
85
81
  const rbacService = container.resolve('rbacService') as RbacService | undefined
86
- const scope = { tenantId: auth.tenantId, organizationId: selectedOrganizationId }
82
+ const scope = { tenantId: auth.tenantId, organizationId: orgFilter.rbacOrganizationId }
87
83
  const hasAccess = await canAccessAssignableStaff(rbacService, actorId, scope)
88
84
  if (!hasAccess) {
89
85
  throw new CrudHttpError(
@@ -104,7 +100,7 @@ export async function GET(request: Request) {
104
100
  StaffTeamMember,
105
101
  {
106
102
  tenantId: auth.tenantId,
107
- organizationId: selectedOrganizationId,
103
+ ...orgFilter.where,
108
104
  deletedAt: null,
109
105
  isActive: true,
110
106
  },
@@ -136,7 +132,7 @@ export async function GET(request: Request) {
136
132
  id: { $in: userIds },
137
133
  deletedAt: null,
138
134
  tenantId: auth.tenantId,
139
- organizationId: selectedOrganizationId,
135
+ ...orgFilter.where,
140
136
  },
141
137
  undefined,
142
138
  scope,
@@ -150,7 +146,7 @@ export async function GET(request: Request) {
150
146
  id: { $in: teamIds },
151
147
  deletedAt: null,
152
148
  tenantId: auth.tenantId,
153
- organizationId: selectedOrganizationId,
149
+ ...orgFilter.where,
154
150
  },
155
151
  undefined,
156
152
  scope,