@open-mercato/core 0.6.4-develop.4239.1.4a264a5828 → 0.6.4-develop.4264.1.53368d85fe

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 (87) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/authFixtures.js +70 -1
  3. package/dist/helpers/integration/authFixtures.js.map +2 -2
  4. package/dist/helpers/integration/dbFixtures.js +98 -0
  5. package/dist/helpers/integration/dbFixtures.js.map +7 -0
  6. package/dist/modules/business_rules/api/execute/route.js +2 -1
  7. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  8. package/dist/modules/business_rules/api/rules/route.js +10 -0
  9. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  10. package/dist/modules/business_rules/cli.js +6 -0
  11. package/dist/modules/business_rules/cli.js.map +2 -2
  12. package/dist/modules/business_rules/lib/rule-engine.js +116 -9
  13. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  14. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -2
  15. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
  16. package/dist/modules/catalog/api/products/route.js +21 -4
  17. package/dist/modules/catalog/api/products/route.js.map +2 -2
  18. package/dist/modules/catalog/lib/pricing.js +6 -0
  19. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  20. package/dist/modules/catalog/services/catalogPricingService.js +5 -1
  21. package/dist/modules/catalog/services/catalogPricingService.js.map +2 -2
  22. package/dist/modules/customer_accounts/api/portal/events/stream.js +1 -0
  23. package/dist/modules/customer_accounts/api/portal/events/stream.js.map +2 -2
  24. package/dist/modules/customers/api/activities/route.js +15 -2
  25. package/dist/modules/customers/api/activities/route.js.map +2 -2
  26. package/dist/modules/customers/api/comments/route.js +15 -3
  27. package/dist/modules/customers/api/comments/route.js.map +2 -2
  28. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -4
  29. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  30. package/dist/modules/customers/api/companies/[id]/route.js +2 -4
  31. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  32. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -4
  33. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  34. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -4
  35. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  36. package/dist/modules/customers/api/deals/[id]/route.js +2 -9
  37. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  38. package/dist/modules/customers/api/deals/[id]/stats/route.js +2 -9
  39. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  40. package/dist/modules/customers/api/entity-roles-factory.js +2 -8
  41. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  42. package/dist/modules/customers/api/people/[id]/companies/context.js +2 -4
  43. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  44. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -4
  45. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  46. package/dist/modules/customers/api/people/[id]/route.js +2 -4
  47. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  48. package/dist/modules/directory/utils/organizationScopeGuard.js +22 -0
  49. package/dist/modules/directory/utils/organizationScopeGuard.js.map +7 -0
  50. package/dist/modules/workflows/cli.js +8 -0
  51. package/dist/modules/workflows/cli.js.map +2 -2
  52. package/dist/modules/workflows/lib/seeds.js +8 -4
  53. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  54. package/dist/modules/workflows/setup.js +3 -1
  55. package/dist/modules/workflows/setup.js.map +2 -2
  56. package/package.json +7 -7
  57. package/src/helpers/integration/authFixtures.ts +98 -0
  58. package/src/helpers/integration/dbFixtures.ts +144 -0
  59. package/src/modules/business_rules/api/execute/route.ts +2 -1
  60. package/src/modules/business_rules/api/rules/route.ts +10 -0
  61. package/src/modules/business_rules/cli.ts +6 -0
  62. package/src/modules/business_rules/lib/rule-engine.ts +163 -9
  63. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +3 -2
  64. package/src/modules/catalog/api/products/route.ts +23 -4
  65. package/src/modules/catalog/lib/pricing.ts +9 -0
  66. package/src/modules/catalog/services/catalogPricingService.ts +6 -0
  67. package/src/modules/customer_accounts/api/portal/events/stream.ts +6 -0
  68. package/src/modules/customers/api/activities/route.ts +16 -5
  69. package/src/modules/customers/api/comments/route.ts +15 -5
  70. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -4
  71. package/src/modules/customers/api/companies/[id]/route.ts +2 -5
  72. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -4
  73. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -4
  74. package/src/modules/customers/api/deals/[id]/route.ts +2 -9
  75. package/src/modules/customers/api/deals/[id]/stats/route.ts +2 -9
  76. package/src/modules/customers/api/entity-roles-factory.ts +2 -12
  77. package/src/modules/customers/api/people/[id]/companies/context.ts +2 -5
  78. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -5
  79. package/src/modules/customers/api/people/[id]/route.ts +2 -5
  80. package/src/modules/directory/utils/organizationScopeGuard.ts +39 -0
  81. package/src/modules/staff/i18n/de.json +23 -0
  82. package/src/modules/staff/i18n/en.json +23 -0
  83. package/src/modules/staff/i18n/es.json +23 -0
  84. package/src/modules/staff/i18n/pl.json +23 -0
  85. package/src/modules/workflows/cli.ts +8 -0
  86. package/src/modules/workflows/lib/seeds.ts +13 -3
  87. package/src/modules/workflows/setup.ts +3 -1
