@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
@@ -0,0 +1,46 @@
1
+ import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'
2
+
3
+ /**
4
+ * The canonical customer record. Every customer also has a person or company profile indexed under
5
+ * its own entity type, and it is the profile — not the base row — that carries the detail-page
6
+ * navigation.
7
+ */
8
+ export const CUSTOMERS_BASE_ENTITY_TYPE = 'customers:customer_entity'
9
+
10
+ /**
11
+ * Whether base customer rows are offered as search results.
12
+ *
13
+ * Why the knob exists: `search_tokens` is written by the query index for **every** entity a CRUD
14
+ * route indexes, independently of any module's `search.ts` config. `customers:customer_entity` is
15
+ * indexed but no module registers it for search, so its rows only ever surface a second,
16
+ * navigation-less copy of a customer who is already represented by their person or company
17
+ * profile — the duplicate reported in #5046. Worse, the duplicate was visible to superadmins only:
18
+ * `resolveReadableEntityTypes` narrows every other caller to entity types a module registered for
19
+ * search, so the same query returned different shapes for different users.
20
+ *
21
+ * Default `false`: the token strategy never returns base customer rows, so a customer appears
22
+ * exactly once, as their profile. `OM_SEARCH_CUSTOMERS_INDEX_BASE_ENTITY=true` restores the
23
+ * previous behavior, where both rows are returned and the presenter enricher's linked-result merge
24
+ * collapses the pair into the canonical entity result.
25
+ *
26
+ * Deliberately a **read-side** filter. `search_tokens` is not only the token-search index: the
27
+ * People and Companies list routes resolve their search box through
28
+ * `findEntityIdsBySearchTokens` on this exact entity type, and both query engines rewrite
29
+ * `like`/`ilike` on encrypted customer columns into the same table. Dropping the rows would
30
+ * silently break list search on `display_name`, `primary_email`, `description` and friends, so the
31
+ * rows stay and only the search strategy stops offering them.
32
+ */
33
+ export function indexesCustomerBaseEntity(): boolean {
34
+ return parseBooleanWithDefault(process.env.OM_SEARCH_CUSTOMERS_INDEX_BASE_ENTITY, false)
35
+ }
36
+
37
+ /**
38
+ * Entity types the token search strategy must not return.
39
+ *
40
+ * Exposed as a list so the strategy can express the exclusion as a single `NOT IN` predicate
41
+ * instead of post-filtering rows it already paid to fetch — a post-filter would silently shrink
42
+ * the caller's `limit`.
43
+ */
44
+ export function listSearchTokenExcludedEntityTypes(): string[] {
45
+ return indexesCustomerBaseEntity() ? [] : [CUSTOMERS_BASE_ENTITY_TYPE]
46
+ }
@@ -223,6 +223,49 @@ export async function deleteSearchTokensForRecord(
223
223
  .execute()
224
224
  }
225
225
 
