@open-mercato/core 0.7.0 → 0.7.1-develop.7102.1.b41f7e3e51

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 (847) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/api.js +13 -1
  3. package/dist/helpers/integration/api.js.map +2 -2
  4. package/dist/helpers/integration/communicationChannelsFixtures.js +19 -0
  5. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  6. package/dist/modules/api_keys/api/keys/route.js +1 -1
  7. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  8. package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
  9. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  10. package/dist/modules/api_keys/di.js +9 -0
  11. package/dist/modules/api_keys/di.js.map +7 -0
  12. package/dist/modules/api_keys/services/principalService.js +31 -0
  13. package/dist/modules/api_keys/services/principalService.js.map +7 -0
  14. package/dist/modules/attachments/api/route.js +2 -2
  15. package/dist/modules/attachments/api/route.js.map +2 -2
  16. package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
  17. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  18. package/dist/modules/attachments/di.js +11 -1
  19. package/dist/modules/attachments/di.js.map +2 -2
  20. package/dist/modules/attachments/index.js.map +1 -1
  21. package/dist/modules/attachments/lib/attachment-service.js +313 -0
  22. package/dist/modules/attachments/lib/attachment-service.js.map +7 -0
  23. package/dist/modules/attachments/lib/scoped-upload-service.js +15 -2
  24. package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
  25. package/dist/modules/attachments/lib/upload-limits.js +6 -2
  26. package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
  27. package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
  28. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  29. package/dist/modules/audit_logs/lib/display-helpers.js +19 -11
  30. package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
  31. package/dist/modules/audit_logs/services/actionLogService.js +57 -12
  32. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  33. package/dist/modules/auth/acl.js +30 -5
  34. package/dist/modules/auth/acl.js.map +2 -2
  35. package/dist/modules/auth/api/feature-check.js +8 -2
  36. package/dist/modules/auth/api/feature-check.js.map +2 -2
  37. package/dist/modules/auth/api/login.js +4 -1
  38. package/dist/modules/auth/api/login.js.map +2 -2
  39. package/dist/modules/auth/api/reset/validate.js +55 -0
  40. package/dist/modules/auth/api/reset/validate.js.map +7 -0
  41. package/dist/modules/auth/api/roles/acl/route.js +28 -33
  42. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  43. package/dist/modules/auth/api/session/refresh.js +25 -1
  44. package/dist/modules/auth/api/session/refresh.js.map +2 -2
  45. package/dist/modules/auth/api/users/acl/route.js +96 -51
  46. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  47. package/dist/modules/auth/api/users/route.js +13 -5
  48. package/dist/modules/auth/api/users/route.js.map +2 -2
  49. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
  50. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
  51. package/dist/modules/auth/backend/roles/page.js +3 -1
  52. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  53. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
  54. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
  55. package/dist/modules/auth/backend/users/page.js +2 -1
  56. package/dist/modules/auth/backend/users/page.js.map +2 -2
  57. package/dist/modules/auth/commands/acl.js +254 -0
  58. package/dist/modules/auth/commands/acl.js.map +7 -0
  59. package/dist/modules/auth/components/AclEditor.js +39 -30
  60. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  61. package/dist/modules/auth/data/entities.js.map +2 -2
  62. package/dist/modules/auth/data/validators.js +5 -1
  63. package/dist/modules/auth/data/validators.js.map +2 -2
  64. package/dist/modules/auth/di.js +35 -9
  65. package/dist/modules/auth/di.js.map +2 -2
  66. package/dist/modules/auth/frontend/reset/[token]/page.js +46 -0
  67. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  68. package/dist/modules/auth/lib/grantChecks.js.map +2 -2
  69. package/dist/modules/auth/lib/userIdFilter.js +16 -0
  70. package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
  71. package/dist/modules/auth/services/authService.js +16 -2
  72. package/dist/modules/auth/services/authService.js.map +2 -2
  73. package/dist/modules/auth/services/principalService.js +200 -0
  74. package/dist/modules/auth/services/principalService.js.map +7 -0
  75. package/dist/modules/auth/services/rbacService.js +67 -24
  76. package/dist/modules/auth/services/rbacService.js.map +3 -3
  77. package/dist/modules/auth/services/roleOrganizationScope.js +41 -0
  78. package/dist/modules/auth/services/roleOrganizationScope.js.map +7 -0
  79. package/dist/modules/business_rules/backend/logs/page.js +3 -1
  80. package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
  81. package/dist/modules/business_rules/backend/rules/page.js +3 -1
  82. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  83. package/dist/modules/business_rules/backend/sets/page.js +3 -1
  84. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  85. package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
  86. package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
  87. package/dist/modules/business_rules/lib/rule-evaluator.js +10 -9
  88. package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
  89. package/dist/modules/catalog/ai-agents.js +1 -1
  90. package/dist/modules/catalog/ai-agents.js.map +1 -1
  91. package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
  92. package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
  93. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
  94. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  95. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
  96. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  97. package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
  98. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  99. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
  100. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
  101. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -1
  102. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  103. package/dist/modules/communication_channels/api/post/test-seed/route.js +95 -10
  104. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  105. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
  106. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  107. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +14 -1
  108. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
  109. package/dist/modules/communication_channels/di.js +7 -1
  110. package/dist/modules/communication_channels/di.js.map +2 -2
  111. package/dist/modules/communication_channels/lib/email-capabilities.js +4 -1
  112. package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
  113. package/dist/modules/communication_channels/lib/outbound-recipient.js +33 -0
  114. package/dist/modules/communication_channels/lib/outbound-recipient.js.map +7 -0
  115. package/dist/modules/communication_channels/lib/resolve-channel-type.js +51 -0
  116. package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +7 -0
  117. package/dist/modules/communication_channels/lib/test-seed.js +41 -2
  118. package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
  119. package/dist/modules/configs/lib/system-status.js +9 -0
  120. package/dist/modules/configs/lib/system-status.js.map +2 -2
  121. package/dist/modules/currencies/backend/currencies/page.js +3 -1
  122. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  123. package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
  124. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  125. package/dist/modules/currencies/services/rateFetchingService.js +6 -1
  126. package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
  127. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
  128. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
  129. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
  130. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  131. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +3 -1
  132. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
  133. package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
  134. package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
  135. package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
  136. package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
  137. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +13 -2
  138. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
  139. package/dist/modules/customers/ai-tools/deals-pack.js +52 -6
  140. package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
  141. package/dist/modules/customers/ai-tools/people-pack.js +4 -1
  142. package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
  143. package/dist/modules/customers/api/activities/route.js +48 -6
  144. package/dist/modules/customers/api/activities/route.js.map +2 -2
  145. package/dist/modules/customers/api/companies/[id]/people/route.js +20 -64
  146. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  147. package/dist/modules/customers/api/companies/[id]/route.js +17 -94
  148. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  149. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +8 -5
  150. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
  151. package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
  152. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  153. package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
  154. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  155. package/dist/modules/customers/api/deals/[id]/route.js +8 -25
  156. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  157. package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
  158. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  159. package/dist/modules/customers/api/deals/aggregate/route.js +11 -4
  160. package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
  161. package/dist/modules/customers/api/deals/map/route.js +2 -1
  162. package/dist/modules/customers/api/deals/map/route.js.map +2 -2
  163. package/dist/modules/customers/api/deals/route.js +38 -1
  164. package/dist/modules/customers/api/deals/route.js.map +2 -2
  165. package/dist/modules/customers/api/entity-roles-factory.js +4 -4
  166. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  167. package/dist/modules/customers/api/interactions/route.js +3 -1
  168. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  169. package/dist/modules/customers/api/interactions/tasks/route.js +27 -4
  170. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  171. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
  172. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  173. package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
  174. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  175. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
  176. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  177. package/dist/modules/customers/api/people/[id]/route.js +11 -10
  178. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  179. package/dist/modules/customers/api/people/check-phone/route.js +51 -9
  180. package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
  181. package/dist/modules/customers/api/todos/route.js +30 -5
  182. package/dist/modules/customers/api/todos/route.js.map +2 -2
  183. package/dist/modules/customers/backend/customers/companies/page.js +4 -2
  184. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  185. package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
  186. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  187. package/dist/modules/customers/backend/customers/deals/page.js +5 -2
  188. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  189. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +4 -13
  190. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
  191. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +85 -14
  192. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
  193. package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +18 -0
  194. package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +7 -0
  195. package/dist/modules/customers/backend/customers/people/page.js +4 -2
  196. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  197. package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +9 -3
  198. package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
  199. package/dist/modules/customers/commands/deals.js +21 -34
  200. package/dist/modules/customers/commands/deals.js.map +2 -2
  201. package/dist/modules/customers/commands/interactions.js.map +1 -1
  202. package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
  203. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  204. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  205. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  206. package/dist/modules/customers/components/DealsKpiStrip.js +7 -6
  207. package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
  208. package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
  209. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  210. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
  211. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  212. package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
  213. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
  214. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
  215. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  216. package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
  217. package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
  218. package/dist/modules/customers/components/calendar/EventPeekPopover.js +3 -2
  219. package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
  220. package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
  221. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  222. package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
  223. package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
  224. package/dist/modules/customers/components/calendar/types.js +1 -1
  225. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  226. package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
  227. package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
  228. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
  229. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  230. package/dist/modules/customers/components/detail/DealForm.js +19 -7
  231. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  232. package/dist/modules/customers/components/detail/DealsSection.js +6 -5
  233. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  234. package/dist/modules/customers/components/detail/EntityTagsDialog.js +35 -11
  235. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  236. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
  237. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  238. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +19 -13
  239. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  240. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
  241. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  242. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
  243. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  244. package/dist/modules/customers/components/detail/utils.js +5 -5
  245. package/dist/modules/customers/components/detail/utils.js.map +2 -2
  246. package/dist/modules/customers/data/entities.js.map +1 -1
  247. package/dist/modules/customers/data/validators.js +32 -3
  248. package/dist/modules/customers/data/validators.js.map +2 -2
  249. package/dist/modules/customers/events.js +5 -0
  250. package/dist/modules/customers/events.js.map +2 -2
  251. package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
  252. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  253. package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
  254. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  255. package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
  256. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  257. package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
  258. package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
  259. package/dist/modules/customers/lib/closureStage.js +45 -0
  260. package/dist/modules/customers/lib/closureStage.js.map +7 -0
  261. package/dist/modules/customers/lib/dealStatus.js +30 -0
  262. package/dist/modules/customers/lib/dealStatus.js.map +2 -2
  263. package/dist/modules/customers/lib/detailReadAccess.js +12 -0
  264. package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
  265. package/dist/modules/customers/lib/findPeopleByAddresses.js +1 -0
  266. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
  267. package/dist/modules/customers/lib/personCompanies.js +100 -2
  268. package/dist/modules/customers/lib/personCompanies.js.map +2 -2
  269. package/dist/modules/customers/lib/todoCompatibility.js +12 -0
  270. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  271. package/dist/modules/dashboards/lib/widgetScope.js +6 -1
  272. package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
  273. package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
  274. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  275. package/dist/modules/data_sync/lib/abandoned-run.js +19 -0
  276. package/dist/modules/data_sync/lib/abandoned-run.js.map +7 -0
  277. package/dist/modules/data_sync/lib/sync-engine.js +72 -6
  278. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  279. package/dist/modules/data_sync/workers/sync-export.js +5 -1
  280. package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
  281. package/dist/modules/data_sync/workers/sync-import.js +5 -1
  282. package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
  283. package/dist/modules/devices/acl.js +9 -1
  284. package/dist/modules/devices/acl.js.map +2 -2
  285. package/dist/modules/devices/api/admin/devices/[id]/route.js +5 -32
  286. package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
  287. package/dist/modules/devices/api/admin/devices/route.js +14 -1
  288. package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
  289. package/dist/modules/devices/api/deviceList.js +99 -12
  290. package/dist/modules/devices/api/deviceList.js.map +2 -2
  291. package/dist/modules/devices/api/deviceSerialization.js +46 -0
  292. package/dist/modules/devices/api/deviceSerialization.js.map +7 -0
  293. package/dist/modules/devices/api/openapi.js +3 -1
  294. package/dist/modules/devices/api/openapi.js.map +2 -2
  295. package/dist/modules/devices/api/route.js +9 -1
  296. package/dist/modules/devices/api/route.js.map +2 -2
  297. package/dist/modules/devices/backend/devices/[id]/page.js +10 -26
  298. package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
  299. package/dist/modules/devices/backend/devices/create/page.js +17 -1
  300. package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
  301. package/dist/modules/devices/backend/devices/page.js +28 -34
  302. package/dist/modules/devices/backend/devices/page.js.map +2 -2
  303. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
  304. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
  305. package/dist/modules/devices/backend/devices/userOptions.js +52 -0
  306. package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
  307. package/dist/modules/devices/setup.js +6 -2
  308. package/dist/modules/devices/setup.js.map +2 -2
  309. package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
  310. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  311. package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
  312. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  313. package/dist/modules/directory/di.js +9 -0
  314. package/dist/modules/directory/di.js.map +2 -2
  315. package/dist/modules/directory/services/organizationHierarchyService.js +29 -0
  316. package/dist/modules/directory/services/organizationHierarchyService.js.map +7 -0
  317. package/dist/modules/directory/services/organizationScopeService.js +56 -0
  318. package/dist/modules/directory/services/organizationScopeService.js.map +7 -0
  319. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  320. package/dist/modules/entities/api/records.js +4 -2
  321. package/dist/modules/entities/api/records.js.map +2 -2
  322. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
  323. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  324. package/dist/modules/eudr/api/plots/route.js +5 -1
  325. package/dist/modules/eudr/api/plots/route.js.map +2 -2
  326. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
  327. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
  328. package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
  329. package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
  330. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
  331. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
  332. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
  333. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
  334. package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
  335. package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
  336. package/dist/modules/eudr/commands/statements.js +0 -3
  337. package/dist/modules/eudr/commands/statements.js.map +2 -2
  338. package/dist/modules/eudr/data/validators.js +4 -1
  339. package/dist/modules/eudr/data/validators.js.map +2 -2
  340. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
  341. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  342. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
  343. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  344. package/dist/modules/feature_toggles/defaults.json +0 -8
  345. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
  346. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
  347. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
  348. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  349. package/dist/modules/inbox_ops/lib/htmlToPlainText.js +1 -12
  350. package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
  351. package/dist/modules/integrations/api/[id]/credentials/route.js +6 -1
  352. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  353. package/dist/modules/integrations/backend/integrations/[id]/page.js +38 -29
  354. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  355. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +36 -9
  356. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  357. package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +55 -0
  358. package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +7 -0
  359. package/dist/modules/integrations/data/validators.js +11 -3
  360. package/dist/modules/integrations/data/validators.js.map +2 -2
  361. package/dist/modules/integrations/lib/credentials-masking.js +6 -2
  362. package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
  363. package/dist/modules/messages/api/[id]/route.js +29 -1
  364. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  365. package/dist/modules/messages/api/openapi.js +17 -2
  366. package/dist/modules/messages/api/openapi.js.map +2 -2
  367. package/dist/modules/messages/api/route.js +17 -2
  368. package/dist/modules/messages/api/route.js.map +2 -2
  369. package/dist/modules/messages/components/MessagesInboxPageClient.js +7 -14
  370. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  371. package/dist/modules/messages/components/inboxFilters.js +15 -0
  372. package/dist/modules/messages/components/inboxFilters.js.map +2 -2
  373. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +11 -1
  374. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
  375. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +3 -1
  376. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
  377. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +2 -1
  378. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  379. package/dist/modules/messages/components/messageListLabels.js +14 -2
  380. package/dist/modules/messages/components/messageListLabels.js.map +2 -2
  381. package/dist/modules/messages/data/validators.js +18 -4
  382. package/dist/modules/messages/data/validators.js.map +2 -2
  383. package/dist/modules/messages/lib/channel-sender-identity.js +26 -0
  384. package/dist/modules/messages/lib/channel-sender-identity.js.map +7 -0
  385. package/dist/modules/messages/lib/composeSourceChannelType.js +41 -0
  386. package/dist/modules/messages/lib/composeSourceChannelType.js.map +7 -0
  387. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +4 -6
  388. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  389. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
  390. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  391. package/dist/modules/payment_gateways/lib/webhook-processor.js +7 -0
  392. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
  393. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
  394. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  395. package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
  396. package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
  397. package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
  398. package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
  399. package/dist/modules/query_index/lib/engine.js +150 -51
  400. package/dist/modules/query_index/lib/engine.js.map +2 -2
  401. package/dist/modules/query_index/lib/search-entity-policy.js +14 -0
  402. package/dist/modules/query_index/lib/search-entity-policy.js.map +7 -0
  403. package/dist/modules/query_index/lib/search-tokens.js +79 -3
  404. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  405. package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
  406. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  407. package/dist/modules/resources/backend/resources/resources/page.js +3 -1
  408. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  409. package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
  410. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  411. package/dist/modules/sales/backend/sales/channels/page.js +3 -0
  412. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  413. package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
  414. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  415. package/dist/modules/sales/commands/documents.js +79 -1
  416. package/dist/modules/sales/commands/documents.js.map +2 -2
  417. package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
  418. package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
  419. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
  420. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  421. package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
  422. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  423. package/dist/modules/sales/components/documents/ItemsSection.js +51 -22
  424. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  425. package/dist/modules/sales/components/documents/LineItemDialog.js +83 -18
  426. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  427. package/dist/modules/sales/components/documents/PaymentsSection.js +23 -9
  428. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  429. package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
  430. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  431. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +68 -14
  432. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  433. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +6 -5
  434. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
  435. package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
  436. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  437. package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
  438. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  439. package/dist/modules/sales/components/documents/lineItemShipmentLock.js +54 -0
  440. package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +7 -0
  441. package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
  442. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  443. package/dist/modules/sales/data/entities.js +8 -2
  444. package/dist/modules/sales/data/entities.js.map +2 -2
  445. package/dist/modules/sales/extension-points.js +1 -0
  446. package/dist/modules/sales/extension-points.js.map +2 -2
  447. package/dist/modules/sales/lib/salesChannelsToggleSeed.js +33 -0
  448. package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +7 -0
  449. package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +32 -0
  450. package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +7 -0
  451. package/dist/modules/sales/setup.js +2 -0
  452. package/dist/modules/sales/setup.js.map +2 -2
  453. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +10 -2
  454. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
  455. package/dist/modules/sales/widgets/injection-table.js +4 -7
  456. package/dist/modules/sales/widgets/injection-table.js.map +2 -2
  457. package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
  458. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  459. package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
  460. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  461. package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
  462. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  463. package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
  464. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  465. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
  466. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  467. package/dist/modules/staff/backend/staff/teams/page.js +3 -0
  468. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  469. package/dist/modules/staff/backend/staff/timesheets/page.js +29 -18
  470. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  471. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
  472. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  473. package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
  474. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  475. package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
  476. package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
  477. package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
  478. package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
  479. package/dist/modules/warranty_claims/api/portal/claims/route.js +12 -9
  480. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  481. package/dist/modules/warranty_claims/backend/page.js +3 -0
  482. package/dist/modules/warranty_claims/backend/page.js.map +2 -2
  483. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
  484. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
  485. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
  486. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
  487. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
  488. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
  489. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
  490. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
  491. package/dist/modules/wms/api/warehouseSearch.js +21 -0
  492. package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
  493. package/dist/modules/wms/api/warehouses/route.js +13 -9
  494. package/dist/modules/wms/api/warehouses/route.js.map +2 -2
  495. package/dist/modules/wms/api/zones/route.js +15 -2
  496. package/dist/modules/wms/api/zones/route.js.map +2 -2
  497. package/dist/modules/wms/commands/configuration.js +121 -9
  498. package/dist/modules/wms/commands/configuration.js.map +2 -2
  499. package/dist/modules/wms/commands/inventory-actions.js +3 -0
  500. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  501. package/dist/modules/wms/commands/shared.js +9 -1
  502. package/dist/modules/wms/commands/shared.js.map +2 -2
  503. package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
  504. package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
  505. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +76 -111
  506. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
  507. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
  508. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  509. package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
  510. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  511. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
  512. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
  513. package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
  514. package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
  515. package/dist/modules/workflows/backend/definitions/page.js +3 -1
  516. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  517. package/dist/modules/workflows/backend/events/page.js +3 -1
  518. package/dist/modules/workflows/backend/events/page.js.map +2 -2
  519. package/dist/modules/workflows/backend/instances/page.js +3 -1
  520. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  521. package/dist/modules/workflows/backend/tasks/page.js +3 -1
  522. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  523. package/dist/modules/workflows/lib/activity-executor.js +4 -0
  524. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  525. package/package.json +9 -10
  526. package/src/helpers/integration/api.ts +33 -1
  527. package/src/helpers/integration/communicationChannelsFixtures.ts +69 -1
  528. package/src/modules/api_keys/api/keys/route.ts +3 -1
  529. package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
  530. package/src/modules/api_keys/di.ts +7 -0
  531. package/src/modules/api_keys/services/principalService.ts +33 -0
  532. package/src/modules/attachments/AGENTS.md +15 -0
  533. package/src/modules/attachments/api/route.ts +2 -2
  534. package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
  535. package/src/modules/attachments/di.ts +16 -0
  536. package/src/modules/attachments/i18n/de.json +5 -0
  537. package/src/modules/attachments/i18n/en.json +5 -0
  538. package/src/modules/attachments/i18n/es.json +5 -0
  539. package/src/modules/attachments/i18n/ko.json +5 -0
  540. package/src/modules/attachments/i18n/pl.json +5 -0
  541. package/src/modules/attachments/index.ts +10 -0
  542. package/src/modules/attachments/lib/attachment-service.ts +468 -0
  543. package/src/modules/attachments/lib/scoped-upload-service.ts +46 -0
  544. package/src/modules/attachments/lib/upload-limits.ts +6 -2
  545. package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
  546. package/src/modules/audit_logs/lib/display-helpers.tsx +42 -32
  547. package/src/modules/audit_logs/services/actionLogService.ts +74 -11
  548. package/src/modules/auth/acl.ts +30 -6
  549. package/src/modules/auth/api/feature-check.ts +10 -3
  550. package/src/modules/auth/api/login.ts +9 -1
  551. package/src/modules/auth/api/reset/validate.ts +57 -0
  552. package/src/modules/auth/api/roles/acl/route.ts +42 -39
  553. package/src/modules/auth/api/session/refresh.ts +32 -1
  554. package/src/modules/auth/api/users/acl/route.ts +177 -53
  555. package/src/modules/auth/api/users/route.ts +18 -5
  556. package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
  557. package/src/modules/auth/backend/roles/page.tsx +4 -2
  558. package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
  559. package/src/modules/auth/backend/users/page.tsx +3 -2
  560. package/src/modules/auth/commands/acl.ts +495 -0
  561. package/src/modules/auth/components/AclEditor.tsx +63 -33
  562. package/src/modules/auth/data/entities.ts +4 -1
  563. package/src/modules/auth/data/validators.ts +5 -0
  564. package/src/modules/auth/di.ts +46 -16
  565. package/src/modules/auth/frontend/reset/[token]/page.tsx +74 -0
  566. package/src/modules/auth/i18n/de.json +389 -0
  567. package/src/modules/auth/i18n/en.json +389 -0
  568. package/src/modules/auth/i18n/es.json +389 -0
  569. package/src/modules/auth/i18n/ko.json +389 -0
  570. package/src/modules/auth/i18n/pl.json +391 -2
  571. package/src/modules/auth/lib/grantChecks.ts +6 -0
  572. package/src/modules/auth/lib/userIdFilter.ts +31 -0
  573. package/src/modules/auth/services/authService.ts +18 -2
  574. package/src/modules/auth/services/principalService.ts +266 -0
  575. package/src/modules/auth/services/rbacService.ts +110 -24
  576. package/src/modules/auth/services/roleOrganizationScope.ts +70 -0
  577. package/src/modules/business_rules/backend/logs/page.tsx +4 -1
  578. package/src/modules/business_rules/backend/rules/page.tsx +4 -1
  579. package/src/modules/business_rules/backend/sets/page.tsx +4 -1
  580. package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
  581. package/src/modules/business_rules/lib/rule-evaluator.ts +10 -9
  582. package/src/modules/catalog/ai-agents.ts +1 -1
  583. package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
  584. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
  585. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
  586. package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
  587. package/src/modules/catalog/i18n/de.json +1 -0
  588. package/src/modules/catalog/i18n/en.json +1 -0
  589. package/src/modules/catalog/i18n/es.json +1 -0
  590. package/src/modules/catalog/i18n/ko.json +1 -0
  591. package/src/modules/catalog/i18n/pl.json +1 -0
  592. package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
  593. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +17 -1
  594. package/src/modules/communication_channels/api/post/test-seed/route.ts +155 -16
  595. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
  596. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +38 -1
  597. package/src/modules/communication_channels/di.ts +7 -0
  598. package/src/modules/communication_channels/lib/adapter.ts +14 -0
  599. package/src/modules/communication_channels/lib/email-capabilities.ts +4 -0
  600. package/src/modules/communication_channels/lib/outbound-recipient.ts +71 -0
  601. package/src/modules/communication_channels/lib/resolve-channel-type.ts +99 -0
  602. package/src/modules/communication_channels/lib/test-seed.ts +64 -4
  603. package/src/modules/configs/i18n/de.json +2 -0
  604. package/src/modules/configs/i18n/en.json +2 -0
  605. package/src/modules/configs/i18n/es.json +2 -0
  606. package/src/modules/configs/i18n/ko.json +2 -0
  607. package/src/modules/configs/i18n/pl.json +2 -0
  608. package/src/modules/configs/lib/system-status.ts +9 -0
  609. package/src/modules/currencies/AGENTS.md +3 -0
  610. package/src/modules/currencies/backend/currencies/page.tsx +4 -1
  611. package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
  612. package/src/modules/currencies/services/README.md +45 -0
  613. package/src/modules/currencies/services/rateFetchingService.ts +6 -1
  614. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
  615. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
  616. package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +4 -1
  617. package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
  618. package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
  619. package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +17 -2
  620. package/src/modules/customers/ai-tools/deals-pack.ts +66 -1
  621. package/src/modules/customers/ai-tools/people-pack.ts +4 -1
  622. package/src/modules/customers/api/activities/route.ts +45 -0
  623. package/src/modules/customers/api/companies/[id]/people/route.ts +24 -73
  624. package/src/modules/customers/api/companies/[id]/route.ts +24 -107
  625. package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +16 -8
  626. package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
  627. package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
  628. package/src/modules/customers/api/deals/[id]/route.ts +17 -28
  629. package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
  630. package/src/modules/customers/api/deals/aggregate/route.ts +14 -4
  631. package/src/modules/customers/api/deals/map/route.ts +1 -0
  632. package/src/modules/customers/api/deals/route.ts +50 -1
  633. package/src/modules/customers/api/entity-roles-factory.ts +8 -4
  634. package/src/modules/customers/api/interactions/route.ts +8 -2
  635. package/src/modules/customers/api/interactions/tasks/route.ts +24 -1
  636. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
  637. package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
  638. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
  639. package/src/modules/customers/api/people/[id]/route.ts +16 -11
  640. package/src/modules/customers/api/people/check-phone/route.ts +75 -10
  641. package/src/modules/customers/api/todos/route.ts +27 -2
  642. package/src/modules/customers/backend/customers/companies/page.tsx +5 -2
  643. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -2
  644. package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
  645. package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +5 -14
  646. package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +109 -27
  647. package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +21 -0
  648. package/src/modules/customers/backend/customers/people/page.tsx +5 -2
  649. package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +12 -4
  650. package/src/modules/customers/commands/deals.ts +34 -59
  651. package/src/modules/customers/commands/interactions.ts +1 -1
  652. package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
  653. package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
  654. package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
  655. package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
  656. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
  657. package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
  658. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
  659. package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
  660. package/src/modules/customers/components/calendar/EventPeekPopover.tsx +4 -2
  661. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
  662. package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
  663. package/src/modules/customers/components/calendar/types.ts +2 -2
  664. package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
  665. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
  666. package/src/modules/customers/components/detail/DealForm.tsx +22 -2
  667. package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
  668. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +58 -12
  669. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
  670. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +23 -14
  671. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
  672. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
  673. package/src/modules/customers/components/detail/types.ts +1 -1
  674. package/src/modules/customers/components/detail/utils.ts +15 -5
  675. package/src/modules/customers/data/entities.ts +1 -1
  676. package/src/modules/customers/data/validators.ts +50 -9
  677. package/src/modules/customers/events.ts +5 -0
  678. package/src/modules/customers/i18n/de.json +9 -1
  679. package/src/modules/customers/i18n/en.json +9 -1
  680. package/src/modules/customers/i18n/es.json +9 -1
  681. package/src/modules/customers/i18n/ko.json +9 -1
  682. package/src/modules/customers/i18n/pl.json +9 -1
  683. package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
  684. package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
  685. package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
  686. package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
  687. package/src/modules/customers/lib/closureStage.ts +76 -0
  688. package/src/modules/customers/lib/dealStatus.ts +42 -0
  689. package/src/modules/customers/lib/detailReadAccess.ts +35 -0
  690. package/src/modules/customers/lib/findPeopleByAddresses.ts +8 -6
  691. package/src/modules/customers/lib/personCompanies.ts +136 -1
  692. package/src/modules/customers/lib/todoCompatibility.ts +19 -1
  693. package/src/modules/dashboards/i18n/es.json +4 -4
  694. package/src/modules/dashboards/i18n/pl.json +7 -7
  695. package/src/modules/dashboards/lib/widgetScope.ts +6 -0
  696. package/src/modules/data_sync/AGENTS.md +1 -1
  697. package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
  698. package/src/modules/data_sync/lib/abandoned-run.ts +56 -0
  699. package/src/modules/data_sync/lib/adapter.ts +36 -0
  700. package/src/modules/data_sync/lib/sync-engine.ts +113 -5
  701. package/src/modules/data_sync/workers/sync-export.ts +4 -0
  702. package/src/modules/data_sync/workers/sync-import.ts +4 -0
  703. package/src/modules/devices/AGENTS.md +20 -1
  704. package/src/modules/devices/acl.ts +9 -1
  705. package/src/modules/devices/api/admin/devices/[id]/route.ts +5 -34
  706. package/src/modules/devices/api/admin/devices/route.ts +14 -1
  707. package/src/modules/devices/api/deviceList.ts +135 -11
  708. package/src/modules/devices/api/deviceSerialization.ts +50 -0
  709. package/src/modules/devices/api/openapi.ts +9 -1
  710. package/src/modules/devices/api/route.ts +9 -1
  711. package/src/modules/devices/backend/devices/[id]/page.tsx +18 -32
  712. package/src/modules/devices/backend/devices/create/page.tsx +17 -1
  713. package/src/modules/devices/backend/devices/page.tsx +44 -45
  714. package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
  715. package/src/modules/devices/backend/devices/userOptions.ts +99 -0
  716. package/src/modules/devices/i18n/de.json +3 -2
  717. package/src/modules/devices/i18n/en.json +3 -2
  718. package/src/modules/devices/i18n/es.json +3 -2
  719. package/src/modules/devices/i18n/ko.json +3 -2
  720. package/src/modules/devices/i18n/pl.json +3 -2
  721. package/src/modules/devices/setup.ts +6 -2
  722. package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
  723. package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
  724. package/src/modules/directory/di.ts +14 -2
  725. package/src/modules/directory/services/organizationHierarchyService.ts +34 -0
  726. package/src/modules/directory/services/organizationScopeService.ts +85 -0
  727. package/src/modules/directory/utils/organizationScope.ts +4 -15
  728. package/src/modules/entities/api/records.ts +2 -0
  729. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
  730. package/src/modules/eudr/api/plots/route.ts +4 -0
  731. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
  732. package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
  733. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
  734. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
  735. package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
  736. package/src/modules/eudr/commands/statements.ts +0 -3
  737. package/src/modules/eudr/data/validators.ts +16 -1
  738. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
  739. package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
  740. package/src/modules/feature_toggles/defaults.json +0 -8
  741. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
  742. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
  743. package/src/modules/inbox_ops/lib/htmlToPlainText.ts +1 -17
  744. package/src/modules/integrations/api/[id]/credentials/route.ts +6 -4
  745. package/src/modules/integrations/backend/integrations/[id]/page.tsx +61 -31
  746. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +44 -9
  747. package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +84 -0
  748. package/src/modules/integrations/data/validators.ts +12 -2
  749. package/src/modules/integrations/i18n/de.json +1 -0
  750. package/src/modules/integrations/i18n/en.json +1 -0
  751. package/src/modules/integrations/i18n/es.json +1 -0
  752. package/src/modules/integrations/i18n/ko.json +1 -0
  753. package/src/modules/integrations/i18n/pl.json +1 -0
  754. package/src/modules/integrations/lib/credentials-masking.ts +10 -6
  755. package/src/modules/messages/api/[id]/route.ts +55 -1
  756. package/src/modules/messages/api/openapi.ts +20 -0
  757. package/src/modules/messages/api/route.ts +31 -2
  758. package/src/modules/messages/components/MessagesInboxPageClient.tsx +9 -16
  759. package/src/modules/messages/components/inboxFilters.ts +36 -0
  760. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +14 -1
  761. package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +3 -1
  762. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +2 -1
  763. package/src/modules/messages/components/message-detail/types.ts +3 -0
  764. package/src/modules/messages/components/messageListLabels.ts +52 -3
  765. package/src/modules/messages/data/validators.ts +43 -4
  766. package/src/modules/messages/lib/channel-sender-identity.ts +55 -0
  767. package/src/modules/messages/lib/composeSourceChannelType.ts +95 -0
  768. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -6
  769. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
  770. package/src/modules/payment_gateways/lib/webhook-processor.ts +11 -0
  771. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
  772. package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
  773. package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
  774. package/src/modules/query_index/lib/engine.ts +249 -61
  775. package/src/modules/query_index/lib/search-entity-policy.ts +46 -0
  776. package/src/modules/query_index/lib/search-tokens.ts +135 -3
  777. package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
  778. package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
  779. package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
  780. package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
  781. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
  782. package/src/modules/sales/commands/documents.ts +93 -0
  783. package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
  784. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
  785. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
  786. package/src/modules/sales/components/documents/ItemsSection.tsx +56 -18
  787. package/src/modules/sales/components/documents/LineItemDialog.tsx +137 -20
  788. package/src/modules/sales/components/documents/PaymentsSection.tsx +20 -9
  789. package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
  790. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +106 -14
  791. package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +6 -5
  792. package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
  793. package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
  794. package/src/modules/sales/components/documents/lineItemShipmentLock.ts +74 -0
  795. package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
  796. package/src/modules/sales/data/entities.ts +6 -0
  797. package/src/modules/sales/extension-points.ts +1 -0
  798. package/src/modules/sales/i18n/de.json +8 -0
  799. package/src/modules/sales/i18n/en.json +8 -0
  800. package/src/modules/sales/i18n/es.json +8 -0
  801. package/src/modules/sales/i18n/ko.json +8 -0
  802. package/src/modules/sales/i18n/pl.json +8 -0
  803. package/src/modules/sales/lib/salesChannelsToggleSeed.ts +42 -0
  804. package/src/modules/sales/migrations/.snapshot-open-mercato.json +60 -0
  805. package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +87 -0
  806. package/src/modules/sales/setup.ts +2 -0
  807. package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +9 -1
  808. package/src/modules/sales/widgets/injection-table.ts +4 -7
  809. package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
  810. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
  811. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
  812. package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
  813. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
  814. package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
  815. package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1
  816. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
  817. package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
  818. package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
  819. package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
  820. package/src/modules/warranty_claims/api/portal/claims/route.ts +12 -9
  821. package/src/modules/warranty_claims/backend/page.tsx +4 -0
  822. package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
  823. package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
  824. package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
  825. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
  826. package/src/modules/wms/api/warehouseSearch.ts +23 -0
  827. package/src/modules/wms/api/warehouses/route.ts +13 -9
  828. package/src/modules/wms/api/zones/route.ts +16 -0
  829. package/src/modules/wms/commands/configuration.ts +159 -10
  830. package/src/modules/wms/commands/inventory-actions.ts +3 -0
  831. package/src/modules/wms/commands/shared.ts +17 -0
  832. package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
  833. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +95 -141
  834. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
  835. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
  836. package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
  837. package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
  838. package/src/modules/wms/i18n/de.json +2 -0
  839. package/src/modules/wms/i18n/en.json +2 -0
  840. package/src/modules/wms/i18n/es.json +2 -0
  841. package/src/modules/wms/i18n/ko.json +2 -0
  842. package/src/modules/wms/i18n/pl.json +2 -0
  843. package/src/modules/workflows/backend/definitions/page.tsx +4 -1
  844. package/src/modules/workflows/backend/events/page.tsx +4 -1
  845. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  846. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  847. package/src/modules/workflows/lib/activity-executor.ts +9 -0