@@ -37,6 +37,7 @@ import type { EntityId } from '@open-mercato/shared/modules/entities'
37
37
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
38
38
  import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
39
39
  import { parseBooleanFromUnknown, parseBooleanToken } from '@open-mercato/shared/lib/boolean'
40
+ import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
40
41
  import { loadPersonCompanyLinks, summarizePersonCompanies } from '../../../lib/personCompanies'
41
42
  import { normalizeCustomerDetailCustomFields } from '../../detailCustomFields'
42
43
 
@@ -474,11 +475,7 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
474
475
  profileMeta = { reason: 'person_tenant_mismatch' }
475
476
  return notFound('Person not found')
476
477
  }
477
- const allowedOrgIds = new Set<string>()
478
- if (scope?.filterIds?.length) scope.filterIds.forEach((id) => allowedOrgIds.add(id))
479
- else if (auth.orgId) allowedOrgIds.add(auth.orgId)
480
-
481
- if (allowedOrgIds.size && person.organizationId && !allowedOrgIds.has(person.organizationId)) {
478
+ if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
482
479
  statusCode = 403
483
480
  profileMeta = { reason: 'organization_forbidden' }
484
481
  return forbidden('Access denied')
@@ -0,0 +1,39 @@
1
+ import type { AuthContext } from '@open-mercato/shared/lib/auth/server'
2
+ import { isOrganizationAccessAllowed } from '@open-mercato/shared/lib/auth/organizationAccess'
3
+ import type { OrganizationScope } from './organizationScope'
4
+
5
+ export type OrganizationReadAccessInput = {
6
+ scope: OrganizationScope | null | undefined
7
+ auth: AuthContext
8
+ organizationId: string | null
9
+ }
10
+
11
+ /**
12
+ * Fail-closed read guard for single-record detail routes. Centralizes the
13
+ * decision so callers keep their own deny mechanism (throw / return response)
14
+ * and their own i18n key.
15
+ *
16
+ * Unrestricted access (super admin or `scope.allowedIds === null`) is the only
17
+ * bypass. For a restricted principal the allowed set is derived the same way
18
+ * the detail routes always have (`filterIds` narrows the active view, else the
19
+ * principal's home org); an empty derived set denies instead of skipping.
20
+ */
21
+ export function isOrganizationReadAccessAllowed(input: OrganizationReadAccessInput): boolean {
22
+ const isSuperAdmin = input.auth?.isSuperAdmin === true
23
+ if (isSuperAdmin || input.scope?.allowedIds === null) return true
24
+
25
+ const allowedOrganizationIds = new Set<string>()
26
+ if (input.scope?.filterIds?.length) {
27
+ for (const id of input.scope.filterIds) {
28
+ if (typeof id === 'string' && id.trim().length) allowedOrganizationIds.add(id)
29
+ }
30
+ } else if (input.auth?.orgId) {
31
+ allowedOrganizationIds.add(input.auth.orgId)
32
+ }
33
+
34
+ return isOrganizationAccessAllowed({
35
+ isSuperAdmin,
36
+ allowedOrganizationIds: Array.from(allowedOrganizationIds),
37
+ targetOrganizationId: input.organizationId,
38
+ })
39
+ }
@@ -118,6 +118,14 @@
118
118
  "staff.audit.teams.create": "Team erstellen",
119
119
  "staff.audit.teams.delete": "Team löschen",
120
120
  "staff.audit.teams.update": "Team aktualisieren",
121
+ "staff.audit.timesheets.time_entries.create": "Zeiteintrag erstellen",
122
+ "staff.audit.timesheets.time_entries.delete": "Zeiteintrag löschen",
123
+ "staff.audit.timesheets.time_entries.update": "Zeiteintrag aktualisieren",
124
+ "staff.audit.timesheets.time_project_members.assign": "Zeitprojektmitglied zuweisen",
125
+ "staff.audit.timesheets.time_project_members.unassign": "Zeitprojektmitglied entfernen",
126
+ "staff.audit.timesheets.time_projects.create": "Zeitprojekt erstellen",
127
+ "staff.audit.timesheets.time_projects.delete": "Zeitprojekt löschen",
128
+ "staff.audit.timesheets.time_projects.update": "Zeitprojekt aktualisieren",
121
129
  "staff.availability.errors.organizationRequired": "Organisationskontext ist erforderlich.",
122
130
  "staff.availability.errors.unauthorized": "Nicht autorisiert",
123
131
  "staff.availability.errors.updateDateSpecific": "Datumsspezifische Verfügbarkeit konnte nicht gespeichert werden.",
@@ -202,6 +210,7 @@
202
210
  "staff.availabilityRuleSets.tabs.availability": "Verfügbarkeit",
203
211
  "staff.availabilityRuleSets.tabs.details": "Details",
204
212
  "staff.availabilityRuleSets.tabs.label": "Zeitplanbereiche",
213
+ "staff.errors.missingScope": "Mandanten- oder Organisationskontext fehlt.",
205
214
  "staff.errors.unauthorized": "Nicht autorisiert",
206
215
  "staff.leaveRequests.actions.accept": "Genehmigen",
207
216
  "staff.leaveRequests.actions.add": "Neuer Antrag",
@@ -413,6 +422,7 @@
413
422
  "staff.search.badge.team": "Team",
414
423
  "staff.search.badge.teamMember": "Teammitglied",
415
424
  "staff.search.badge.teamRole": "Teamrolle",
425
+ "staff.search.badge.timeProject": "Projekt",
416
426
  "staff.search.service.maxAttendees": "Max. {{count}}",
417
427
  "staff.search.status.active": "Aktiv",
418
428
  "staff.search.status.inactive": "Inaktiv",
@@ -918,13 +928,26 @@
918
928
  "staff.teams.tabs.details": "Details",
919
929
  "staff.teams.tabs.label": "Teamabschnitte",
920
930
  "staff.teams.tabs.members": "Teammitglieder",
931
+ "staff.timesheets.errors.bulkSave": "Zeiteinträge konnten nicht im Stapel gespeichert werden.",
921
932
  "staff.timesheets.errors.entryNotFound": "Zeiteintrag nicht gefunden, gelöscht oder Ihnen nicht zugeordnet.",
933
+ "staff.timesheets.errors.invalidBody": "Ungültiger Anfragetext.",
934
+ "staff.timesheets.errors.invalidProjectId": "Ungültige Projekt-ID.",
935
+ "staff.timesheets.errors.memberRequired": "Die ID des Zeitprojektmitglieds ist erforderlich.",
936
+ "staff.timesheets.errors.missingEntryId": "Eintrags-ID fehlt.",
937
+ "staff.timesheets.errors.myProjects": "Ihre Projekte konnten nicht geladen werden.",
938
+ "staff.timesheets.errors.noActiveSegment": "Für diesen Eintrag wurde kein aktives Timer-Segment gefunden.",
922
939
  "staff.timesheets.errors.noStaffMember": "Ihrem Konto ist kein Mitarbeiter zugeordnet.",
940
+ "staff.timesheets.errors.notAssigned": "Sie sind diesem Projekt nicht zugewiesen.",
923
941
  "staff.timesheets.errors.notOwner": "Sie können nur Ihre eigenen Zeiteinträge verwalten.",
924
942
  "staff.timesheets.errors.projectCodeDuplicate": "Ein Projekt mit diesem Code existiert bereits.",
925
943
  "staff.timesheets.errors.projectNotFound": "Zeitprojekt nicht gefunden oder nicht zugänglich.",
926
944
  "staff.timesheets.errors.projectsKpis": "Failed to load project KPIs.",
945
+ "staff.timesheets.errors.segmentCreate": "Zeiteintragssegment konnte nicht erstellt werden.",
927
946
  "staff.timesheets.errors.staffMemberNotFound": "Mitarbeiter nicht gefunden oder nicht zugänglich.",
947
+ "staff.timesheets.errors.timerAlreadyStarted": "Der Timer ist für diesen Eintrag bereits gestartet.",
948
+ "staff.timesheets.errors.timerStart": "Timer konnte nicht gestartet werden.",
949
+ "staff.timesheets.errors.timerStop": "Timer konnte nicht gestoppt werden.",
950
+ "staff.timesheets.errors.updateMyProject": "Projektsichtbarkeit konnte nicht aktualisiert werden.",
928
951
  "staff.timesheets.my.addRow.createProject": "Create a new project",
929
952
  "staff.timesheets.my.addRow.error": "Das Projekt konnte nicht hinzugefügt werden. Bitte erneut versuchen.",
930
953
  "staff.timesheets.my.addRow.noProjects": "No projects assigned",
@@ -118,6 +118,14 @@
118
118
  "staff.audit.teams.create": "Create team",
119
119
  "staff.audit.teams.delete": "Delete team",
120
120
  "staff.audit.teams.update": "Update team",
121
+ "staff.audit.timesheets.time_entries.create": "Create time entry",
122
+ "staff.audit.timesheets.time_entries.delete": "Delete time entry",
123
+ "staff.audit.timesheets.time_entries.update": "Update time entry",
124
+ "staff.audit.timesheets.time_project_members.assign": "Assign time project member",
125
+ "staff.audit.timesheets.time_project_members.unassign": "Unassign time project member",
126
+ "staff.audit.timesheets.time_projects.create": "Create time project",
127
+ "staff.audit.timesheets.time_projects.delete": "Delete time project",
128
+ "staff.audit.timesheets.time_projects.update": "Update time project",
121
129
  "staff.availability.errors.organizationRequired": "Organization context is required.",
122
130
  "staff.availability.errors.unauthorized": "Unauthorized",
123
131
  "staff.availability.errors.updateDateSpecific": "Failed to save date-specific availability.",
@@ -202,6 +210,7 @@
202
210
  "staff.availabilityRuleSets.tabs.availability": "Availability",
203
211
  "staff.availabilityRuleSets.tabs.details": "Details",
204
212
  "staff.availabilityRuleSets.tabs.label": "Schedule sections",
213
+ "staff.errors.missingScope": "Missing tenant or organization scope.",
205
214
  "staff.errors.unauthorized": "Unauthorized",
206
215
  "staff.leaveRequests.actions.accept": "Approve",
207
216
  "staff.leaveRequests.actions.add": "New request",
@@ -413,6 +422,7 @@
413
422
  "staff.search.badge.team": "Team",
414
423
  "staff.search.badge.teamMember": "Team member",
415
424
  "staff.search.badge.teamRole": "Team role",
425
+ "staff.search.badge.timeProject": "Project",
416
426
  "staff.search.service.maxAttendees": "Max {{count}}",
417
427
  "staff.search.status.active": "Active",
418
428
  "staff.search.status.inactive": "Inactive",
@@ -918,13 +928,26 @@
918
928
  "staff.teams.tabs.details": "Details",
919
929
  "staff.teams.tabs.label": "Team sections",
920
930
  "staff.teams.tabs.members": "Team members",
931
+ "staff.timesheets.errors.bulkSave": "Failed to bulk save time entries.",
921
932
  "staff.timesheets.errors.entryNotFound": "Time entry not found, deleted, or not owned by you.",
933
+ "staff.timesheets.errors.invalidBody": "Invalid request body.",
934
+ "staff.timesheets.errors.invalidProjectId": "Invalid project id.",
935
+ "staff.timesheets.errors.memberRequired": "Time project member id is required.",
936
+ "staff.timesheets.errors.missingEntryId": "Missing entry ID.",
937
+ "staff.timesheets.errors.myProjects": "Failed to load your projects.",
938
+ "staff.timesheets.errors.noActiveSegment": "No active timer segment found for this entry.",
922
939
  "staff.timesheets.errors.noStaffMember": "No staff member linked to your account.",
940
+ "staff.timesheets.errors.notAssigned": "You are not assigned to this project.",
923
941
  "staff.timesheets.errors.notOwner": "You can only manage your own time entries.",
924
942
  "staff.timesheets.errors.projectCodeDuplicate": "A project with this code already exists.",
925
943
  "staff.timesheets.errors.projectNotFound": "Time project not found or not accessible.",
926
944
  "staff.timesheets.errors.projectsKpis": "Failed to load project KPIs.",
945
+ "staff.timesheets.errors.segmentCreate": "Failed to create time entry segment.",
927
946
  "staff.timesheets.errors.staffMemberNotFound": "Staff member not found or not accessible.",
947
+ "staff.timesheets.errors.timerAlreadyStarted": "Timer is already started for this entry.",
948
+ "staff.timesheets.errors.timerStart": "Failed to start timer.",
949
+ "staff.timesheets.errors.timerStop": "Failed to stop timer.",
950
+ "staff.timesheets.errors.updateMyProject": "Failed to update project visibility.",
928
951
  "staff.timesheets.my.addRow.createProject": "Create a new project",
929
952
  "staff.timesheets.my.addRow.error": "Could not add the project. Please try again.",
930
953
  "staff.timesheets.my.addRow.noProjects": "No projects assigned",
@@ -118,6 +118,14 @@
118
118
  "staff.audit.teams.create": "Crear equipo",
119
119
  "staff.audit.teams.delete": "Eliminar equipo",
120
120
  "staff.audit.teams.update": "Actualizar equipo",
121
+ "staff.audit.timesheets.time_entries.create": "Crear registro de tiempo",
122
+ "staff.audit.timesheets.time_entries.delete": "Eliminar registro de tiempo",
123
+ "staff.audit.timesheets.time_entries.update": "Actualizar registro de tiempo",
124
+ "staff.audit.timesheets.time_project_members.assign": "Asignar miembro del proyecto de tiempo",
125
+ "staff.audit.timesheets.time_project_members.unassign": "Desasignar miembro del proyecto de tiempo",
126
+ "staff.audit.timesheets.time_projects.create": "Crear proyecto de tiempo",
127
+ "staff.audit.timesheets.time_projects.delete": "Eliminar proyecto de tiempo",
128
+ "staff.audit.timesheets.time_projects.update": "Actualizar proyecto de tiempo",
121
129
  "staff.availability.errors.organizationRequired": "Se requiere el contexto de la organización.",
122
130
  "staff.availability.errors.unauthorized": "No autorizado",
123
131
  "staff.availability.errors.updateDateSpecific": "No se pudo guardar la disponibilidad por fecha.",
@@ -202,6 +210,7 @@
202
210
  "staff.availabilityRuleSets.tabs.availability": "Disponibilidad",
203
211
  "staff.availabilityRuleSets.tabs.details": "Detalles",
204
212
  "staff.availabilityRuleSets.tabs.label": "Secciones del horario",
213
+ "staff.errors.missingScope": "Falta el contexto de inquilino u organización.",
205
214
  "staff.errors.unauthorized": "No autorizado",
206
215
  "staff.leaveRequests.actions.accept": "Aprobar",
207
216
  "staff.leaveRequests.actions.add": "Nueva solicitud",
@@ -413,6 +422,7 @@
413
422
  "staff.search.badge.team": "Equipo",
414
423
  "staff.search.badge.teamMember": "Miembro del equipo",
415
424
  "staff.search.badge.teamRole": "Rol del equipo",
425
+ "staff.search.badge.timeProject": "Proyecto",
416
426
  "staff.search.service.maxAttendees": "Máx. {{count}}",
417
427
  "staff.search.status.active": "Activo",
418
428
  "staff.search.status.inactive": "Inactivo",
@@ -918,13 +928,26 @@
918
928
  "staff.teams.tabs.details": "Detalles",
919
929
  "staff.teams.tabs.label": "Secciones del equipo",
920
930
  "staff.teams.tabs.members": "Miembros del equipo",
931
+ "staff.timesheets.errors.bulkSave": "No se pudieron guardar en lote los registros de tiempo.",
921
932
  "staff.timesheets.errors.entryNotFound": "Registro de tiempo no encontrado, eliminado o no asignado a ti.",
933
+ "staff.timesheets.errors.invalidBody": "Cuerpo de la solicitud no válido.",
934
+ "staff.timesheets.errors.invalidProjectId": "Identificador de proyecto no válido.",
935
+ "staff.timesheets.errors.memberRequired": "El identificador del miembro del proyecto de tiempo es obligatorio.",
936
+ "staff.timesheets.errors.missingEntryId": "Falta el identificador del registro.",
937
+ "staff.timesheets.errors.myProjects": "No se pudieron cargar tus proyectos.",
938
+ "staff.timesheets.errors.noActiveSegment": "No se encontró ningún segmento de temporizador activo para este registro.",
922
939
  "staff.timesheets.errors.noStaffMember": "Ningún miembro del personal vinculado a tu cuenta.",
940
+ "staff.timesheets.errors.notAssigned": "No estás asignado a este proyecto.",
923
941
  "staff.timesheets.errors.notOwner": "Solo puedes gestionar tus propios registros de tiempo.",
924
942
  "staff.timesheets.errors.projectCodeDuplicate": "Ya existe un proyecto con este código.",
925
943
  "staff.timesheets.errors.projectNotFound": "Proyecto de tiempo no encontrado o no accesible.",
926
944
  "staff.timesheets.errors.projectsKpis": "Failed to load project KPIs.",
945
+ "staff.timesheets.errors.segmentCreate": "No se pudo crear el segmento del registro de tiempo.",
927
946
  "staff.timesheets.errors.staffMemberNotFound": "Miembro del personal no encontrado o no accesible.",
947
+ "staff.timesheets.errors.timerAlreadyStarted": "El temporizador ya está iniciado para este registro.",
948
+ "staff.timesheets.errors.timerStart": "No se pudo iniciar el temporizador.",
949
+ "staff.timesheets.errors.timerStop": "No se pudo detener el temporizador.",
950
+ "staff.timesheets.errors.updateMyProject": "No se pudo actualizar la visibilidad del proyecto.",
928
951
  "staff.timesheets.my.addRow.createProject": "Create a new project",
929
952
  "staff.timesheets.my.addRow.error": "No se pudo añadir el proyecto. Inténtalo de nuevo.",
930
953
  "staff.timesheets.my.addRow.noProjects": "No projects assigned",
@@ -118,6 +118,14 @@
118
118
  "staff.audit.teams.create": "Utwórz zespół",
119
119
  "staff.audit.teams.delete": "Usuń zespół",
120
120
  "staff.audit.teams.update": "Zaktualizuj zespół",
121
+ "staff.audit.timesheets.time_entries.create": "Utwórz wpis czasu",
122
+ "staff.audit.timesheets.time_entries.delete": "Usuń wpis czasu",
123
+ "staff.audit.timesheets.time_entries.update": "Zaktualizuj wpis czasu",
124
+ "staff.audit.timesheets.time_project_members.assign": "Przypisz członka projektu czasu",
125
+ "staff.audit.timesheets.time_project_members.unassign": "Usuń przypisanie członka projektu czasu",
126
+ "staff.audit.timesheets.time_projects.create": "Utwórz projekt czasu",
127
+ "staff.audit.timesheets.time_projects.delete": "Usuń projekt czasu",
128
+ "staff.audit.timesheets.time_projects.update": "Zaktualizuj projekt czasu",
121
129
  "staff.availability.errors.organizationRequired": "Wymagany jest kontekst organizacji.",
122
130
  "staff.availability.errors.unauthorized": "Brak autoryzacji",
123
131
  "staff.availability.errors.updateDateSpecific": "Nie udało się zapisać dostępności dla dat.",
@@ -202,6 +210,7 @@
202
210
  "staff.availabilityRuleSets.tabs.availability": "Dostępność",
203
211
  "staff.availabilityRuleSets.tabs.details": "Szczegóły",
204
212
  "staff.availabilityRuleSets.tabs.label": "Sekcje harmonogramu",
213
+ "staff.errors.missingScope": "Brak kontekstu najemcy lub organizacji.",
205
214
  "staff.errors.unauthorized": "Brak autoryzacji",
206
215
  "staff.leaveRequests.actions.accept": "Zatwierdź",
207
216
  "staff.leaveRequests.actions.add": "Nowy wniosek",
@@ -413,6 +422,7 @@
413
422
  "staff.search.badge.team": "Zespół",
414
423
  "staff.search.badge.teamMember": "Członek zespołu",
415
424
  "staff.search.badge.teamRole": "Rola zespołu",
425
+ "staff.search.badge.timeProject": "Projekt",
416
426
  "staff.search.service.maxAttendees": "Maks. {{count}}",
417
427
  "staff.search.status.active": "Aktywny",
418
428
  "staff.search.status.inactive": "Nieaktywny",
@@ -918,13 +928,26 @@
918
928
  "staff.teams.tabs.details": "Szczegóły",
919
929
  "staff.teams.tabs.label": "Sekcje zespołu",
920
930
  "staff.teams.tabs.members": "Członkowie zespołu",
931
+ "staff.timesheets.errors.bulkSave": "Nie udało się zbiorczo zapisać wpisów czasu.",
921
932
  "staff.timesheets.errors.entryNotFound": "Wpis czasu nie istnieje, został usunięty lub nie należy do Ciebie.",
933
+ "staff.timesheets.errors.invalidBody": "Nieprawidłowe ciało żądania.",
934
+ "staff.timesheets.errors.invalidProjectId": "Nieprawidłowy identyfikator projektu.",
935
+ "staff.timesheets.errors.memberRequired": "Identyfikator członka projektu czasu jest wymagany.",
936
+ "staff.timesheets.errors.missingEntryId": "Brak identyfikatora wpisu.",
937
+ "staff.timesheets.errors.myProjects": "Nie udało się załadować Twoich projektów.",
938
+ "staff.timesheets.errors.noActiveSegment": "Nie znaleziono aktywnego segmentu czasomierza dla tego wpisu.",
922
939
  "staff.timesheets.errors.noStaffMember": "Brak pracownika powiązanego z Twoim kontem.",
940
+ "staff.timesheets.errors.notAssigned": "Nie jesteś przypisany do tego projektu.",
923
941
  "staff.timesheets.errors.notOwner": "Możesz zarządzać tylko własnymi wpisami czasu.",
924
942
  "staff.timesheets.errors.projectCodeDuplicate": "Projekt o tym kodzie już istnieje.",
925
943
  "staff.timesheets.errors.projectNotFound": "Projekt czasowy nie został znaleziony lub jest niedostępny.",
926
944
  "staff.timesheets.errors.projectsKpis": "Failed to load project KPIs.",
945
+ "staff.timesheets.errors.segmentCreate": "Nie udało się utworzyć segmentu wpisu czasu.",
927
946
  "staff.timesheets.errors.staffMemberNotFound": "Pracownik nie został znaleziony lub jest niedostępny.",
947
+ "staff.timesheets.errors.timerAlreadyStarted": "Czasomierz jest już uruchomiony dla tego wpisu.",
948
+ "staff.timesheets.errors.timerStart": "Nie udało się uruchomić czasomierza.",
949
+ "staff.timesheets.errors.timerStop": "Nie udało się zatrzymać czasomierza.",
950
+ "staff.timesheets.errors.updateMyProject": "Nie udało się zaktualizować widoczności projektu.",
928
951
  "staff.timesheets.my.addRow.createProject": "Create a new project",
929
952
  "staff.timesheets.my.addRow.error": "Nie udało się dodać projektu. Spróbuj ponownie.",
930
953
  "staff.timesheets.my.addRow.noProjects": "No projects assigned",
@@ -4,6 +4,10 @@ import { getRedisUrl, getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/
4
4
  import type { EntityManager } from '@mikro-orm/postgresql'
5
5
  import { WorkflowDefinition } from './data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ resolveBusinessRuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
  import * as fs from 'fs'
8
12
  import * as path from 'path'
9
13
  import { fileURLToPath } from 'url'
@@ -69,6 +73,7 @@ const seedDemoWithRules: ModuleCli = {
69
73
  try {
70
74
  const { resolve } = await createRequestContainer()
71
75
  const em = resolve<EntityManager>('em')
76
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
72
77
 
73
78
  // Import BusinessRule entity
74
79
  const { BusinessRule } = await import('../business_rules/data/entities')
@@ -100,6 +105,7 @@ const seedDemoWithRules: ModuleCli = {
100
105
  })
101
106
 
102
107
  await em.persist(rule).flush()
108
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
103
109
  console.log(` ✓ Seeded guard rule: ${rule.ruleName}`)
104
110
  seededCount++
105
111
  }
@@ -211,6 +217,7 @@ const seedOrderApproval: ModuleCli = {
211
217
  try {
212
218
  const { resolve } = await createRequestContainer()
213
219
  const em = resolve<EntityManager>('em')
220
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
214
221
 
215
222
  // 1. Seed order approval guard rules first
216
223
  const guardRulesPath = path.join(__dirname, 'examples', 'order-approval-guard-rules.json')
@@ -252,6 +259,7 @@ const seedOrderApproval: ModuleCli = {
252
259
 
253
260
  if (rulesSeeded > 0) {
254
261
  await em.flush()
262
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
255
263
  }
256
264
 
257
265
  console.log(`✅ Seeded order approval guard rules`)
@@ -4,6 +4,10 @@ import * as path from 'path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { WorkflowDefinition, type WorkflowDefinitionData } from '../data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ type RuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
 
8
12
  const __esmDirname = path.dirname(fileURLToPath(import.meta.url))
9
13
 
@@ -130,6 +134,7 @@ async function seedGuardRules(
130
134
  em: EntityManager,
131
135
  scope: WorkflowSeedScope,
132
136
  fileName: string,
137
+ cache?: RuleDiscoveryCache | null,
133
138
  ): Promise<{ seeded: number; skipped: number; updated: number }> {
134
139
  const seeds = readExampleJson<GuardRuleSeed[]>(fileName)
135
140
  if (!Array.isArray(seeds)) {
@@ -185,16 +190,21 @@ async function seedGuardRules(
185
190
  }
186
191
  if (seeded > 0 || updated > 0) {
187
192
  await em.flush()
193
+ await invalidateBusinessRuleDiscoveryCache(cache, scope.tenantId, scope.organizationId)
188
194
  }
189
195
  return { seeded, skipped, updated }
190
196
  }
191
197
 
192
- export async function seedExampleWorkflows(em: EntityManager, scope: WorkflowSeedScope): Promise<void> {
198
+ export async function seedExampleWorkflows(
199
+ em: EntityManager,
200
+ scope: WorkflowSeedScope,
201
+ options: { cache?: RuleDiscoveryCache | null } = {},
202
+ ): Promise<void> {
193
203
  // workflows.checkout-demo and workflows.simple-approval are now code-defined
194
204
  // (see packages/core/src/modules/workflows/workflows.ts). Seeding DB rows for
195
205
  // them would shadow the code definitions in the merge layer, so they are no
196
206
  // longer seeded here. Existing tenants are migrated via Migration20260428102318.
197
- await seedGuardRules(em, scope, 'guard-rules-example.json')
207
+ await seedGuardRules(em, scope, 'guard-rules-example.json', options.cache)
198
208
  await seedWorkflowDefinition(em, scope, 'sales-pipeline-definition.json')
199
- await seedGuardRules(em, scope, 'order-approval-guard-rules.json')
209
+ await seedGuardRules(em, scope, 'order-approval-guard-rules.json', options.cache)
200
210
  }
@@ -1,10 +1,12 @@
1
1
  import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { resolveBusinessRuleDiscoveryCache } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
2
3
  import { seedExampleWorkflows } from './lib/seeds'
3
4
 
4
5
  export const setup: ModuleSetupConfig = {
5
6
  seedDefaults: async (ctx) => {
6
7
  const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
7
- await seedExampleWorkflows(ctx.em, scope)
8
+ const cache = resolveBusinessRuleDiscoveryCache(ctx.container.resolve.bind(ctx.container))
9
+ await seedExampleWorkflows(ctx.em, scope, { cache })
8
10
  },
9
11
 
10
12
  defaultRoleFeatures: {