226
+ // NUL, not a printable separator: a field name may itself contain a space, so `a b` + hash `c`
227
+ // would otherwise sign identically to field `a` + hash `b c`.
228
+ const SIGNATURE_SEPARATOR = String.fromCharCode(0)
229
+
230
+ // Identifies one token row for comparison. `token` is NULL unless `storeRawTokens` is on, and a
231
+ // stored NULL has to sign the same as the `null` a freshly built row carries — otherwise every
232
+ // record compares as changed and the skip never fires.
233
+ function tokenSignature(row: { field?: unknown; token_hash?: unknown; token?: unknown }): string {
234
+ return [
235
+ String(row.field ?? ''),
236
+ String(row.token_hash ?? ''),
237
+ row.token == null ? '' : String(row.token),
238
+ ].join(SIGNATURE_SEPARATOR)
239
+ }
240
+
241
+ // Multiplicities, not sets: #4681 reports token rows duplicated by the concurrent-replacement
242
+ // defect, and a set comparison reads such a record as already correct and preserves the duplicates
243
+ // forever. Counting sends it through a full rewrite, which collapses them.
244
+ function tallyEquals(a: Map<string, number> | undefined, b: Map<string, number> | undefined): boolean {
245
+ const left = a ?? new Map<string, number>()
246
+ const right = b ?? new Map<string, number>()
247
+ if (left.size !== right.size) return false
248
+ for (const [key, count] of left.entries()) {
249
+ if (right.get(key) !== count) return false
250
+ }
251
+ return true
252
+ }
253
+
254
+ function tallyTokenRows<TRow extends { field?: unknown; token_hash?: unknown; token?: unknown }>(
255
+ rows: Iterable<TRow>,
256
+ keyOf: (row: TRow) => string
257
+ ): Map<string, Map<string, number>> {
258
+ const tallies = new Map<string, Map<string, number>>()
259
+ for (const row of rows) {
260
+ const key = keyOf(row)
261
+ const tally = tallies.get(key) ?? new Map<string, number>()
262
+ const signature = tokenSignature(row)
263
+ tally.set(signature, (tally.get(signature) ?? 0) + 1)
264
+ tallies.set(key, tally)
265
+ }
266
+ return tallies
267
+ }
268
+
226
269
  export async function replaceSearchTokensForBatch(
227
270
  db: Kysely<any>,
228
271
  payloads: Array<BuildTokenOptions & { doc: Record<string, unknown> }>
@@ -256,8 +299,95 @@ export async function replaceSearchTokensForBatch(
256
299
  scopeBuckets.set(key, bucket)
257
300
  }
258
301
 
302
+ const recordKeyOf = (row: SearchTokenRow) =>
303
+ `${scopeKey(row.organization_id ?? null, row.tenant_id ?? null)}|${String(row.entity_id)}`
304
+ const builtTally = tallyTokenRows(rows, recordKeyOf)
305
+
306
+ // Read outside the transaction, deliberately. The comparison decides only whether to skip a
307
+ // rewrite, so a concurrent writer costs us at most a rewrite we declined — declined because the
308
+ // table already held exactly the rows this call wanted to write. One ordering is worth naming
309
+ // though: if the read matches and a concurrent writer then commits tokens built from a *staler*
310
+ // doc, the unconditional rewrite this call used to perform would have overwritten them by
311
+ // accident. It no longer does, so those stale rows survive until the record's next write. That
312
+ // is a repair we lose, not a guarantee we break.
313
+ const changedIdsByBucket = new Map<string, Set<string>>()
314
+ for (const [key, bucket] of scopeBuckets.entries()) {
315
+ const ids = Array.from(bucket.ids)
316
+ const builtCountById = new Map<string, number>()
317
+ for (const id of ids) {
318
+ let total = 0
319
+ const tally = builtTally.get(`${key}|${id}`)
320
+ if (tally) for (const count of tally.values()) total += count
321
+ builtCountById.set(id, total)
322
+ }
323
+
324
+ // Count probe first. Its result is one row per record in the batch, so it is bounded by the
325
+ // batch size — unlike a bare row read, which would be bounded only by how many token rows the
326
+ // table already holds for these ids, a quantity this function does not control and (per #4681)
327
+ // has no reason to trust.
328
+ const storedCounts = await db
329
+ .selectFrom('search_tokens' as any)
330
+ .select(['entity_id' as any, sql<number>`count(*)`.as('token_count') as any])
331
+ .where('entity_type' as any, '=', payloads[0].entityType)
332
+ .where(sql<boolean>`organization_id is not distinct from ${bucket.organizationId}`)
333
+ .where(sql<boolean>`tenant_id is not distinct from ${bucket.tenantId}`)
334
+ .where('entity_id' as any, 'in', ids)
335
+ .groupBy('entity_id' as any)
336
+ .execute()
337
+ const storedCountById = new Map<string, number>()
338
+ for (const row of storedCounts as any[]) {
339
+ storedCountById.set(String(row.entity_id), Number(row.token_count))
340
+ }
341
+
342
+ const changed = new Set<string>()
343
+ // A record whose stored row count already differs is changed, whatever the rows say — the
344
+ // duplicate case from #4681 resolves here without ever materializing the duplicated rows.
345
+ const contentCandidates = ids.filter((id) => {
346
+ const builtCount = builtCountById.get(id) ?? 0
347
+ if ((storedCountById.get(id) ?? 0) !== builtCount) {
348
+ changed.add(id)
349
+ return false
350
+ }
351
+ return builtCount > 0
352
+ })
353
+
354
+ if (contentCandidates.length) {
355
+ const rowBudget = contentCandidates.reduce((sum, id) => sum + (builtCountById.get(id) ?? 0), 0)
356
+ const stored = await db
357
+ .selectFrom('search_tokens' as any)
358
+ .select(['entity_id' as any, 'field' as any, 'token_hash' as any, 'token' as any])
359
+ .where('entity_type' as any, '=', payloads[0].entityType)
360
+ .where(sql<boolean>`organization_id is not distinct from ${bucket.organizationId}`)
361
+ .where(sql<boolean>`tenant_id is not distinct from ${bucket.tenantId}`)
362
+ .where('entity_id' as any, 'in', contentCandidates)
363
+ // Counts already match, so this cannot truncate — it bounds the damage if a concurrent
364
+ // writer inserts between the probe and this read. A truncated read compares as changed,
365
+ // which costs a rewrite rather than a wrong skip.
366
+ .limit(rowBudget)
367
+ .execute()
368
+ const storedTally = tallyTokenRows(stored as any[], (row) => String(row.entity_id))
369
+ for (const id of contentCandidates) {
370
+ if (!tallyEquals(builtTally.get(`${key}|${id}`), storedTally.get(id))) changed.add(id)
371
+ }
372
+ }
373
+ changedIdsByBucket.set(key, changed)
374
+ }
375
+
376
+ const changedRecordKeys = new Set<string>()
377
+ for (const [key, changed] of changedIdsByBucket.entries()) {
378
+ for (const id of changed) changedRecordKeys.add(`${key}|${id}`)
379
+ }
380
+ debug('batch.skip', {
381
+ entityType: payloads[0].entityType,
382
+ recordCount: payloads.length,
383
+ changedCount: changedRecordKeys.size,
384
+ })
385
+ if (!changedRecordKeys.size) return
386
+
259
387
  await db.transaction().execute(async (trx) => {
260
- for (const [, bucket] of scopeBuckets.entries()) {
388
+ for (const [key, bucket] of scopeBuckets.entries()) {
389
+ const changed = changedIdsByBucket.get(key)
390
+ if (!changed?.size) continue
261
391
  // Delete by entity_id: a batch replaces all of a record's tokens, and a per-field OR over the
262
392
  // whole batch overflows the query compiler's call stack on large batches.
263
393
  const deleteQuery = trx
@@ -265,10 +395,12 @@ export async function replaceSearchTokensForBatch(
265
395
  .where('entity_type' as any, '=', payloads[0].entityType)
266
396
  .where(sql<boolean>`organization_id is not distinct from ${bucket.organizationId}`)
267
397
  .where(sql<boolean>`tenant_id is not distinct from ${bucket.tenantId}`)
268
- .where('entity_id' as any, 'in', Array.from(bucket.ids))
398
+ .where('entity_id' as any, 'in', Array.from(changed))
269
399
  await deleteQuery.execute()
270
400
  }
271
- const payloadWithTimestamps = rows.map((row) => ({ ...row, created_at: sql`now()` }))
401
+ const payloadWithTimestamps = rows
402
+ .filter((row) => changedRecordKeys.has(recordKeyOf(row)))
403
+ .map((row) => ({ ...row, created_at: sql`now()` }))
272
404
  for (const batch of chunk(payloadWithTimestamps, INSERT_BATCH_SIZE)) {
273
405
  await trx.insertInto('search_tokens' as any).values(batch as any).execute()
274
406
  }
@@ -45,6 +45,7 @@ type ResourceTypesResponse = {
45
45
  items?: Array<Record<string, unknown>>
46
46
  total?: number
47
47
  totalPages?: number
48
+ totalIsCapped?: boolean
48
49
  }
49
50
 
50
51
  type ResourceTypesMutationContext = {
@@ -63,6 +64,7 @@ export default function ResourcesResourceTypesPage() {
63
64
  const [page, setPage] = React.useState(1)
64
65
  const [total, setTotal] = React.useState(0)
65
66
  const [totalPages, setTotalPages] = React.useState(1)
67
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
66
68
  const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])
67
69
  const [search, setSearch] = React.useState('')
68
70
  const [isLoading, setIsLoading] = React.useState(true)
@@ -232,6 +234,7 @@ export default function ResourcesResourceTypesPage() {
232
234
  setRows(items.map(mapApiResourceType))
233
235
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
234
236
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))
237
+ setTotalIsCapped(payload?.totalIsCapped === true)
235
238
  } catch (error) {
236
239
  logger.error('Failed to list resource types', { err: error })
237
240
  flash(translations.errors.load, 'error')
@@ -308,7 +311,7 @@ export default function ResourcesResourceTypesPage() {
308
311
  sortable
309
312
  sorting={sorting}
310
313
  onSortingChange={setSorting}
311
- pagination={{ page, pageSize: PAGE_SIZE, total, totalPages, onPageChange: setPage }}
314
+ pagination={{ page, pageSize: PAGE_SIZE, total, totalPages, totalIsCapped, onPageChange: setPage }}
312
315
  rowActions={(row) => (
313
316
  <RowActions
314
317
  items={[
@@ -63,6 +63,7 @@ type ResourcesResponse = {
63
63
  total: number
64
64
  page: number
65
65
  totalPages: number
66
+ totalIsCapped?: boolean
66
67
  }
67
68
 
68
69
  type ResourceTypesResponse = {
@@ -81,6 +82,7 @@ export default function ResourcesResourcesPage() {
81
82
  const [page, setPage] = React.useState(1)
82
83
  const [total, setTotal] = React.useState(0)
83
84
  const [totalPages, setTotalPages] = React.useState(1)
85
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
84
86
  const [search, setSearch] = React.useState('')
85
87
  const [filterValues, setFilterValues] = React.useState<FilterValues>({})
86
88
  const [isLoading, setIsLoading] = React.useState(true)
@@ -360,6 +362,7 @@ export default function ResourcesResourcesPage() {
360
362
  setRows(mapped)
361
363
  setTotal(payload.total || 0)
362
364
  setTotalPages(payload.totalPages || 1)
365
+ setTotalIsCapped(payload?.totalIsCapped === true)
363
366
  }
364
367
  } catch (error) {
365
368
  if (!cancelled) {
@@ -546,7 +549,7 @@ export default function ResourcesResourcesPage() {
546
549
  createLabel={t('resources.resources.list.actions.create', 'New resource')}
547
550
  />
548
551
  )}
549
- pagination={{ page, pageSize: PAGE_SIZE, total, totalPages, onPageChange: setPage }}
552
+ pagination={{ page, pageSize: PAGE_SIZE, total, totalPages, totalIsCapped, onPageChange: setPage }}
550
553
  isLoading={isLoading}
551
554
  />
552
555
  </PageBody>
@@ -29,6 +29,7 @@ type OffersResponse = {
29
29
  items?: Array<Record<string, unknown>>
30
30
  total?: number
31
31
  totalPages?: number
32
+ totalIsCapped?: boolean
32
33
  }
33
34
 
34
35
  const PAGE_SIZE = 25
@@ -61,6 +62,7 @@ export default function SalesChannelOffersListPage() {
61
62
  const [page, setPage] = React.useState(1)
62
63
  const [total, setTotal] = React.useState(0)
63
64
  const [totalPages, setTotalPages] = React.useState(1)
65
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
64
66
  const [sorting, setSorting] = React.useState<SortingState>([{ id: 'updatedAt', desc: true }])
65
67
  const [search, setSearch] = React.useState('')
66
68
  const [filterValues, setFilterValues] = React.useState<FilterValues>({})
@@ -271,6 +273,7 @@ export default function SalesChannelOffersListPage() {
271
273
  setRows(mapped)
272
274
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
273
275
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))
276
+ setTotalIsCapped(payload?.totalIsCapped === true)
274
277
  const ids = mapped
275
278
  .map((row) => row.channelId)
276
279
  .filter((value): value is string => typeof value === 'string' && value.length > 0)
@@ -362,6 +365,7 @@ export default function SalesChannelOffersListPage() {
362
365
  pageSize: PAGE_SIZE,
363
366
  total,
364
367
  totalPages,
368
+ totalIsCapped,
365
369
  onPageChange: setPage,
366
370
  }}
367
371
  refreshButton={{
@@ -37,6 +37,7 @@ type ChannelsResponse = {
37
37
  items?: Array<Record<string, unknown>>
38
38
  total?: number
39
39
  totalPages?: number
40
+ totalIsCapped?: boolean
40
41
  }
41
42
 
42
43
  const PAGE_SIZE = 25
@@ -68,6 +69,7 @@ export default function SalesChannelsPage() {
68
69
  const [page, setPage] = React.useState(1)
69
70
  const [total, setTotal] = React.useState(0)
70
71
  const [totalPages, setTotalPages] = React.useState(1)
72
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
71
73
  const [sorting, setSorting] = React.useState<SortingState>([])
72
74
  const [search, setSearch] = React.useState('')
73
75
  const [isLoading, setLoading] = React.useState(true)
@@ -142,6 +144,7 @@ export default function SalesChannelsPage() {
142
144
  setRows(items.map(mapApiChannel))
143
145
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
144
146
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))
147
+ setTotalIsCapped(payload?.totalIsCapped === true)
145
148
  } catch (err) {
146
149
  logger.error('sales.channels.list', { err })
147
150
  flash(t('sales.channels.table.errors.load', 'Failed to load channels.'), 'error')
@@ -230,6 +233,7 @@ export default function SalesChannelsPage() {
230
233
  pageSize: PAGE_SIZE,
231
234
  total,
232
235
  totalPages,
236
+ totalIsCapped,
233
237
  onPageChange: setPage,
234
238
  }}
235
239
  refreshButton={{
@@ -38,7 +38,7 @@ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimi
38
38
  import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
39
39
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
40
40
  import { mapCrudServerErrorToFormErrors } from '@open-mercato/ui/backend/utils/serverErrors'
41
- import { useT } from '@open-mercato/shared/lib/i18n/context'
41
+ import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
42
42
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
43
43
  import { cn } from '@open-mercato/shared/lib/utils'
44
44
  import { ContactEmailDisplay } from '@open-mercato/core/modules/sales/components/ContactEmailDisplay'
@@ -79,13 +79,17 @@ import { createLogger } from '@open-mercato/shared/lib/logger'
79
79
 
80
80
  const logger = createLogger('sales')
81
81
 
82
- function formatMessageAmount(amount: number | null | undefined, currency: string | null | undefined): string | null {
82
+ function formatMessageAmount(
83
+ amount: number | null | undefined,
84
+ currency: string | null | undefined,
85
+ locale?: string
86
+ ): string | null {
83
87
  if (typeof amount !== 'number' || !Number.isFinite(amount)) return null
84
- if (!currency) return amount.toLocaleString()
88
+ if (!currency) return amount.toLocaleString(locale)
85
89
  try {
86
- return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(amount)
90
+ return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount)
87
91
  } catch {
88
- return `${amount.toLocaleString()} ${currency}`
92
+ return `${amount.toLocaleString(locale)} ${currency}`
89
93
  }
90
94
  }
91
95
 
@@ -1900,6 +1904,7 @@ export default function SalesDocumentDetailPage({
1900
1904
  includeAmountInMessageMetadata?: boolean
1901
1905
  }) {
1902
1906
  const t = useT()
1907
+ const locale = useLocale()
1903
1908
  const { enabled: channelsEnabled } = useSalesChannelsEnabled()
1904
1909
  const router = useRouter()
1905
1910
  const searchParams = useSearchParams()
@@ -2807,7 +2812,7 @@ export default function SalesDocumentDetailPage({
2807
2812
  : null
2808
2813
  const contactEmail = resolveCustomerEmail(customerSnapshot) ?? metadataEmail ?? record?.contactEmail ?? null
2809
2814
  const statusDisplay = record?.status ? statusDictionaryMap[record.status] ?? null : null
2810
- const previewAmount = formatMessageAmount(record?.grandTotalGrossAmount ?? null, record?.currencyCode ?? null)
2815
+ const previewAmount = formatMessageAmount(record?.grandTotalGrossAmount ?? null, record?.currencyCode ?? null, locale)
2811
2816
  const messagePreviewMetadata: Record<string, string> = {}
2812
2817
  if (includeAmountInMessageMetadata && previewAmount) {
2813
2818
  messagePreviewMetadata[t('sales.documents.detail.totals.grandTotalGross')] = previewAmount
@@ -3867,7 +3872,7 @@ export default function SalesDocumentDetailPage({
3867
3872
  renderDisplay: (params) => {
3868
3873
  const { value, emptyLabel } = params
3869
3874
  if (value && value.length) {
3870
- return <span className="text-sm text-muted-foreground">{new Date(value).toLocaleDateString()}</span>
3875
+ return <span className="text-sm text-muted-foreground">{new Date(value).toLocaleDateString(locale)}</span>
3871
3876
  }
3872
3877
  return <span className="text-sm text-muted-foreground">{emptyLabel}</span>
3873
3878
  },
@@ -3944,6 +3949,7 @@ export default function SalesDocumentDetailPage({
3944
3949
  shippingMethodLoading,
3945
3950
  shippingMethodOptions,
3946
3951
  t,
3952
+ locale,
3947
3953
  kind,
3948
3954
  saveShortcutLabel,
3949
3955
  ])
@@ -4717,7 +4723,7 @@ export default function SalesDocumentDetailPage({
4717
4723
  renderDisplay={({ value, emptyLabel }) =>
4718
4724
  value && value.length ? (
4719
4725
  <span className="text-sm text-muted-foreground">
4720
- {new Date(value).toLocaleDateString()}
4726
+ {new Date(value).toLocaleDateString(locale)}
4721
4727
  </span>
4722
4728
  ) : (
4723
4729
  <span className="text-sm text-muted-foreground">{emptyLabel}</span>
@@ -584,6 +584,11 @@ const addressSnapshotSchema = z
584
584
  .nullable()
585
585
  .optional();
586
586
 
587
+ // Mirrors the create schema's `decimal({ min: 0 })` (data/validators.ts). `null`
588
+ // comes first in the union below because the coercion accepts it — `Number(null)`
589
+ // is 0 — and would turn a clear into a written zero.
590
+ const exchangeRateSchema = z.coerce.number().min(0);
591
+
587
592
  export const documentUpdateSchema = z
588
593
  .object({
589
594
  id: z.string().uuid(),
@@ -602,6 +607,13 @@ export const documentUpdateSchema = z
602
607
  orderNumber: z.string().trim().min(1).max(191).optional(),
603
608
  quoteNumber: z.string().trim().min(1).max(191).optional(),
604
609
  currencyCode: currencyCodeSchema.optional(),
610
+ // Order-only columns — SalesQuote declares none of them. Declared here
611
+ // because the create schema declares them and a caller reusing its create
612
+ // payload on an update otherwise lost them silently; `applyDocumentUpdate`
613
+ // rejects each with a 400 on a quote rather than dropping it.
614
+ exchangeRate: z.union([z.null(), exchangeRateSchema]).optional(),
615
+ paymentStatusEntryId: z.string().uuid().nullable().optional(),
616
+ fulfillmentStatusEntryId: z.string().uuid().nullable().optional(),
605
617
  channelId: z.string().uuid().nullable().optional(),
606
618
  statusEntryId: z.string().uuid().nullable().optional(),
607
619
  placedAt: z.union([dateOnlySchema, z.null()]).optional(),
@@ -633,6 +645,9 @@ export const documentUpdateSchema = z
633
645
  input.expectedDeliveryAt !== undefined ||
634
646
  input.channelId !== undefined ||
635
647
  input.statusEntryId !== undefined ||
648
+ input.exchangeRate !== undefined ||
649
+ input.paymentStatusEntryId !== undefined ||
650
+ input.fulfillmentStatusEntryId !== undefined ||
636
651
  input.shippingAddressId !== undefined ||
637
652
  input.billingAddressId !== undefined ||
638
653
  input.customerEntityId !== undefined ||
@@ -1198,6 +1213,19 @@ async function applyDocumentUpdate({
1198
1213
  if (typeof input.currencyCode === "string") {
1199
1214
  entity.currencyCode = input.currencyCode;
1200
1215
  }
1216
+ // Orders only — SalesQuote has no exchange_rate column, so the field is
1217
+ // rejected rather than ignored for the same reason as internalNotes above.
1218
+ if (kind === "quote" && input.exchangeRate !== undefined) {
1219
+ throw new CrudHttpError(400, {
1220
+ error: translate(
1221
+ "sales.quotes.exchange_rate_unsupported",
1222
+ "Exchange rate is not available on quotes.",
1223
+ ),
1224
+ });
1225
+ }
1226
+ if (kind === "order" && input.exchangeRate !== undefined) {
1227
+ (entity as SalesOrder).exchangeRate = toNumericString(input.exchangeRate);
1228
+ }
1201
1229
  if (input.channelId !== undefined) {
1202
1230
  if (input.channelId === null) {
1203
1231
  entity.channelId = null;
@@ -1242,6 +1270,62 @@ async function applyDocumentUpdate({
1242
1270
  (entity as any).statusEntryId = input.statusEntryId ?? null;
1243
1271
  (entity as any).status = statusValue;
1244
1272
  }
1273
+ // Orders only — SalesQuote has neither the entry-id nor the derived text
1274
+ // column for payment and fulfillment status, so both are rejected on a quote.
1275
+ if (kind === "quote" && input.paymentStatusEntryId !== undefined) {
1276
+ throw new CrudHttpError(400, {
1277
+ error: translate(
1278
+ "sales.quotes.payment_status_unsupported",
1279
+ "Payment status is not available on quotes.",
1280
+ ),
1281
+ });
1282
+ }
1283
+ if (kind === "order" && input.paymentStatusEntryId !== undefined) {
1284
+ const paymentStatusValue = await resolveDictionaryEntryValue(
1285
+ em,
1286
+ input.paymentStatusEntryId,
1287
+ { tenantId },
1288
+ );
1289
+ if (input.paymentStatusEntryId && !paymentStatusValue) {
1290
+ throw new CrudHttpError(400, {
1291
+ error: translate(
1292
+ "sales.documents.detail.statusInvalid",
1293
+ "Selected status could not be found.",
1294
+ ),
1295
+ });
1296
+ }
1297
+ // Both columns, like the statusEntryId branch above and like order create:
1298
+ // moving the id without its derived text leaves a self-contradicting row.
1299
+ (entity as SalesOrder).paymentStatusEntryId =
1300
+ input.paymentStatusEntryId ?? null;
1301
+ (entity as SalesOrder).paymentStatus = paymentStatusValue;
1302
+ }
1303
+ if (kind === "quote" && input.fulfillmentStatusEntryId !== undefined) {
1304
+ throw new CrudHttpError(400, {
1305
+ error: translate(
1306
+ "sales.quotes.fulfillment_status_unsupported",
1307
+ "Fulfillment status is not available on quotes.",
1308
+ ),
1309
+ });
1310
+ }
1311
+ if (kind === "order" && input.fulfillmentStatusEntryId !== undefined) {
1312
+ const fulfillmentStatusValue = await resolveDictionaryEntryValue(
1313
+ em,
1314
+ input.fulfillmentStatusEntryId,
1315
+ { tenantId },
1316
+ );
1317
+ if (input.fulfillmentStatusEntryId && !fulfillmentStatusValue) {
1318
+ throw new CrudHttpError(400, {
1319
+ error: translate(
1320
+ "sales.documents.detail.statusInvalid",
1321
+ "Selected status could not be found.",
1322
+ ),
1323
+ });
1324
+ }
1325
+ (entity as SalesOrder).fulfillmentStatusEntryId =
1326
+ input.fulfillmentStatusEntryId ?? null;
1327
+ (entity as SalesOrder).fulfillmentStatus = fulfillmentStatusValue;
1328
+ }
1245
1329
  if (input.placedAt !== undefined) {
1246
1330
  if (input.placedAt === null) {
1247
1331
  entity.placedAt = null;
@@ -3732,6 +3816,15 @@ function buildDocumentUpdateChangeKeys(kind: SalesDocumentKind, input: DocumentU
3732
3816
  if (input.internalNotes !== undefined) keys.add("internalNotes");
3733
3817
  if (input.placedAt !== undefined) keys.add("placedAt");
3734
3818
  if (input.expectedDeliveryAt !== undefined) keys.add("expectedDeliveryAt");
3819
+ if (input.exchangeRate !== undefined) keys.add("exchangeRate");
3820
+ if (input.paymentStatusEntryId !== undefined) {
3821
+ keys.add("paymentStatusEntryId");
3822
+ keys.add("paymentStatus");
3823
+ }
3824
+ if (input.fulfillmentStatusEntryId !== undefined) {
3825
+ keys.add("fulfillmentStatusEntryId");
3826
+ keys.add("fulfillmentStatus");
3827
+ }
3735
3828
  }
3736
3829
  if (
3737
3830
  input.shippingAddressId !== undefined ||
@@ -1,19 +1,26 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { useOptionalLocale } from '@open-mercato/shared/lib/i18n/context'
4
5
  import { cn } from '@open-mercato/shared/lib/utils'
5
6
 
7
+ /**
8
+ * Pass `locale` to format in the application locale (`useLocale()` in client components).
9
+ * Omitting it keeps the runtime default, which varies per machine and is therefore not
10
+ * assertable in tests.
11
+ */
6
12
  export function formatPriceWithCurrency(
7
13
  amount: number | string | null | undefined,
8
14
  currency: string | null | undefined,
9
- fallback = '—'
15
+ fallback = '—',
16
+ locale?: string
10
17
  ): string {
11
18
  if (amount === null || amount === undefined) return fallback
12
19
  const parsed = typeof amount === 'string' ? Number(amount) : amount
13
20
  if (Number.isNaN(parsed)) return fallback
14
21
  if (currency) {
15
22
  try {
16
- return new Intl.NumberFormat(undefined, {
23
+ return new Intl.NumberFormat(locale, {
17
24
  style: 'currency',
18
25
  currency,
19
26
  minimumFractionDigits: 2,
@@ -23,7 +30,7 @@ export function formatPriceWithCurrency(
23
30
  // fall through to plain number formatting
24
31
  }
25
32
  }
26
- return parsed.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
33
+ return parsed.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
27
34
  }
28
35
 
29
36
  type PriceWithCurrencyProps = {
@@ -34,9 +41,14 @@ type PriceWithCurrencyProps = {
34
41
  }
35
42
 
36
43
  export function PriceWithCurrency({ amount, currency, fallback = '—', className }: PriceWithCurrencyProps) {
44
+ // `useOptionalLocale` rather than `useLocale`: this component is deep-importable and had no i18n
45
+ // dependency at all before it started formatting in the app locale, so making it throw outside
46
+ // `I18nProvider` would narrow its mounting contract. Without a provider it falls back to the
47
+ // runtime default, which is exactly the behaviour it had before.
48
+ const locale = useOptionalLocale()
37
49
  const label = React.useMemo(
38
- () => formatPriceWithCurrency(amount, currency, fallback),
39
- [amount, currency, fallback]
50
+ () => formatPriceWithCurrency(amount, currency, fallback, locale),
51
+ [amount, currency, fallback, locale]
40
52
  )
41
53
  return <span className={cn('font-mono text-sm text-foreground', className)}>{label}</span>
42
54
  }
@@ -25,6 +25,7 @@ type OffersResponse = {
25
25
  items?: Array<Record<string, unknown>>
26
26
  total?: number
27
27
  totalPages?: number
28
+ totalIsCapped?: boolean
28
29
  }
29
30
 
30
31
  const PAGE_SIZE = 25
@@ -54,6 +55,7 @@ export function SalesChannelOffersPanel({ channelId, channelName }: { channelId:
54
55
  const [page, setPage] = React.useState(1)
55
56
  const [total, setTotal] = React.useState(0)
56
57
  const [totalPages, setTotalPages] = React.useState(1)
58
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
57
59
  const [search, setSearch] = React.useState('')
58
60
  const [isLoading, setLoading] = React.useState(true)
59
61
  const [sorting, setSorting] = React.useState<SortingState>([])
@@ -134,6 +136,7 @@ export function SalesChannelOffersPanel({ channelId, channelName }: { channelId:
134
136
  setRows(items.map(mapOfferRow))
135
137
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
136
138
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))
139
+ setTotalIsCapped(payload?.totalIsCapped === true)
137
140
  } catch (err) {
138
141
  logger.error('sales.channels.offers', { err })
139
142
  flash(t('sales.channels.offers.errors.load', 'Failed to load offers.'), 'error')
@@ -208,6 +211,7 @@ export function SalesChannelOffersPanel({ channelId, channelName }: { channelId:
208
211
  pageSize: PAGE_SIZE,
209
212
  total,
210
213
  totalPages,
214
+ totalIsCapped,
211
215
  onPageChange: setPage,
212
216
  }}
213
217
  refreshButton={{