@@ -28,7 +28,10 @@ export const metadata = {
28
28
 
29
29
  type WidgetContext = WidgetScopeContext & { limit: number }
30
30
 
31
- async function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {
31
+ async function resolveContext(
32
+ req: Request,
33
+ translate: (key: string, fallback?: string) => string,
34
+ ): Promise<WidgetContext> {
32
35
  const url = new URL(req.url)
33
36
  const rawQuery: Record<string, string> = {}
34
37
  for (const [key, value] of url.searchParams.entries()) {
@@ -39,16 +42,18 @@ async function resolveContext(req: Request, translate: (key: string, fallback?:
39
42
  throw new CrudHttpError(400, { error: translate('customers.errors.invalid_query', 'Invalid query parameters') })
40
43
  }
41
44
 
42
- const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {
43
- tenantId: parsed.data.tenantId ?? null,
44
- organizationId: parsed.data.organizationId ?? null,
45
- })
45
+ const { container, em, tenantId, organizationIds, isUnrestrictedOrganizationScope } =
46
+ await resolveWidgetScope(req, translate, {
47
+ tenantId: parsed.data.tenantId ?? null,
48
+ organizationId: parsed.data.organizationId ?? null,
49
+ })
46
50
 
47
51
  return {
48
52
  container,
49
53
  em,
50
54
  tenantId,
51
55
  organizationIds,
56
+ isUnrestrictedOrganizationScope,
52
57
  limit: parsed.data.limit,
53
58
  }
54
59
  }
@@ -56,7 +61,8 @@ async function resolveContext(req: Request, translate: (key: string, fallback?:
56
61
  export async function GET(req: Request) {
57
62
  const { translate } = await resolveTranslations()
58
63
  try {
59
- const { container, em, tenantId, organizationIds, limit } = await resolveContext(req, translate)
64
+ const { container, em, tenantId, organizationIds, isUnrestrictedOrganizationScope, limit } =
65
+ await resolveContext(req, translate)
60
66
  const auth = {
61
67
  tenantId,
62
68
  orgId: organizationIds?.[0] ?? null,
@@ -64,6 +70,7 @@ export async function GET(req: Request) {
64
70
  }
65
71
  const flags = await resolveCustomerInteractionFeatureFlags(container, tenantId)
66
72
  const mergedWindow = Math.min(limit * 4, 50)
73
+ const isUnrestricted = isUnrestrictedOrganizationScope === true
67
74
  const rows = flags.unified
68
75
  ? (await listCanonicalTodoRows(
69
76
  em,
@@ -71,7 +78,7 @@ export async function GET(req: Request) {
71
78
  auth,
72
79
  organizationIds?.[0] ?? null,
73
80
  organizationIds ?? null,
74
- { pagination: { page: 1, pageSize: limit } },
81
+ { pagination: { page: 1, pageSize: limit }, isUnrestricted },
75
82
  )).items
76
83
  : await Promise.all([
77
84
  listLegacyTodoRows(
@@ -80,7 +87,7 @@ export async function GET(req: Request) {
80
87
  tenantId,
81
88
  organizationIds ?? null,
82
89
  undefined,
83
- { limit: mergedWindow },
90
+ { limit: mergedWindow, isUnrestricted },
84
91
  ),
85
92
  listCanonicalTodoRows(
86
93
  em,
@@ -91,6 +98,7 @@ export async function GET(req: Request) {
91
98
  {
92
99
  includeDeleted: true,
93
100
  limit: mergedWindow,
101
+ isUnrestricted,
94
102
  },
95
103
  ),
96
104
  ]).then(([legacyRows, canonicalRows]) =>
@@ -101,8 +101,11 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
101
101
  throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
102
102
  }
103
103
 
104
+ // Existence oracle (#5504): deny a cross-org read as not-found — identical to
105
+ // the parent-not-found above — so it cannot reveal that a deal exists in an
106
+ // organization the caller cannot see.
104
107
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
105
- throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })
108
+ throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
106
109
  }
107
110
 
108
111
  const entityScope = { tenantId: deal.tenantId, organizationId: deal.organizationId }
@@ -102,8 +102,11 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
102
102
  throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
103
103
  }
104
104
 
105
+ // Existence oracle (#5504): deny a cross-org read as not-found — identical to
106
+ // the parent-not-found above — so it cannot reveal that a deal exists in an
107
+ // organization the caller cannot see.
105
108
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
106
- throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })
109
+ throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
107
110
  }
108
111
 
109
112
  const entityScope = { tenantId: deal.tenantId, organizationId: deal.organizationId }
@@ -19,10 +19,9 @@ import type { ActionLogService } from '@open-mercato/core/modules/audit_logs/ser
19
19
  import { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'
20
20
  import { normalizeCustomFieldResponse } from '@open-mercato/shared/lib/custom-fields/normalize'
21
21
  import { E } from '#generated/entities.ids.generated'
22
- import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
23
22
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
24
23
  import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
25
- import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
24
+ import { denyCustomerDetailReadAsNotFound } from '../../../lib/detailReadAccess'
26
25
  import { decryptEntitiesWithFallbackScope } from '@open-mercato/shared/lib/encryption/subscriber'
27
26
  import { runWithCacheTenant } from '@open-mercato/cache'
28
27
  import {
@@ -47,10 +46,6 @@ function notFound(message: string) {
47
46
  return NextResponse.json({ error: message }, { status: 404 })
48
47
  }
49
48
 
50
- function forbidden(message: string) {
51
- return NextResponse.json({ error: message }, { status: 403 })
52
- }
53
-
54
49
  type DealAssociation = {
55
50
  id: string
56
51
  label: string
@@ -416,23 +411,10 @@ export async function GET(request: Request, context: { params?: Record<string, u
416
411
  return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
417
412
  }
418
413
 
419
- let rbac: RbacService | null = null
420
- try {
421
- rbac = (container.resolve('rbacService') as RbacService)
422
- } catch {
423
- rbac = null
424
- }
425
-
426
- if (!rbac || !auth?.sub) {
427
- return forbidden('Access denied')
428
- }
429
- const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {
430
- tenantId: auth.tenantId ?? null,
431
- organizationId: auth.orgId ?? null,
432
- })
433
- if (!hasFeature) {
434
- return forbidden('Access denied')
435
- }
414
+ // The API dispatcher already enforces `requireFeatures: ['customers.deals.view']`
415
+ // against the caller's effective (selected) organization before this handler
416
+ // runs, so no in-route feature re-check is needed. Re-checking here against the
417
+ // token org (auth.orgId) would wrongly 403 org-switched callers (#5012).
436
418
 
437
419
  const scope = await resolveOrganizationScopeForRequest({ container, auth, request })
438
420
  const em = (container.resolve('em') as EntityManager)
@@ -454,9 +436,16 @@ export async function GET(request: Request, context: { params?: Record<string, u
454
436
  return notFound('Deal not found')
455
437
  }
456
438
 
457
- if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
458
- return forbidden('Access denied')
459
- }
439
+ // Existence oracle (issue #5504): a caller who holds customers.deals.view but
440
+ // whose scope excludes the record's organization must get the SAME response as
441
+ // for a non-existent id, so 403-when-present / 404-when-absent collapses to a
442
+ // uniform 404 not-found. The dispatcher already returns a uniform 403 for
443
+ // callers who lack the feature entirely.
444
+ const organizationReadDenied = denyCustomerDetailReadAsNotFound(
445
+ { scope, auth, organizationId: deal.organizationId },
446
+ 'Deal not found',
447
+ )
448
+ if (organizationReadDenied) return organizationReadDenied
460
449
 
461
450
  const decryptionScope = {
462
451
  tenantId: deal.tenantId ?? auth.tenantId ?? null,
@@ -977,8 +966,8 @@ export const openApi: OpenApiRouteDoc = {
977
966
  ],
978
967
  errors: [
979
968
  { status: 401, description: 'Unauthorized', schema: dealDetailErrorSchema },
980
- { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealDetailErrorSchema },
981
- { status: 404, description: 'Deal not found', schema: dealDetailErrorSchema },
969
+ { status: 403, description: 'Forbidden caller lacks the required feature', schema: dealDetailErrorSchema },
970
+ { status: 404, description: 'Deal not found, or its organization is not in the caller’s scope', schema: dealDetailErrorSchema },
982
971
  ],
983
972
  },
984
973
  },
@@ -7,7 +7,6 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
7
  import type { EntityManager } from '@mikro-orm/postgresql'
8
8
  import { CustomerDeal, CustomerPipeline } from '../../../../data/entities'
9
9
  import { DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'
10
- import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
11
10
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
12
11
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
13
12
  import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
@@ -24,10 +23,6 @@ function notFound(message: string) {
24
23
  return NextResponse.json({ error: message }, { status: 404 })
25
24
  }
26
25
 
27
- function forbidden(message: string) {
28
- return NextResponse.json({ error: message }, { status: 403 })
29
- }
30
-
31
26
  function badRequest(message: string, code?: string) {
32
27
  return NextResponse.json(
33
28
  code ? { error: message, code } : { error: message },
@@ -67,23 +62,10 @@ export async function GET(request: Request, context: { params?: Record<string, u
67
62
  return NextResponse.json({ error: translate('customers.errors.authentication_required', 'Authentication required') }, { status: 401 })
68
63
  }
69
64
 
70
- let rbac: RbacService | null = null
71
- try {
72
- rbac = (container.resolve('rbacService') as RbacService)
73
- } catch {
74
- rbac = null
75
- }
76
-
77
- if (!rbac || !auth?.sub) {
78
- return forbidden(translate('customers.errors.access_denied', 'Access denied'))
79
- }
80
- const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {
81
- tenantId: auth.tenantId ?? null,
82
- organizationId: auth.orgId ?? null,
83
- })
84
- if (!hasFeature) {
85
- return forbidden(translate('customers.errors.access_denied', 'Access denied'))
86
- }
65
+ // The API dispatcher already enforces `requireFeatures: ['customers.deals.view']`
66
+ // against the caller's effective (selected) organization before this handler
67
+ // runs, so no in-route feature re-check is needed. Re-checking against the token
68
+ // org (auth.orgId) would wrongly 403 org-switched callers (#5012).
87
69
 
88
70
  const scope = await resolveOrganizationScopeForRequest({ container, auth, request })
89
71
  const em = (container.resolve('em') as EntityManager)
@@ -98,8 +80,11 @@ export async function GET(request: Request, context: { params?: Record<string, u
98
80
  return notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
99
81
  }
100
82
 
83
+ // Existence oracle (#5504): deny a cross-org read as not-found — identical to
84
+ // the deal-not-found above — so it cannot reveal that a deal exists in an
85
+ // organization the caller cannot see.
101
86
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
102
- return forbidden(translate('customers.errors.access_denied', 'Access denied'))
87
+ return notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
103
88
  }
104
89
 
105
90
  if (!deal.closureOutcome) {
@@ -206,8 +191,8 @@ export const openApi: OpenApiRouteDoc = {
206
191
  errors: [
207
192
  { status: 400, description: 'Deal is not closed', schema: dealStatsErrorSchema },
208
193
  { status: 401, description: 'Unauthorized', schema: dealStatsErrorSchema },
209
- { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealStatsErrorSchema },
210
- { status: 404, description: 'Deal not found', schema: dealStatsErrorSchema },
194
+ { status: 403, description: 'Forbidden caller lacks the required feature', schema: dealStatsErrorSchema },
195
+ { status: 404, description: 'Deal not found, or its organization is not in the caller’s scope', schema: dealStatsErrorSchema },
211
196
  ],
212
197
  },
213
198
  },
@@ -14,6 +14,7 @@ import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
14
14
  import { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'
15
15
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
16
16
  import { fetchStuckDealIds } from '../../../lib/stuckDeals'
17
+ import { expandDealStatusAliases } from '../../../lib/dealStatus'
17
18
  import { findMatchingEntityIdsBySearchTokensAcrossSources } from '../../utils'
18
19
  import { E } from '#generated/entities.ids.generated'
19
20
  import { createLogger } from '@open-mercato/shared/lib/logger'
@@ -27,7 +28,7 @@ export const metadata = {
27
28
  const querySchema = z.object({
28
29
  pipelineId: z.string().uuid().optional(),
29
30
  search: z.string().optional(),
30
- status: z.array(z.enum(['open', 'closed', 'win', 'loose'])).optional(),
31
+ status: z.array(z.string().max(50)).max(20).optional(),
31
32
  ownerUserId: z.array(z.string().uuid()).optional(),
32
33
  personId: z.array(z.string().uuid()).optional(),
33
34
  companyId: z.array(z.string().uuid()).optional(),
@@ -247,9 +248,11 @@ export async function GET(req: Request) {
247
248
  }
248
249
  }
249
250
  if (parsed.data.status && parsed.data.status.length) {
250
- const placeholders = parsed.data.status.map(() => '?').join(',')
251
+ // Non-empty input always expands to a non-empty set, so this always narrows.
252
+ const expandedStatuses = expandDealStatusAliases(parsed.data.status)
253
+ const placeholders = expandedStatuses.map(() => '?').join(',')
251
254
  where.push(`status IN (${placeholders})`)
252
- values.push(...parsed.data.status)
255
+ values.push(...expandedStatuses)
253
256
  }
254
257
  if (parsed.data.ownerUserId && parsed.data.ownerUserId.length) {
255
258
  const placeholders = parsed.data.ownerUserId.map(() => '?').join(',')
@@ -265,7 +268,14 @@ export async function GET(req: Request) {
265
268
  values.push(parsed.data.expectedCloseAtTo)
266
269
  }
267
270
  if (parsed.data.isOverdue) {
268
- where.push("expected_close_at < CURRENT_DATE AND status = 'open'")
271
+ // Mirror the list route's precedence: the caller-supplied status filter wins, and
272
+ // status='open' is injected only when none was provided.
273
+ const hasCallerStatus = !!parsed.data.status?.length
274
+ if (hasCallerStatus) {
275
+ where.push('expected_close_at < CURRENT_DATE')
276
+ } else {
277
+ where.push("expected_close_at < CURRENT_DATE AND status = 'open'")
278
+ }
269
279
  }
270
280
  if (parsed.data.isStuck) {
271
281
  // Reuse the list endpoint's stuck-deal lookup so kanban headers, lane counts, and the
@@ -545,5 +545,6 @@ export async function GET(req: Request) {
545
545
  page: query.page,
546
546
  pageSize: query.pageSize,
547
547
  totalPages: Math.ceil(res.total / (query.pageSize || 1)),
548
+ ...(res.meta?.listCountCapWarning ? { totalIsCapped: true } : {}),
548
549
  })
549
550
  }
@@ -22,7 +22,9 @@ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
22
22
  import { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'
23
23
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
24
24
  import { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'
25
+ import type { FilterGroup, FilterRule } from '@open-mercato/shared/lib/query/advanced-filter-tree'
25
26
  import { fetchStuckDealIds } from '../../lib/stuckDeals'
27
+ import { expandDealStatusAliases } from '../../lib/dealStatus'
26
28
  import { createLogger } from '@open-mercato/shared/lib/logger'
27
29
 
28
30
  const logger = createLogger('customers')
@@ -31,6 +33,19 @@ const rawBodySchema = z.object({}).passthrough()
31
33
 
32
34
  const stringOrStringArray = z.union([z.string(), z.array(z.string())])
33
35
  const OPEN_DEAL_STATUSES = ['open', 'in_progress'] as const
36
+ // Tree operators whose `status` values are rewritten through the canonical vocabulary.
37
+ // Text (`contains`/`starts_with`/…) and range (`between`) operators are left untouched
38
+ // so a URL-supplied rule keeps its scalar semantics.
39
+ const STATUS_TREE_OPERATORS = new Set([
40
+ 'is',
41
+ 'equals',
42
+ 'is_not',
43
+ 'not_equals',
44
+ 'is_any_of',
45
+ 'has_any_of',
46
+ 'is_none_of',
47
+ 'has_none_of',
48
+ ])
34
49
  const booleanQueryParam = z.preprocess((value) => {
35
50
  const parsed = parseBooleanFromUnknown(value)
36
51
  return parsed === null ? value : parsed
@@ -245,6 +260,39 @@ function normalizeUuidList(values: Array<unknown>): string[] {
245
260
 
246
261
  export async function buildDealListFilters(query: DealListQuery, ctx?: import('@open-mercato/shared/lib/crud/factory').CrudCtx) {
247
262
  const advancedFilterTree = consumeAdvancedFilterState(query)
263
+ if (advancedFilterTree) {
264
+ // Expand status aliases in the advanced-filter tree so List (tree path) and
265
+ // Kanban (plain ?status=) share the same canonical vocabulary (#5107). Only
266
+ // set-membership operators are rewritten — text/range operators on `status`
267
+ // keep their pre-existing scalar semantics instead of being corrupted.
268
+ const walk = (node: FilterGroup | FilterRule): void => {
269
+ if (node.type === 'rule' && node.field === 'status' && STATUS_TREE_OPERATORS.has(node.operator)) {
270
+ const listOperators = new Set(['is_any_of', 'has_any_of', 'is_none_of', 'has_none_of'])
271
+ const rawValues: string[] = Array.isArray(node.value)
272
+ ? (node.value as unknown[]).filter((v): v is string => typeof v === 'string')
273
+ : typeof node.value === 'string'
274
+ ? [node.value]
275
+ : []
276
+ if (rawValues.length === 0) return
277
+ const expanded = expandDealStatusAliases(rawValues)
278
+ if (listOperators.has(node.operator)) {
279
+ node.value = expanded
280
+ } else if (expanded.length === 1) {
281
+ node.value = expanded[0]
282
+ } else {
283
+ node.value = expanded
284
+ if (node.operator === 'is' || node.operator === 'equals') {
285
+ node.operator = 'is_any_of'
286
+ } else if (node.operator === 'is_not' || node.operator === 'not_equals') {
287
+ node.operator = 'is_none_of'
288
+ }
289
+ }
290
+ } else if (node.type === 'group') {
291
+ for (const child of node.children) walk(child)
292
+ }
293
+ }
294
+ walk(advancedFilterTree.root)
295
+ }
248
296
  const filters: Record<string, unknown> = {}
249
297
  let restrictedIds: string[] | null = null
250
298
 
@@ -300,7 +348,8 @@ export async function buildDealListFilters(query: DealListQuery, ctx?: import('@
300
348
  }
301
349
  }
302
350
 
303
- const statusList = query.status ? normalizeStringList(query.status) : []
351
+ const rawStatusList = query.status ? normalizeStringList(query.status) : []
352
+ const statusList = expandDealStatusAliases(rawStatusList)
304
353
  if (statusList.length > 0) {
305
354
  filters.status = statusList.length === 1 ? { $eq: statusList[0] } : { $in: statusList }
306
355
  }
@@ -77,10 +77,14 @@ function ensureRouteOrganizationAccess(
77
77
  organizationId: string,
78
78
  scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],
79
79
  auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],
80
- translate: Translator,
80
+ notFoundMessage: string,
81
81
  ) {
82
+ // Existence oracle (#5504): a caller who holds the feature but whose scope
83
+ // excludes the record's organization is denied as not-found (the same response
84
+ // the caller gets for a missing record), so this cannot reveal that the record
85
+ // exists in an organization they cannot see.
82
86
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId })) {
83
- throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })
87
+ throw notFound(notFoundMessage)
84
88
  }
85
89
  }
86
90
 
@@ -132,7 +136,7 @@ async function resolveEntityRouteScope(
132
136
  if (!entity || entity.tenantId !== auth.tenantId) {
133
137
  throw notFound(translate('customers.errors.customer_not_found', 'Customer not found'))
134
138
  }
135
- ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate)
139
+ ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate('customers.errors.customer_not_found', 'Customer not found'))
136
140
  return {
137
141
  entity,
138
142
  organizationId: entity.organizationId,
@@ -164,7 +168,7 @@ async function resolveRoleRouteScope(
164
168
  ) {
165
169
  throw notFound(translate('customers.errors.role_not_found', 'Role not found'))
166
170
  }
167
- ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate)
171
+ ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate('customers.errors.role_not_found', 'Role not found'))
168
172
  return {
169
173
  role,
170
174
  organizationId: role.organizationId,
@@ -180,7 +180,7 @@ type InteractionListRow = {
180
180
  all_day: boolean | null
181
181
  recurrence_rule: string | null
182
182
  recurrence_end: Date | null
183
- participants: Array<{ userId: string; name?: string; email?: string; status?: string }> | null
183
+ participants: Array<{ userId?: string; name?: string; email?: string; status?: string }> | null
184
184
  reminder_minutes: number | null
185
185
  visibility: string | null
186
186
  linked_entities: Array<{ id: string; type: string; label: string }> | null
@@ -744,7 +744,13 @@ const interactionListItemSchema = z
744
744
  recurrenceEnd: z.string().nullable().optional(),
745
745
  participants: z.array(
746
746
  z.object({
747
- userId: z.string().uuid(),
747
+ userId: z
748
+ .string()
749
+ .uuid()
750
+ .optional()
751
+ .describe(
752
+ 'Absent for an external guest, who has no person/customer/staff record and is identified by email instead. Identify a participant by userId when present, otherwise by its normalized email.',
753
+ ),
748
754
  name: z.string().optional(),
749
755
  email: z.string().optional(),
750
756
  status: z.string().optional(),
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
5
5
  import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
6
+ import { isUnrestrictedOrganizationScope } from '@open-mercato/shared/lib/auth/organizationAccess'
6
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
8
  import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
8
9
  import { createCustomersCrudOpenApi, createPagedListResponseSchema } from '../../openapi'
@@ -40,11 +41,30 @@ const MERGED_TASK_FETCH_CAP = 2000
40
41
  export async function GET(request: Request): Promise<Response> {
41
42
  const { translate } = await resolveTranslations()
42
43
  try {
43
- const { auth, em, organizationIds, container, selectedOrganizationId } =
44
+ const { auth, em, organizationIds, container, selectedOrganizationId, scope } =
44
45
  await resolveCustomersRequestContext(request)
45
46
  const query = querySchema.parse(Object.fromEntries(new URL(request.url).searchParams))
46
47
  const flags = await resolveCustomerInteractionFeatureFlags(container, auth.tenantId)
47
48
  const exportAll = parseBooleanToken(query.all) === true
49
+ const isUnrestricted = isUnrestrictedOrganizationScope({
50
+ isSuperAdmin: auth.isSuperAdmin === true,
51
+ allowedOrganizationIds: scope?.allowedIds,
52
+ })
53
+ if (!isUnrestricted && (!organizationIds || organizationIds.length === 0)) {
54
+ logger.warn('customers.interactions.tasks.list collapsed organization scope', {
55
+ tenantId: auth.tenantId,
56
+ organizationIds,
57
+ selectedId: scope?.selectedId ?? null,
58
+ allowedIdsCount: scope?.allowedIds?.length ?? null,
59
+ })
60
+ return NextResponse.json({
61
+ items: [],
62
+ total: 0,
63
+ page: exportAll ? 1 : query.page,
64
+ pageSize: exportAll ? 0 : query.pageSize,
65
+ totalPages: 1,
66
+ })
67
+ }
48
68
  const search = normalizeTodoSearch(query.search)
49
69
  const queryEngine = container.resolve('queryEngine') as QueryEngine
50
70
 
@@ -59,6 +79,7 @@ export async function GET(request: Request): Promise<Response> {
59
79
  entityId: query.entityId,
60
80
  pagination: exportAll ? null : { page: query.page, pageSize: query.pageSize },
61
81
  searchText: search,
82
+ isUnrestricted,
62
83
  },
63
84
  )
64
85
  const total = canonical.total
@@ -80,6 +101,7 @@ export async function GET(request: Request): Promise<Response> {
80
101
  const [legacyRows, canonicalRows] = await Promise.all([
81
102
  listLegacyTodoRows(em, queryEngine, auth.tenantId, organizationIds, query.entityId, {
82
103
  limit: legacyWindow,
104
+ isUnrestricted,
83
105
  }),
84
106
  listCanonicalTodoRows(
85
107
  em,
@@ -91,6 +113,7 @@ export async function GET(request: Request): Promise<Response> {
91
113
  entityId: query.entityId,
92
114
  includeDeleted: true,
93
115
  limit: legacyWindow,
116
+ isUnrestricted,
94
117
  },
95
118
  ),
96
119
  ])
@@ -234,7 +234,7 @@ export async function DELETE(req: Request, ctx: { params?: { id?: string; linkId
234
234
  const { translate } = await resolveTranslations()
235
235
  try {
236
236
  const { id, linkId } = paramsSchema.parse({ id: ctx.params?.id, linkId: ctx.params?.linkId })
237
- const { container, auth, selectedOrganizationId, person } = await loadPersonContext(req, id)
237
+ const { container, auth, selectedOrganizationId, person, profile } = await loadPersonContext(req, id)
238
238
  if (!selectedOrganizationId) {
239
239
  throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })
240
240
  }
@@ -262,18 +262,26 @@ export async function DELETE(req: Request, ctx: { params?: { id?: string; linkId
262
262
  auth.tenantId,
263
263
  selectedOrganizationId,
264
264
  )
265
- if (!resolvedLinkId) {
265
+ // No `CustomerPersonCompanyLink` row resolves for the profile-only association case
266
+ // (`CustomerPersonProfile.company` set without a link row, e.g. from a CRM migration).
267
+ // The delete command accepts that shape too, so it is dispatched instead of 404ing and
268
+ // the detach keeps its audit entry, undo token and cache invalidation (#5114).
269
+ const isProfileOnlyMatch =
270
+ profile.company && typeof profile.company !== 'string' && profile.company.id === linkId
271
+ if (!resolvedLinkId && !isProfileOnlyMatch) {
266
272
  throw notFound(translate('customers.errors.person_company_link_not_found', 'Person-company link not found'))
267
273
  }
268
274
 
269
275
  const commandInput = personCompanyLinkDeleteSchema.parse({
270
- linkId: resolvedLinkId,
276
+ ...(resolvedLinkId
277
+ ? { linkId: resolvedLinkId }
278
+ : { personEntityId: person.id, companyEntityId: linkId }),
271
279
  tenantId: auth.tenantId,
272
280
  organizationId: selectedOrganizationId,
273
281
  } satisfies PersonCompanyLinkDeleteInput)
274
282
 
275
283
  const commandBus = container.resolve('commandBus') as CommandBus
276
- const { result, logEntry } = await commandBus.execute<PersonCompanyLinkDeleteInput, { linkId: string }>(
284
+ const { result, logEntry } = await commandBus.execute<PersonCompanyLinkDeleteInput, { linkId: string | null }>(
277
285
  'customers.personCompanyLinks.delete',
278
286
  {
279
287
  input: commandInput,
@@ -38,8 +38,11 @@ export async function loadPersonContext(req: Request, personId: string) {
38
38
  throw notFound(translate('customers.errors.person_not_found', 'Person not found'))
39
39
  }
40
40
 
41
+ // Existence oracle (#5504): deny a cross-org read as not-found — identical to
42
+ // the parent-not-found above — so it cannot reveal that a person exists in an
43
+ // organization the caller cannot see.
41
44
  if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {
42
- throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })
45
+ throw notFound(translate('customers.errors.person_not_found', 'Person not found'))
43
46
  }
44
47
 
45
48
  const profile = await findOneWithDecryption(
@@ -178,8 +178,11 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
178
178
  throw notFound(translate('customers.errors.person_not_found', 'Person not found'))
179
179
  }
180
180
 
181
+ // Existence oracle (#5504): deny a cross-org read as not-found — identical to
182
+ // the parent-not-found above — so it cannot reveal that a person exists in an
183
+ // organization the caller cannot see.
181
184
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
182
- throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })
185
+ throw notFound(translate('customers.errors.person_not_found', 'Person not found'))
183
186
  }
184
187
 
185
188
  const entityScope = { tenantId: auth.tenantId, organizationId: person.organizationId }
@@ -38,7 +38,7 @@ import type { EntityId } from '@open-mercato/shared/modules/entities'
38
38
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
39
39
  import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
40
40
  import { parseBooleanFromUnknown, parseBooleanToken } from '@open-mercato/shared/lib/boolean'
41
- import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
41
+ import { denyCustomerDetailReadAsNotFound } from '../../../lib/detailReadAccess'
42
42
  import { loadPersonCompanyLinks, summarizePersonCompanies } from '../../../lib/personCompanies'
43
43
  import { normalizeCustomerDetailCustomFields } from '../../detailCustomFields'
44
44
  import { buildEmailVisibilityMikroFilter } from '../../../lib/visibilityFilter'
@@ -129,10 +129,6 @@ function parseIncludeParams(request: Request): Set<string> {
129
129
  return tokens
130
130
  }
131
131
 
132
- function forbidden(message: string) {
133
- return NextResponse.json({ error: message }, { status: 403 })
134
- }
135
-
136
132
  function notFound(message: string) {
137
133
  return NextResponse.json({ error: message }, { status: 404 })
138
134
  }
@@ -542,10 +538,19 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
542
538
  return notFound('Person not found')
543
539
  }
544
540
 
545
- if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
546
- statusCode = 403
547
- profileMeta = { reason: 'organization_forbidden' }
548
- return forbidden('Access denied')
541
+ // Existence oracle (issue #5504): a caller who holds customers.people.view
542
+ // but whose scope excludes the record's organization must get the SAME
543
+ // response as for a non-existent id, so 403-when-present / 404-when-absent
544
+ // collapses to a uniform 404 not-found. The dispatcher already returns a
545
+ // uniform 403 for callers who lack the feature entirely.
546
+ const organizationReadDenied = denyCustomerDetailReadAsNotFound(
547
+ { scope, auth, organizationId: person.organizationId },
548
+ 'Person not found',
549
+ )
550
+ if (organizationReadDenied) {
551
+ statusCode = 404
552
+ profileMeta = { reason: 'organization_not_in_scope' }
553
+ return organizationReadDenied
549
554
  }
550
555
 
551
556
  // Gated get-then-set cache. Key on the resolved person plus the effective
@@ -1337,8 +1342,8 @@ export const openApi: OpenApiRouteDoc = {
1337
1342
  errors: [
1338
1343
  { status: 400, description: 'Invalid identifier', schema: personDetailErrorSchema },
1339
1344
  { status: 401, description: 'Unauthorized', schema: personDetailErrorSchema },
1340
- { status: 403, description: 'Forbidden for tenant/organization scope', schema: personDetailErrorSchema },
1341
- { status: 404, description: 'Person not found', schema: personDetailErrorSchema },
1345
+ { status: 403, description: 'Forbidden caller lacks the required feature', schema: personDetailErrorSchema },
1346
+ { status: 404, description: 'Person not found, or its organization is not in the caller’s scope', schema: personDetailErrorSchema },
1342
1347
  ],
1343
1348
  },
1344
1349
  },