@open-mercato/core 0.8.1-develop.7296.1.2111d779db → 0.8.1-develop.7441.1.b42958b98c

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 (2353) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/AGENTS.md +4 -5
  3. package/dist/generated/entities/availability_policy/index.js +41 -0
  4. package/dist/generated/entities/availability_policy/index.js.map +7 -0
  5. package/dist/generated/entities/communication_channel/index.js +3 -1
  6. package/dist/generated/entities/communication_channel/index.js.map +2 -2
  7. package/dist/generated/entities/customer_email_conversation_share/index.js +21 -0
  8. package/dist/generated/entities/customer_email_conversation_share/index.js.map +7 -0
  9. package/dist/generated/entities/customer_group/index.js +33 -0
  10. package/dist/generated/entities/customer_group/index.js.map +7 -0
  11. package/dist/generated/entities/customer_group_membership/index.js +29 -0
  12. package/dist/generated/entities/customer_group_membership/index.js.map +7 -0
  13. package/dist/generated/entities/customer_group_terms/index.js +33 -0
  14. package/dist/generated/entities/customer_group_terms/index.js.map +7 -0
  15. package/dist/generated/entities/customer_interaction/index.js +2 -0
  16. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  17. package/dist/generated/entities/form/index.js +35 -0
  18. package/dist/generated/entities/form/index.js.map +7 -0
  19. package/dist/generated/entities/form_access_audit/index.js +21 -0
  20. package/dist/generated/entities/form_access_audit/index.js.map +7 -0
  21. package/dist/generated/entities/form_attachment/index.js +29 -0
  22. package/dist/generated/entities/form_attachment/index.js.map +7 -0
  23. package/dist/generated/entities/form_consent_record/index.js +37 -0
  24. package/dist/generated/entities/form_consent_record/index.js.map +7 -0
  25. package/dist/generated/entities/form_distribution/index.js +47 -0
  26. package/dist/generated/entities/form_distribution/index.js.map +7 -0
  27. package/dist/generated/entities/form_invitation/index.js +49 -0
  28. package/dist/generated/entities/form_invitation/index.js.map +7 -0
  29. package/dist/generated/entities/form_submission/index.js +39 -0
  30. package/dist/generated/entities/form_submission/index.js.map +7 -0
  31. package/dist/generated/entities/form_submission_actor/index.js +23 -0
  32. package/dist/generated/entities/form_submission_actor/index.js.map +7 -0
  33. package/dist/generated/entities/form_submission_revision/index.js +29 -0
  34. package/dist/generated/entities/form_submission_revision/index.js.map +7 -0
  35. package/dist/generated/entities/form_version/index.js +37 -0
  36. package/dist/generated/entities/form_version/index.js.map +7 -0
  37. package/dist/generated/entities/forms_encryption_key/index.js +15 -0
  38. package/dist/generated/entities/forms_encryption_key/index.js.map +7 -0
  39. package/dist/generated/entities.ids.generated.js +27 -2
  40. package/dist/generated/entities.ids.generated.js.map +2 -2
  41. package/dist/generated/entity-fields-registry.js +269 -0
  42. package/dist/generated/entity-fields-registry.js.map +2 -2
  43. package/dist/helpers/integration/communicationChannelsFixtures.js +16 -0
  44. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  45. package/dist/helpers/integration/customerGroupsFixtures.js +88 -0
  46. package/dist/helpers/integration/customerGroupsFixtures.js.map +7 -0
  47. package/dist/helpers/integration/formsFixtures.js +448 -0
  48. package/dist/helpers/integration/formsFixtures.js.map +7 -0
  49. package/dist/modules/api_docs/api/get/version.js +19 -7
  50. package/dist/modules/api_docs/api/get/version.js.map +2 -2
  51. package/dist/modules/api_keys/api/keys/route.js +74 -169
  52. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  53. package/dist/modules/api_keys/backend/api-keys/create/page.js +1 -1
  54. package/dist/modules/api_keys/backend/api-keys/create/page.js.map +2 -2
  55. package/dist/modules/api_keys/commands/keys.js +140 -0
  56. package/dist/modules/api_keys/commands/keys.js.map +7 -0
  57. package/dist/modules/api_keys/services/apiKeyService.js +72 -43
  58. package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
  59. package/dist/modules/attachments/api/file/[id]/route.js +9 -2
  60. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  61. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +6 -2
  62. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  63. package/dist/modules/attachments/api/route.js +10 -4
  64. package/dist/modules/attachments/api/route.js.map +2 -2
  65. package/dist/modules/attachments/components/AttachmentLibrary.js +16 -6
  66. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  67. package/dist/modules/attachments/lib/access.js +1 -3
  68. package/dist/modules/attachments/lib/access.js.map +2 -2
  69. package/dist/modules/attachments/lib/requestScope.js +13 -4
  70. package/dist/modules/attachments/lib/requestScope.js.map +2 -2
  71. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js +6 -1
  72. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js.map +2 -2
  73. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +36 -6
  74. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +3 -3
  75. package/dist/modules/audit_logs/api/audit-logs/actions/route.js +6 -1
  76. package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
  77. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +34 -4
  78. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
  79. package/dist/modules/audit_logs/data/validators.js +1 -0
  80. package/dist/modules/audit_logs/data/validators.js.map +2 -2
  81. package/dist/modules/audit_logs/lib/actorSubject.js +65 -0
  82. package/dist/modules/audit_logs/lib/actorSubject.js.map +7 -0
  83. package/dist/modules/audit_logs/lib/replayAuthorization.js +41 -0
  84. package/dist/modules/audit_logs/lib/replayAuthorization.js.map +7 -0
  85. package/dist/modules/audit_logs/services/actionLogService.js +162 -39
  86. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  87. package/dist/modules/auth/api/features.js +1 -1
  88. package/dist/modules/auth/api/features.js.map +2 -2
  89. package/dist/modules/auth/cli.js +29 -17
  90. package/dist/modules/auth/cli.js.map +2 -2
  91. package/dist/modules/auth/commands/acl.js +25 -36
  92. package/dist/modules/auth/commands/acl.js.map +2 -2
  93. package/dist/modules/auth/commands/interceptors.js +22 -0
  94. package/dist/modules/auth/commands/interceptors.js.map +7 -0
  95. package/dist/modules/auth/commands/roles.js +421 -147
  96. package/dist/modules/auth/commands/roles.js.map +2 -2
  97. package/dist/modules/auth/commands/users.js +543 -69
  98. package/dist/modules/auth/commands/users.js.map +2 -2
  99. package/dist/modules/auth/lib/authorizationStateLocks.js +249 -0
  100. package/dist/modules/auth/lib/authorizationStateLocks.js.map +7 -0
  101. package/dist/modules/auth/lib/commandReplay.js +144 -0
  102. package/dist/modules/auth/lib/commandReplay.js.map +7 -0
  103. package/dist/modules/auth/lib/executionPrincipal.js +32 -25
  104. package/dist/modules/auth/lib/executionPrincipal.js.map +2 -2
  105. package/dist/modules/auth/lib/grantChecks.js +7 -2
  106. package/dist/modules/auth/lib/grantChecks.js.map +2 -2
  107. package/dist/modules/auth/lib/setup-app.js +85 -49
  108. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  109. package/dist/modules/auth/lib/tokenHash.js +13 -1
  110. package/dist/modules/auth/lib/tokenHash.js.map +2 -2
  111. package/dist/modules/auth/services/rbacService.js +386 -5
  112. package/dist/modules/auth/services/rbacService.js.map +3 -3
  113. package/dist/modules/availability/acl.js +16 -0
  114. package/dist/modules/availability/acl.js.map +7 -0
  115. package/dist/modules/availability/api/check/route.js +115 -0
  116. package/dist/modules/availability/api/check/route.js.map +7 -0
  117. package/dist/modules/availability/api/openapi.js +27 -0
  118. package/dist/modules/availability/api/openapi.js.map +7 -0
  119. package/dist/modules/availability/api/policies/resolve-preview/route.js +72 -0
  120. package/dist/modules/availability/api/policies/resolve-preview/route.js.map +7 -0
  121. package/dist/modules/availability/api/policies/route.js +203 -0
  122. package/dist/modules/availability/api/policies/route.js.map +7 -0
  123. package/dist/modules/availability/backend/availability/check/page.js +185 -0
  124. package/dist/modules/availability/backend/availability/check/page.js.map +7 -0
  125. package/dist/modules/availability/backend/availability/check/page.meta.js +16 -0
  126. package/dist/modules/availability/backend/availability/check/page.meta.js.map +7 -0
  127. package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js +83 -0
  128. package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js.map +7 -0
  129. package/dist/modules/availability/backend/availability/policies/ScopeFields.js +53 -0
  130. package/dist/modules/availability/backend/availability/policies/ScopeFields.js.map +7 -0
  131. package/dist/modules/availability/backend/availability/policies/StockManagedField.js +32 -0
  132. package/dist/modules/availability/backend/availability/policies/StockManagedField.js.map +7 -0
  133. package/dist/modules/availability/backend/availability/policies/[id]/page.js +168 -0
  134. package/dist/modules/availability/backend/availability/policies/[id]/page.js.map +7 -0
  135. package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js +12 -0
  136. package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js.map +7 -0
  137. package/dist/modules/availability/backend/availability/policies/create/page.js +119 -0
  138. package/dist/modules/availability/backend/availability/policies/create/page.js.map +7 -0
  139. package/dist/modules/availability/backend/availability/policies/create/page.meta.js +12 -0
  140. package/dist/modules/availability/backend/availability/policies/create/page.meta.js.map +7 -0
  141. package/dist/modules/availability/backend/availability/policies/formGroups.js +77 -0
  142. package/dist/modules/availability/backend/availability/policies/formGroups.js.map +7 -0
  143. package/dist/modules/availability/backend/availability/policies/page.js +207 -0
  144. package/dist/modules/availability/backend/availability/policies/page.js.map +7 -0
  145. package/dist/modules/availability/backend/availability/policies/page.meta.js +16 -0
  146. package/dist/modules/availability/backend/availability/policies/page.meta.js.map +7 -0
  147. package/dist/modules/availability/backend/availability/policies/policyPayload.js +28 -0
  148. package/dist/modules/availability/backend/availability/policies/policyPayload.js.map +7 -0
  149. package/dist/modules/availability/commands/policies.js +364 -0
  150. package/dist/modules/availability/commands/policies.js.map +7 -0
  151. package/dist/modules/availability/commands/scope.js +19 -0
  152. package/dist/modules/availability/commands/scope.js.map +7 -0
  153. package/dist/modules/availability/data/entities.js +94 -0
  154. package/dist/modules/availability/data/entities.js.map +7 -0
  155. package/dist/modules/availability/data/validators.js +59 -0
  156. package/dist/modules/availability/data/validators.js.map +7 -0
  157. package/dist/modules/availability/di.js +50 -0
  158. package/dist/modules/availability/di.js.map +7 -0
  159. package/dist/modules/availability/events.js +18 -0
  160. package/dist/modules/availability/events.js.map +7 -0
  161. package/dist/modules/availability/index.js +15 -0
  162. package/dist/modules/availability/index.js.map +7 -0
  163. package/dist/modules/availability/lib/catalogTarget.js +42 -0
  164. package/dist/modules/availability/lib/catalogTarget.js.map +7 -0
  165. package/dist/modules/availability/lib/organizationScope.js +10 -0
  166. package/dist/modules/availability/lib/organizationScope.js.map +7 -0
  167. package/dist/modules/availability/lib/policyResolution.js +150 -0
  168. package/dist/modules/availability/lib/policyResolution.js.map +7 -0
  169. package/dist/modules/availability/lib/tryResolve.js +11 -0
  170. package/dist/modules/availability/lib/tryResolve.js.map +7 -0
  171. package/dist/modules/availability/migrations/Migration20260922095844_availability.js +16 -0
  172. package/dist/modules/availability/migrations/Migration20260922095844_availability.js.map +7 -0
  173. package/dist/modules/availability/migrations/Migration20260930120000_availability.js +20 -0
  174. package/dist/modules/availability/migrations/Migration20260930120000_availability.js.map +7 -0
  175. package/dist/modules/availability/setup.js +12 -0
  176. package/dist/modules/availability/setup.js.map +7 -0
  177. package/dist/modules/catalog/api/categories/bulk-create/route.js +110 -0
  178. package/dist/modules/catalog/api/categories/bulk-create/route.js.map +7 -0
  179. package/dist/modules/catalog/api/categories/route.js +56 -8
  180. package/dist/modules/catalog/api/categories/route.js.map +2 -2
  181. package/dist/modules/catalog/api/products/bulk-create/route.js +110 -0
  182. package/dist/modules/catalog/api/products/bulk-create/route.js.map +7 -0
  183. package/dist/modules/catalog/api/products/route.js +55 -31
  184. package/dist/modules/catalog/api/products/route.js.map +3 -3
  185. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +2 -6
  186. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  187. package/dist/modules/catalog/backend/catalog/products/create/page.js +12 -4
  188. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  189. package/dist/modules/catalog/commands/categories.js +1 -0
  190. package/dist/modules/catalog/commands/categories.js.map +2 -2
  191. package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
  192. package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
  193. package/dist/modules/catalog/commands/products.js +57 -19
  194. package/dist/modules/catalog/commands/products.js.map +2 -2
  195. package/dist/modules/catalog/components/PriceKindSettings.js +5 -19
  196. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  197. package/dist/modules/catalog/components/products/ProductUomSection.js +26 -29
  198. package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
  199. package/dist/modules/catalog/components/products/productForm.js +3 -2
  200. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  201. package/dist/modules/catalog/data/validators.js +15 -0
  202. package/dist/modules/catalog/data/validators.js.map +2 -2
  203. package/dist/modules/catalog/di.js +3 -2
  204. package/dist/modules/catalog/di.js.map +2 -2
  205. package/dist/modules/catalog/events.js +9 -3
  206. package/dist/modules/catalog/events.js.map +2 -2
  207. package/dist/modules/catalog/lib/bulkCreateCategories.js +237 -0
  208. package/dist/modules/catalog/lib/bulkCreateCategories.js.map +7 -0
  209. package/dist/modules/catalog/lib/bulkCreateCheckpoint.js +82 -0
  210. package/dist/modules/catalog/lib/bulkCreateCheckpoint.js.map +7 -0
  211. package/dist/modules/catalog/lib/bulkCreateMutationGuards.js +39 -0
  212. package/dist/modules/catalog/lib/bulkCreateMutationGuards.js.map +7 -0
  213. package/dist/modules/catalog/lib/bulkCreateProducts.js +241 -0
  214. package/dist/modules/catalog/lib/bulkCreateProducts.js.map +7 -0
  215. package/dist/modules/catalog/lib/bulkDelete.js +10 -11
  216. package/dist/modules/catalog/lib/bulkDelete.js.map +2 -2
  217. package/dist/modules/catalog/lib/catalogQueue.js +14 -0
  218. package/dist/modules/catalog/lib/catalogQueue.js.map +7 -0
  219. package/dist/modules/catalog/lib/currencyOptions.js +25 -0
  220. package/dist/modules/catalog/lib/currencyOptions.js.map +7 -0
  221. package/dist/modules/catalog/lib/productSearch.js +8 -0
  222. package/dist/modules/catalog/lib/productSearch.js.map +7 -0
  223. package/dist/modules/catalog/lib/seeds.js +2 -42
  224. package/dist/modules/catalog/lib/seeds.js.map +2 -2
  225. package/dist/modules/catalog/lib/unitLabels.js +53 -0
  226. package/dist/modules/catalog/lib/unitLabels.js.map +7 -0
  227. package/dist/modules/catalog/migrations/Migration20260914120000.js +29 -0
  228. package/dist/modules/catalog/migrations/Migration20260914120000.js.map +7 -0
  229. package/dist/modules/catalog/migrations/Migration20260914120100.js +28 -0
  230. package/dist/modules/catalog/migrations/Migration20260914120100.js.map +7 -0
  231. package/dist/modules/catalog/workers/catalog-category-bulk-create.js +40 -0
  232. package/dist/modules/catalog/workers/catalog-category-bulk-create.js.map +7 -0
  233. package/dist/modules/catalog/workers/catalog-product-bulk-create.js +40 -0
  234. package/dist/modules/catalog/workers/catalog-product-bulk-create.js.map +7 -0
  235. package/dist/modules/communication_channels/acl.js +8 -0
  236. package/dist/modules/communication_channels/acl.js.map +2 -2
  237. package/dist/modules/communication_channels/api/get/me/channels/route.js +4 -0
  238. package/dist/modules/communication_channels/api/get/me/channels/route.js.map +2 -2
  239. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +22 -0
  240. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +2 -2
  241. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +4 -4
  242. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +2 -2
  243. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +2 -1
  244. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  245. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
  246. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  247. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
  248. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
  249. package/dist/modules/communication_channels/api/post/test-seed/route.js +90 -20
  250. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +3 -3
  251. package/dist/modules/communication_channels/api/put/channels/[id]/visibility/route.js +137 -0
  252. package/dist/modules/communication_channels/api/put/channels/[id]/visibility/route.js.map +7 -0
  253. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +175 -107
  254. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  255. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +2 -1
  256. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
  257. package/dist/modules/communication_channels/commands/set-channel-visibility.js +88 -0
  258. package/dist/modules/communication_channels/commands/set-channel-visibility.js.map +7 -0
  259. package/dist/modules/communication_channels/data/enrichers.js +18 -9
  260. package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
  261. package/dist/modules/communication_channels/data/entities.js +4 -0
  262. package/dist/modules/communication_channels/data/entities.js.map +2 -2
  263. package/dist/modules/communication_channels/di.js +9 -1
  264. package/dist/modules/communication_channels/di.js.map +2 -2
  265. package/dist/modules/communication_channels/events.js +7 -0
  266. package/dist/modules/communication_channels/events.js.map +2 -2
  267. package/dist/modules/communication_channels/index.js +1 -1
  268. package/dist/modules/communication_channels/index.js.map +1 -1
  269. package/dist/modules/communication_channels/lib/access-control.js +2 -1
  270. package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
  271. package/dist/modules/communication_channels/lib/channel-thread-access.js +60 -0
  272. package/dist/modules/communication_channels/lib/channel-thread-access.js.map +7 -0
  273. package/dist/modules/communication_channels/lib/credential-refresh.js +11 -2
  274. package/dist/modules/communication_channels/lib/credential-refresh.js.map +2 -2
  275. package/dist/modules/communication_channels/lib/inbound-message-origin.js +18 -0
  276. package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +7 -0
  277. package/dist/modules/communication_channels/lib/oauth-state.js +37 -0
  278. package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
  279. package/dist/modules/communication_channels/lib/outbound-delivery-intent.js +12 -0
  280. package/dist/modules/communication_channels/lib/outbound-delivery-intent.js.map +7 -0
  281. package/dist/modules/communication_channels/lib/send-as-user.js +1 -1
  282. package/dist/modules/communication_channels/lib/send-as-user.js.map +2 -2
  283. package/dist/modules/communication_channels/migrations/Migration20260825141000_communication_channels.js +14 -0
  284. package/dist/modules/communication_channels/migrations/Migration20260825141000_communication_channels.js.map +7 -0
  285. package/dist/modules/communication_channels/setup.js +6 -2
  286. package/dist/modules/communication_channels/setup.js.map +2 -2
  287. package/dist/modules/communication_channels/subscribers/outbound-bridge.js +8 -2
  288. package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +2 -2
  289. package/dist/modules/communication_channels/workers/gmail-history-sync.js +1 -1
  290. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +2 -2
  291. package/dist/modules/communication_channels/workers/poll-channel.js +1 -1
  292. package/dist/modules/communication_channels/workers/poll-channel.js.map +2 -2
  293. package/dist/modules/communication_channels/workers/reaction-processor.js +2 -2
  294. package/dist/modules/communication_channels/workers/reaction-processor.js.map +2 -2
  295. package/dist/modules/configs/api/upgrade-actions/route.js +2 -1
  296. package/dist/modules/configs/api/upgrade-actions/route.js.map +2 -2
  297. package/dist/modules/configs/cli.js +2 -2
  298. package/dist/modules/configs/cli.js.map +2 -2
  299. package/dist/modules/configs/lib/upgrade-actions.js +28 -6
  300. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  301. package/dist/modules/configs/services/upgradeActionsService.js +44 -30
  302. package/dist/modules/configs/services/upgradeActionsService.js.map +2 -2
  303. package/dist/modules/currencies/api/currencies/route.js +11 -5
  304. package/dist/modules/currencies/api/currencies/route.js.map +2 -2
  305. package/dist/modules/currencies/api/exchange-rates/route.js +11 -5
  306. package/dist/modules/currencies/api/exchange-rates/route.js.map +2 -2
  307. package/dist/modules/customer_groups/acl.js +14 -0
  308. package/dist/modules/customer_groups/acl.js.map +7 -0
  309. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +266 -0
  310. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +7 -0
  311. package/dist/modules/customer_groups/api/customer-groups/crud.js +473 -0
  312. package/dist/modules/customer_groups/api/customer-groups/crud.js.map +7 -0
  313. package/dist/modules/customer_groups/api/customer-groups/explain-terms/route.js +143 -0
  314. package/dist/modules/customer_groups/api/customer-groups/explain-terms/route.js.map +7 -0
  315. package/dist/modules/customer_groups/api/customer-groups/memberships/crud.js +383 -0
  316. package/dist/modules/customer_groups/api/customer-groups/memberships/crud.js.map +7 -0
  317. package/dist/modules/customer_groups/api/customer-groups/memberships/route.js +66 -0
  318. package/dist/modules/customer_groups/api/customer-groups/memberships/route.js.map +7 -0
  319. package/dist/modules/customer_groups/api/customer-groups/reconcile/adopt/route.js +133 -0
  320. package/dist/modules/customer_groups/api/customer-groups/reconcile/adopt/route.js.map +7 -0
  321. package/dist/modules/customer_groups/api/customer-groups/reconcile/route.js +80 -0
  322. package/dist/modules/customer_groups/api/customer-groups/reconcile/route.js.map +7 -0
  323. package/dist/modules/customer_groups/api/customer-groups/reorder/route.js +127 -0
  324. package/dist/modules/customer_groups/api/customer-groups/reorder/route.js.map +7 -0
  325. package/dist/modules/customer_groups/api/customer-groups/route.js +62 -0
  326. package/dist/modules/customer_groups/api/customer-groups/route.js.map +7 -0
  327. package/dist/modules/customer_groups/api/openapi.js +27 -0
  328. package/dist/modules/customer_groups/api/openapi.js.map +7 -0
  329. package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.js +328 -0
  330. package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.js.map +7 -0
  331. package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.js +16 -0
  332. package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.js.map +7 -0
  333. package/dist/modules/customer_groups/backend/customer-groups/create/page.js +249 -0
  334. package/dist/modules/customer_groups/backend/customer-groups/create/page.js.map +7 -0
  335. package/dist/modules/customer_groups/backend/customer-groups/create/page.meta.js +27 -0
  336. package/dist/modules/customer_groups/backend/customer-groups/create/page.meta.js.map +7 -0
  337. package/dist/modules/customer_groups/backend/customer-groups/orphans/page.js +139 -0
  338. package/dist/modules/customer_groups/backend/customer-groups/orphans/page.js.map +7 -0
  339. package/dist/modules/customer_groups/backend/customer-groups/orphans/page.meta.js +16 -0
  340. package/dist/modules/customer_groups/backend/customer-groups/orphans/page.meta.js.map +7 -0
  341. package/dist/modules/customer_groups/backend/customer-groups/page.js +364 -0
  342. package/dist/modules/customer_groups/backend/customer-groups/page.js.map +7 -0
  343. package/dist/modules/customer_groups/backend/customer-groups/page.meta.js +15 -0
  344. package/dist/modules/customer_groups/backend/customer-groups/page.meta.js.map +7 -0
  345. package/dist/modules/customer_groups/cli.js +72 -0
  346. package/dist/modules/customer_groups/cli.js.map +7 -0
  347. package/dist/modules/customer_groups/commands/reorderGroups.js +96 -0
  348. package/dist/modules/customer_groups/commands/reorderGroups.js.map +7 -0
  349. package/dist/modules/customer_groups/components/CustomerGroupDefaultField.js +41 -0
  350. package/dist/modules/customer_groups/components/CustomerGroupDefaultField.js.map +7 -0
  351. package/dist/modules/customer_groups/components/CustomerGroupParentField.js +156 -0
  352. package/dist/modules/customer_groups/components/CustomerGroupParentField.js.map +7 -0
  353. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js +94 -0
  354. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js.map +7 -0
  355. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +197 -0
  356. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +7 -0
  357. package/dist/modules/customer_groups/components/GroupPickerField.js +116 -0
  358. package/dist/modules/customer_groups/components/GroupPickerField.js.map +7 -0
  359. package/dist/modules/customer_groups/components/OrphanBanner.js +75 -0
  360. package/dist/modules/customer_groups/components/OrphanBanner.js.map +7 -0
  361. package/dist/modules/customer_groups/components/customerGroupTree.js +57 -0
  362. package/dist/modules/customer_groups/components/customerGroupTree.js.map +7 -0
  363. package/dist/modules/customer_groups/data/entities.js +226 -0
  364. package/dist/modules/customer_groups/data/entities.js.map +7 -0
  365. package/dist/modules/customer_groups/data/validators.js +153 -0
  366. package/dist/modules/customer_groups/data/validators.js.map +7 -0
  367. package/dist/modules/customer_groups/di.js +20 -0
  368. package/dist/modules/customer_groups/di.js.map +7 -0
  369. package/dist/modules/customer_groups/events.js +22 -0
  370. package/dist/modules/customer_groups/events.js.map +7 -0
  371. package/dist/modules/customer_groups/index.js +15 -0
  372. package/dist/modules/customer_groups/index.js.map +7 -0
  373. package/dist/modules/customer_groups/lib/createConflictRecheck.js +29 -0
  374. package/dist/modules/customer_groups/lib/createConflictRecheck.js.map +7 -0
  375. package/dist/modules/customer_groups/lib/customerScope.js +81 -0
  376. package/dist/modules/customer_groups/lib/customerScope.js.map +7 -0
  377. package/dist/modules/customer_groups/lib/groupEvents.js +31 -0
  378. package/dist/modules/customer_groups/lib/groupEvents.js.map +7 -0
  379. package/dist/modules/customer_groups/lib/priceKindScope.js +16 -0
  380. package/dist/modules/customer_groups/lib/priceKindScope.js.map +7 -0
  381. package/dist/modules/customer_groups/lib/reconcile.js +161 -0
  382. package/dist/modules/customer_groups/lib/reconcile.js.map +7 -0
  383. package/dist/modules/customer_groups/migrations/Migration20260922095316_customer_groups.js +27 -0
  384. package/dist/modules/customer_groups/migrations/Migration20260922095316_customer_groups.js.map +7 -0
  385. package/dist/modules/customer_groups/migrations/Migration20260922103033_customer_groups.js +19 -0
  386. package/dist/modules/customer_groups/migrations/Migration20260922103033_customer_groups.js.map +7 -0
  387. package/dist/modules/customer_groups/migrations/Migration20260922105444_customer_groups.js +19 -0
  388. package/dist/modules/customer_groups/migrations/Migration20260922105444_customer_groups.js.map +7 -0
  389. package/dist/modules/customer_groups/migrations/Migration20260923193330_customer_groups.js +19 -0
  390. package/dist/modules/customer_groups/migrations/Migration20260923193330_customer_groups.js.map +7 -0
  391. package/dist/modules/customer_groups/migrations/Migration20260929083000_customer_groups.js +20 -0
  392. package/dist/modules/customer_groups/migrations/Migration20260929083000_customer_groups.js.map +7 -0
  393. package/dist/modules/customer_groups/services/customerGroupsService.js +190 -0
  394. package/dist/modules/customer_groups/services/customerGroupsService.js.map +7 -0
  395. package/dist/modules/customer_groups/setup.js +13 -0
  396. package/dist/modules/customer_groups/setup.js.map +7 -0
  397. package/dist/modules/customer_groups/widgets/injection/group-picker-field/widget.js +31 -0
  398. package/dist/modules/customer_groups/widgets/injection/group-picker-field/widget.js.map +7 -0
  399. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js +166 -0
  400. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js.map +7 -0
  401. package/dist/modules/customer_groups/widgets/injection/person-groups-tab/widget.js +17 -0
  402. package/dist/modules/customer_groups/widgets/injection/person-groups-tab/widget.js.map +7 -0
  403. package/dist/modules/customer_groups/widgets/injection/person-groups-tab.js +457 -0
  404. package/dist/modules/customer_groups/widgets/injection/person-groups-tab.js.map +7 -0
  405. package/dist/modules/customer_groups/widgets/injection-table.js +33 -0
  406. package/dist/modules/customer_groups/widgets/injection-table.js.map +7 -0
  407. package/dist/modules/customers/acl.js +13 -0
  408. package/dist/modules/customers/acl.js.map +2 -2
  409. package/dist/modules/customers/api/activities/route.js +9 -1
  410. package/dist/modules/customers/api/activities/route.js.map +2 -2
  411. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -1
  412. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  413. package/dist/modules/customers/api/companies/[id]/route.js +19 -7
  414. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  415. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -1
  416. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  417. package/dist/modules/customers/api/deals/[id]/people/route.js +60 -6
  418. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  419. package/dist/modules/customers/api/deals/map/route.js +7 -2
  420. package/dist/modules/customers/api/deals/map/route.js.map +2 -2
  421. package/dist/modules/customers/api/dictionaries/context.js +2 -1
  422. package/dist/modules/customers/api/dictionaries/context.js.map +2 -2
  423. package/dist/modules/customers/api/entity-roles-factory.js +3 -2
  424. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  425. package/dist/modules/customers/api/interactions/[id]/visibility/route.js +2 -1
  426. package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +2 -2
  427. package/dist/modules/customers/api/interactions/cancel/route.js +2 -1
  428. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  429. package/dist/modules/customers/api/interactions/complete/route.js +2 -1
  430. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  431. package/dist/modules/customers/api/interactions/conflicts/route.js +10 -6
  432. package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
  433. package/dist/modules/customers/api/interactions/counts/route.js +24 -4
  434. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  435. package/dist/modules/customers/api/interactions/route.js +31 -8
  436. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  437. package/dist/modules/customers/api/labels/assign/route.js +2 -1
  438. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  439. package/dist/modules/customers/api/labels/route.js +3 -2
  440. package/dist/modules/customers/api/labels/route.js.map +2 -2
  441. package/dist/modules/customers/api/labels/unassign/route.js +2 -1
  442. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  443. package/dist/modules/customers/api/people/[id]/companies/context.js +7 -2
  444. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  445. package/dist/modules/customers/api/people/[id]/email-share/route.js +218 -0
  446. package/dist/modules/customers/api/people/[id]/email-share/route.js.map +7 -0
  447. package/dist/modules/customers/api/people/[id]/email-threads/route.js +11 -2
  448. package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +2 -2
  449. package/dist/modules/customers/api/people/[id]/emails/route.js +2 -1
  450. package/dist/modules/customers/api/people/[id]/emails/route.js.map +2 -2
  451. package/dist/modules/customers/api/people/[id]/route.js +20 -19
  452. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  453. package/dist/modules/customers/api/people/check-phone/route.js +2 -1
  454. package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
  455. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +3 -2
  456. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  457. package/dist/modules/customers/api/pipeline-stages/route.js +3 -3
  458. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  459. package/dist/modules/customers/api/pipelines/route.js +3 -3
  460. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  461. package/dist/modules/customers/api/settings/address-format/route.js +2 -1
  462. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  463. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +2 -1
  464. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
  465. package/dist/modules/customers/api/settings/stuck-threshold/route.js +2 -1
  466. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
  467. package/dist/modules/customers/api/tags/assign/route.js +2 -1
  468. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  469. package/dist/modules/customers/api/tags/unassign/route.js +2 -1
  470. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  471. package/dist/modules/customers/backend/customers/companies/[id]/page.js +4 -2
  472. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  473. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +6 -25
  474. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
  475. package/dist/modules/customers/backend/customers/deals/[id]/page.js +21 -18
  476. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  477. package/dist/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.js +93 -0
  478. package/dist/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.js.map +7 -0
  479. package/dist/modules/customers/backend/customers/deals/page.js +111 -1
  480. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  481. package/dist/modules/customers/backend/customers/people/[id]/page.js +4 -2
  482. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  483. package/dist/modules/customers/commands/addresses.js +83 -15
  484. package/dist/modules/customers/commands/addresses.js.map +2 -2
  485. package/dist/modules/customers/commands/companies.js +10 -4
  486. package/dist/modules/customers/commands/companies.js.map +2 -2
  487. package/dist/modules/customers/commands/emailConversationShares.js +177 -0
  488. package/dist/modules/customers/commands/emailConversationShares.js.map +7 -0
  489. package/dist/modules/customers/commands/index.js +1 -0
  490. package/dist/modules/customers/commands/index.js.map +2 -2
  491. package/dist/modules/customers/commands/interactions.js +48 -23
  492. package/dist/modules/customers/commands/interactions.js.map +3 -3
  493. package/dist/modules/customers/commands/people.js +3 -2
  494. package/dist/modules/customers/commands/people.js.map +2 -2
  495. package/dist/modules/customers/commands/shared.js +4 -0
  496. package/dist/modules/customers/commands/shared.js.map +2 -2
  497. package/dist/modules/customers/components/calendar/types.js.map +1 -1
  498. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +81 -354
  499. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  500. package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
  501. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
  502. package/dist/modules/customers/components/detail/DealForm.js +73 -5
  503. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  504. package/dist/modules/customers/components/detail/DealOwnerSelect.js +96 -0
  505. package/dist/modules/customers/components/detail/DealOwnerSelect.js.map +7 -0
  506. package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
  507. package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
  508. package/dist/modules/customers/components/detail/DealsSection.js +8 -14
  509. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  510. package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
  511. package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
  512. package/dist/modules/customers/components/detail/PersonCard.js +5 -2
  513. package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
  514. package/dist/modules/customers/components/detail/PersonEmailShareControl.js +190 -0
  515. package/dist/modules/customers/components/detail/PersonEmailShareControl.js.map +7 -0
  516. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +17 -0
  517. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +2 -2
  518. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +5 -2
  519. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  520. package/dist/modules/customers/components/detail/create/CreateDealForm.js +19 -0
  521. package/dist/modules/customers/components/detail/create/CreateDealForm.js.map +2 -2
  522. package/dist/modules/customers/components/detail/create/DealDetailsFields.js +11 -0
  523. package/dist/modules/customers/components/detail/create/DealDetailsFields.js.map +2 -2
  524. package/dist/modules/customers/components/detail/create/DealSectionCard.js +2 -2
  525. package/dist/modules/customers/components/detail/create/DealSectionCard.js.map +2 -2
  526. package/dist/modules/customers/components/detail/create/dealFormTypes.js +1 -0
  527. package/dist/modules/customers/components/detail/create/dealFormTypes.js.map +2 -2
  528. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +1 -1
  529. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  530. package/dist/modules/customers/components/formConfig.js +2 -0
  531. package/dist/modules/customers/components/formConfig.js.map +2 -2
  532. package/dist/modules/customers/components/linking/LinkEntityDialog.js +8 -0
  533. package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
  534. package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
  535. package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
  536. package/dist/modules/customers/data/enrichers.js +40 -2
  537. package/dist/modules/customers/data/enrichers.js.map +2 -2
  538. package/dist/modules/customers/data/entities.js +56 -0
  539. package/dist/modules/customers/data/entities.js.map +2 -2
  540. package/dist/modules/customers/data/validators.js +11 -0
  541. package/dist/modules/customers/data/validators.js.map +2 -2
  542. package/dist/modules/customers/events.js +6 -1
  543. package/dist/modules/customers/events.js.map +2 -2
  544. package/dist/modules/customers/lib/assignableStaff.js +14 -0
  545. package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
  546. package/dist/modules/customers/lib/calendar/editorPayload.js +17 -13
  547. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  548. package/dist/modules/customers/lib/calendar/mapItem.js +1 -0
  549. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  550. package/dist/modules/customers/lib/conversationShares.js +126 -0
  551. package/dist/modules/customers/lib/conversationShares.js.map +7 -0
  552. package/dist/modules/customers/lib/dealsOrganizationScope.js +5 -3
  553. package/dist/modules/customers/lib/dealsOrganizationScope.js.map +2 -2
  554. package/dist/modules/customers/lib/interactionRequestContext.js +2 -1
  555. package/dist/modules/customers/lib/interactionRequestContext.js.map +2 -2
  556. package/dist/modules/customers/lib/link-channel-message-handler.js +19 -9
  557. package/dist/modules/customers/lib/link-channel-message-handler.js.map +2 -2
  558. package/dist/modules/customers/lib/personDetailCacheTags.js +32 -0
  559. package/dist/modules/customers/lib/personDetailCacheTags.js.map +7 -0
  560. package/dist/modules/customers/lib/personEmailThreads.js +26 -5
  561. package/dist/modules/customers/lib/personEmailThreads.js.map +2 -2
  562. package/dist/modules/customers/lib/reassignDealOwner.js +25 -0
  563. package/dist/modules/customers/lib/reassignDealOwner.js.map +7 -0
  564. package/dist/modules/customers/lib/userNames.js +26 -0
  565. package/dist/modules/customers/lib/userNames.js.map +7 -0
  566. package/dist/modules/customers/lib/visibilityFilter.js +70 -2
  567. package/dist/modules/customers/lib/visibilityFilter.js.map +2 -2
  568. package/dist/modules/customers/migrations/Migration20260825120000_customers.js +18 -0
  569. package/dist/modules/customers/migrations/Migration20260825120000_customers.js.map +7 -0
  570. package/dist/modules/customers/migrations/Migration20260825140000_customers.js +27 -0
  571. package/dist/modules/customers/migrations/Migration20260825140000_customers.js.map +7 -0
  572. package/dist/modules/customers/setup.js +1 -0
  573. package/dist/modules/customers/setup.js.map +2 -2
  574. package/dist/modules/customers/subscribers/channel-visibility-changed.js +16 -0
  575. package/dist/modules/customers/subscribers/channel-visibility-changed.js.map +7 -0
  576. package/dist/modules/dashboards/api/widgets/data/batch/route.js +3 -8
  577. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  578. package/dist/modules/dashboards/api/widgets/data/route.js +10 -15
  579. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  580. package/dist/modules/dashboards/lib/aggregations.js +1 -1
  581. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  582. package/dist/modules/dashboards/lib/widgetScope.js +2 -7
  583. package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
  584. package/dist/modules/data_sync/api/runs/[id]/cancel.js +4 -1
  585. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  586. package/dist/modules/data_sync/backend/data-sync/page.js +497 -329
  587. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  588. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +248 -113
  589. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +3 -3
  590. package/dist/modules/data_sync/components/useDataSyncRunAccess.js +17 -0
  591. package/dist/modules/data_sync/components/useDataSyncRunAccess.js.map +7 -0
  592. package/dist/modules/data_sync/lib/resume-point.js +21 -0
  593. package/dist/modules/data_sync/lib/resume-point.js.map +7 -0
  594. package/dist/modules/data_sync/lib/start-run.js +52 -2
  595. package/dist/modules/data_sync/lib/start-run.js.map +2 -2
  596. package/dist/modules/data_sync/lib/sync-run-service.js +22 -33
  597. package/dist/modules/data_sync/lib/sync-run-service.js.map +3 -3
  598. package/dist/modules/devices/api/[id]/route.js +3 -2
  599. package/dist/modules/devices/api/[id]/route.js.map +2 -2
  600. package/dist/modules/devices/api/admin/devices/route.js +2 -1
  601. package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
  602. package/dist/modules/devices/api/route.js +2 -1
  603. package/dist/modules/devices/api/route.js.map +2 -2
  604. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  605. package/dist/modules/dictionaries/api/context.js +2 -1
  606. package/dist/modules/dictionaries/api/context.js.map +2 -2
  607. package/dist/modules/dictionaries/api/route.js +4 -0
  608. package/dist/modules/dictionaries/api/route.js.map +2 -2
  609. package/dist/modules/dictionaries/components/DictionariesManager.js +4 -3
  610. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  611. package/dist/modules/dictionaries/data/validators.js +3 -1
  612. package/dist/modules/dictionaries/data/validators.js.map +2 -2
  613. package/dist/modules/directory/api/organization-branding/route.js +10 -1
  614. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  615. package/dist/modules/directory/api/organizations/route.js +1 -1
  616. package/dist/modules/directory/api/organizations/route.js.map +1 -1
  617. package/dist/modules/directory/backend/directory/branding/page.js +17 -4
  618. package/dist/modules/directory/backend/directory/branding/page.js.map +3 -3
  619. package/dist/modules/directory/commands/organizations.js +136 -74
  620. package/dist/modules/directory/commands/organizations.js.map +2 -2
  621. package/dist/modules/directory/lib/hierarchy.js +19 -1
  622. package/dist/modules/directory/lib/hierarchy.js.map +2 -2
  623. package/dist/modules/directory/utils/organizationScope.js +66 -12
  624. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  625. package/dist/modules/directory/utils/organizationScopeFilter.js +7 -4
  626. package/dist/modules/directory/utils/organizationScopeFilter.js.map +2 -2
  627. package/dist/modules/directory/utils/organizationScopeGuard.js +6 -2
  628. package/dist/modules/directory/utils/organizationScopeGuard.js.map +2 -2
  629. package/dist/modules/entities/api/encryption.js +68 -25
  630. package/dist/modules/entities/api/encryption.js.map +2 -2
  631. package/dist/modules/entities/api/records.js +9 -8
  632. package/dist/modules/entities/api/records.js.map +2 -2
  633. package/dist/modules/entities/cli.js +33 -22
  634. package/dist/modules/entities/cli.js.map +2 -2
  635. package/dist/modules/entities/data/entities.js +8 -0
  636. package/dist/modules/entities/data/entities.js.map +2 -2
  637. package/dist/modules/entities/lib/definition-scope.js +3 -1
  638. package/dist/modules/entities/lib/definition-scope.js.map +2 -2
  639. package/dist/modules/entities/lib/encryption-maps.js +117 -0
  640. package/dist/modules/entities/lib/encryption-maps.js.map +7 -0
  641. package/dist/modules/entities/lib/helpers.js +30 -6
  642. package/dist/modules/entities/lib/helpers.js.map +2 -2
  643. package/dist/modules/entities/migrations/Migration20260910120000.js +17 -0
  644. package/dist/modules/entities/migrations/Migration20260910120000.js.map +7 -0
  645. package/dist/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.js +130 -0
  646. package/dist/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.js.map +7 -0
  647. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +2 -1
  648. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +2 -2
  649. package/dist/modules/eudr/api/plots/import/route.js +2 -1
  650. package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
  651. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +2 -1
  652. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
  653. package/dist/modules/eudr/api/product-mappings/suggestions/route.js +2 -1
  654. package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +2 -2
  655. package/dist/modules/eudr/api/reports/annual/route.js +2 -1
  656. package/dist/modules/eudr/api/reports/annual/route.js.map +2 -2
  657. package/dist/modules/eudr/api/statements/[id]/export/route.js +2 -1
  658. package/dist/modules/eudr/api/statements/[id]/export/route.js.map +2 -2
  659. package/dist/modules/eudr/api/statements/[id]/readiness/route.js +2 -1
  660. package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +2 -2
  661. package/dist/modules/eudr/api/suppliers/compliance/route.js +2 -1
  662. package/dist/modules/eudr/api/suppliers/compliance/route.js.map +2 -2
  663. package/dist/modules/feature_toggles/commands/global.js +13 -13
  664. package/dist/modules/feature_toggles/commands/global.js.map +2 -2
  665. package/dist/modules/feature_toggles/lib/feature-flag-check.js +8 -2
  666. package/dist/modules/feature_toggles/lib/feature-flag-check.js.map +2 -2
  667. package/dist/modules/forms/acl.js +14 -0
  668. package/dist/modules/forms/acl.js.map +7 -0
  669. package/dist/modules/forms/api/[id]/analytics/route.js +113 -0
  670. package/dist/modules/forms/api/[id]/analytics/route.js.map +7 -0
  671. package/dist/modules/forms/api/[id]/distributions/route.js +176 -0
  672. package/dist/modules/forms/api/[id]/distributions/route.js.map +7 -0
  673. package/dist/modules/forms/api/[id]/route.js +225 -0
  674. package/dist/modules/forms/api/[id]/route.js.map +7 -0
  675. package/dist/modules/forms/api/[id]/run/context/route.js +115 -0
  676. package/dist/modules/forms/api/[id]/run/context/route.js.map +7 -0
  677. package/dist/modules/forms/api/[id]/run/submissions/route.js +109 -0
  678. package/dist/modules/forms/api/[id]/run/submissions/route.js.map +7 -0
  679. package/dist/modules/forms/api/[id]/submissions/route.js +83 -0
  680. package/dist/modules/forms/api/[id]/submissions/route.js.map +7 -0
  681. package/dist/modules/forms/api/[id]/theme-logo/route.js +117 -0
  682. package/dist/modules/forms/api/[id]/theme-logo/route.js.map +7 -0
  683. package/dist/modules/forms/api/[id]/versions/[versionId]/diff/route.js +125 -0
  684. package/dist/modules/forms/api/[id]/versions/[versionId]/diff/route.js.map +7 -0
  685. package/dist/modules/forms/api/[id]/versions/[versionId]/publish/route.js +99 -0
  686. package/dist/modules/forms/api/[id]/versions/[versionId]/publish/route.js.map +7 -0
  687. package/dist/modules/forms/api/[id]/versions/[versionId]/route.js +176 -0
  688. package/dist/modules/forms/api/[id]/versions/[versionId]/route.js.map +7 -0
  689. package/dist/modules/forms/api/[id]/versions/fork/route.js +92 -0
  690. package/dist/modules/forms/api/[id]/versions/fork/route.js.map +7 -0
  691. package/dist/modules/forms/api/attachment-helpers.js +111 -0
  692. package/dist/modules/forms/api/attachment-helpers.js.map +7 -0
  693. package/dist/modules/forms/api/by-key/[key]/active/route.js +140 -0
  694. package/dist/modules/forms/api/by-key/[key]/active/route.js.map +7 -0
  695. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js +84 -0
  696. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js.map +7 -0
  697. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.js +88 -0
  698. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.js.map +7 -0
  699. package/dist/modules/forms/api/distributions/[distributionId]/invitations/route.js +183 -0
  700. package/dist/modules/forms/api/distributions/[distributionId]/invitations/route.js.map +7 -0
  701. package/dist/modules/forms/api/distributions/[distributionId]/route.js +191 -0
  702. package/dist/modules/forms/api/distributions/[distributionId]/route.js.map +7 -0
  703. package/dist/modules/forms/api/form-submissions/[id]/attachments/route.js +91 -0
  704. package/dist/modules/forms/api/form-submissions/[id]/attachments/route.js.map +7 -0
  705. package/dist/modules/forms/api/form-submissions/[id]/resume-token/route.js +110 -0
  706. package/dist/modules/forms/api/form-submissions/[id]/resume-token/route.js.map +7 -0
  707. package/dist/modules/forms/api/form-submissions/[id]/route.js +146 -0
  708. package/dist/modules/forms/api/form-submissions/[id]/route.js.map +7 -0
  709. package/dist/modules/forms/api/form-submissions/[id]/submit/route.js +74 -0
  710. package/dist/modules/forms/api/form-submissions/[id]/submit/route.js.map +7 -0
  711. package/dist/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.js +76 -0
  712. package/dist/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.js.map +7 -0
  713. package/dist/modules/forms/api/form-submissions/route.js +113 -0
  714. package/dist/modules/forms/api/form-submissions/route.js.map +7 -0
  715. package/dist/modules/forms/api/helpers.js +124 -0
  716. package/dist/modules/forms/api/helpers.js.map +7 -0
  717. package/dist/modules/forms/api/public/distributions/[slug]/embed-policy/route.js +47 -0
  718. package/dist/modules/forms/api/public/distributions/[slug]/embed-policy/route.js.map +7 -0
  719. package/dist/modules/forms/api/public/distributions/[slug]/route.js +81 -0
  720. package/dist/modules/forms/api/public/distributions/[slug]/route.js.map +7 -0
  721. package/dist/modules/forms/api/public/embed-loader/route.js +116 -0
  722. package/dist/modules/forms/api/public/embed-loader/route.js.map +7 -0
  723. package/dist/modules/forms/api/public/invitations/[token]/route.js +87 -0
  724. package/dist/modules/forms/api/public/invitations/[token]/route.js.map +7 -0
  725. package/dist/modules/forms/api/public/rate-limit.js +54 -0
  726. package/dist/modules/forms/api/public/rate-limit.js.map +7 -0
  727. package/dist/modules/forms/api/public/start/route.js +142 -0
  728. package/dist/modules/forms/api/public/start/route.js.map +7 -0
  729. package/dist/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.js +67 -0
  730. package/dist/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.js.map +7 -0
  731. package/dist/modules/forms/api/public/submissions/[id]/attachments/route.js +85 -0
  732. package/dist/modules/forms/api/public/submissions/[id]/attachments/route.js.map +7 -0
  733. package/dist/modules/forms/api/public/submissions/[id]/pdf/route.js +72 -0
  734. package/dist/modules/forms/api/public/submissions/[id]/pdf/route.js.map +7 -0
  735. package/dist/modules/forms/api/public/submissions/[id]/route.js +154 -0
  736. package/dist/modules/forms/api/public/submissions/[id]/route.js.map +7 -0
  737. package/dist/modules/forms/api/public/submissions/[id]/submit/route.js +127 -0
  738. package/dist/modules/forms/api/public/submissions/[id]/submit/route.js.map +7 -0
  739. package/dist/modules/forms/api/route.js +216 -0
  740. package/dist/modules/forms/api/route.js.map +7 -0
  741. package/dist/modules/forms/api/runtime-helpers.js +149 -0
  742. package/dist/modules/forms/api/runtime-helpers.js.map +7 -0
  743. package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.js +137 -0
  744. package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.js.map +7 -0
  745. package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.js +101 -0
  746. package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.js.map +7 -0
  747. package/dist/modules/forms/api/submissions/[submissionId]/access-audit/route.js +76 -0
  748. package/dist/modules/forms/api/submissions/[submissionId]/access-audit/route.js.map +7 -0
  749. package/dist/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.js +56 -0
  750. package/dist/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.js.map +7 -0
  751. package/dist/modules/forms/api/submissions/[submissionId]/actors/route.js +76 -0
  752. package/dist/modules/forms/api/submissions/[submissionId]/actors/route.js.map +7 -0
  753. package/dist/modules/forms/api/submissions/[submissionId]/anonymize/route.js +107 -0
  754. package/dist/modules/forms/api/submissions/[submissionId]/anonymize/route.js.map +7 -0
  755. package/dist/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.js +73 -0
  756. package/dist/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.js.map +7 -0
  757. package/dist/modules/forms/api/submissions/[submissionId]/export/route.js +90 -0
  758. package/dist/modules/forms/api/submissions/[submissionId]/export/route.js.map +7 -0
  759. package/dist/modules/forms/api/submissions/[submissionId]/pdf/route.js +91 -0
  760. package/dist/modules/forms/api/submissions/[submissionId]/pdf/route.js.map +7 -0
  761. package/dist/modules/forms/api/submissions/[submissionId]/reopen/route.js +55 -0
  762. package/dist/modules/forms/api/submissions/[submissionId]/reopen/route.js.map +7 -0
  763. package/dist/modules/forms/api/submissions/[submissionId]/revisions/route.js +61 -0
  764. package/dist/modules/forms/api/submissions/[submissionId]/revisions/route.js.map +7 -0
  765. package/dist/modules/forms/api/submissions/[submissionId]/route.js +75 -0
  766. package/dist/modules/forms/api/submissions/[submissionId]/route.js.map +7 -0
  767. package/dist/modules/forms/backend/forms/[id]/FormStudio.js +2811 -0
  768. package/dist/modules/forms/backend/forms/[id]/FormStudio.js.map +7 -0
  769. package/dist/modules/forms/backend/forms/[id]/analytics/page.js +214 -0
  770. package/dist/modules/forms/backend/forms/[id]/analytics/page.js.map +7 -0
  771. package/dist/modules/forms/backend/forms/[id]/analytics/page.meta.js +19 -0
  772. package/dist/modules/forms/backend/forms/[id]/analytics/page.meta.js.map +7 -0
  773. package/dist/modules/forms/backend/forms/[id]/distributions/page.js +12 -0
  774. package/dist/modules/forms/backend/forms/[id]/distributions/page.js.map +7 -0
  775. package/dist/modules/forms/backend/forms/[id]/distributions/page.meta.js +19 -0
  776. package/dist/modules/forms/backend/forms/[id]/distributions/page.meta.js.map +7 -0
  777. package/dist/modules/forms/backend/forms/[id]/history/page.js +152 -0
  778. package/dist/modules/forms/backend/forms/[id]/history/page.js.map +7 -0
  779. package/dist/modules/forms/backend/forms/[id]/history/page.meta.js +15 -0
  780. package/dist/modules/forms/backend/forms/[id]/history/page.meta.js.map +7 -0
  781. package/dist/modules/forms/backend/forms/[id]/page.js +13 -0
  782. package/dist/modules/forms/backend/forms/[id]/page.js.map +7 -0
  783. package/dist/modules/forms/backend/forms/[id]/page.meta.js +15 -0
  784. package/dist/modules/forms/backend/forms/[id]/page.meta.js.map +7 -0
  785. package/dist/modules/forms/backend/forms/[id]/studio/YesNoRow.js +51 -0
  786. package/dist/modules/forms/backend/forms/[id]/studio/YesNoRow.js.map +7 -0
  787. package/dist/modules/forms/backend/forms/[id]/studio/autosave-guard.js +21 -0
  788. package/dist/modules/forms/backend/forms/[id]/studio/autosave-guard.js.map +7 -0
  789. package/dist/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.js +18 -0
  790. package/dist/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.js.map +7 -0
  791. package/dist/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.js +41 -0
  792. package/dist/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.js.map +7 -0
  793. package/dist/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.js +277 -0
  794. package/dist/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.js.map +7 -0
  795. package/dist/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.js +196 -0
  796. package/dist/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.js.map +7 -0
  797. package/dist/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.js +151 -0
  798. package/dist/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.js.map +7 -0
  799. package/dist/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.js +340 -0
  800. package/dist/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.js.map +7 -0
  801. package/dist/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.js +24 -0
  802. package/dist/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.js.map +7 -0
  803. package/dist/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.js +91 -0
  804. package/dist/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.js.map +7 -0
  805. package/dist/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.js +11 -0
  806. package/dist/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.js.map +7 -0
  807. package/dist/modules/forms/backend/forms/[id]/studio/canvas/resize-math.js +27 -0
  808. package/dist/modules/forms/backend/forms/[id]/studio/canvas/resize-math.js.map +7 -0
  809. package/dist/modules/forms/backend/forms/[id]/studio/canvas/row-layout.js +80 -0
  810. package/dist/modules/forms/backend/forms/[id]/studio/canvas/row-layout.js.map +7 -0
  811. package/dist/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.js +210 -0
  812. package/dist/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.js.map +7 -0
  813. package/dist/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.js +91 -0
  814. package/dist/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.js.map +7 -0
  815. package/dist/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.js +201 -0
  816. package/dist/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.js.map +7 -0
  817. package/dist/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.js +66 -0
  818. package/dist/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.js.map +7 -0
  819. package/dist/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.js +223 -0
  820. package/dist/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.js.map +7 -0
  821. package/dist/modules/forms/backend/forms/[id]/studio/logic/condition-model.js +151 -0
  822. package/dist/modules/forms/backend/forms/[id]/studio/logic/condition-model.js.map +7 -0
  823. package/dist/modules/forms/backend/forms/[id]/studio/lucide-icons.js +88 -0
  824. package/dist/modules/forms/backend/forms/[id]/studio/lucide-icons.js.map +7 -0
  825. package/dist/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.js +493 -0
  826. package/dist/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.js.map +7 -0
  827. package/dist/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.js +44 -0
  828. package/dist/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.js.map +7 -0
  829. package/dist/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.js +350 -0
  830. package/dist/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.js.map +7 -0
  831. package/dist/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.js +40 -0
  832. package/dist/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.js.map +7 -0
  833. package/dist/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.js +202 -0
  834. package/dist/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.js.map +7 -0
  835. package/dist/modules/forms/backend/forms/[id]/studio/palette/entries.js +66 -0
  836. package/dist/modules/forms/backend/forms/[id]/studio/palette/entries.js.map +7 -0
  837. package/dist/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.js +1200 -0
  838. package/dist/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.js.map +7 -0
  839. package/dist/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.js +34 -0
  840. package/dist/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.js.map +7 -0
  841. package/dist/modules/forms/backend/forms/[id]/studio/recall.js +76 -0
  842. package/dist/modules/forms/backend/forms/[id]/studio/recall.js.map +7 -0
  843. package/dist/modules/forms/backend/forms/[id]/studio/schema-helpers.js +1693 -0
  844. package/dist/modules/forms/backend/forms/[id]/studio/schema-helpers.js.map +7 -0
  845. package/dist/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.js +127 -0
  846. package/dist/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.js.map +7 -0
  847. package/dist/modules/forms/backend/forms/[id]/studio/style/ColorControl.js +181 -0
  848. package/dist/modules/forms/backend/forms/[id]/studio/style/ColorControl.js.map +7 -0
  849. package/dist/modules/forms/backend/forms/[id]/studio/style/contrast.js +75 -0
  850. package/dist/modules/forms/backend/forms/[id]/studio/style/contrast.js.map +7 -0
  851. package/dist/modules/forms/backend/forms/[id]/studio/style/presets.js +91 -0
  852. package/dist/modules/forms/backend/forms/[id]/studio/style/presets.js.map +7 -0
  853. package/dist/modules/forms/backend/forms/[id]/studio/type-label.js +11 -0
  854. package/dist/modules/forms/backend/forms/[id]/studio/type-label.js.map +7 -0
  855. package/dist/modules/forms/backend/forms/[id]/studio/types.js +1 -0
  856. package/dist/modules/forms/backend/forms/[id]/studio/types.js.map +7 -0
  857. package/dist/modules/forms/backend/forms/[id]/studio/undo-controller.js +43 -0
  858. package/dist/modules/forms/backend/forms/[id]/studio/undo-controller.js.map +7 -0
  859. package/dist/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.js +74 -0
  860. package/dist/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.js.map +7 -0
  861. package/dist/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.js +134 -0
  862. package/dist/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.js.map +7 -0
  863. package/dist/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.js +61 -0
  864. package/dist/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.js.map +7 -0
  865. package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.js +149 -0
  866. package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.js.map +7 -0
  867. package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.js +168 -0
  868. package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.js.map +7 -0
  869. package/dist/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.js +64 -0
  870. package/dist/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.js.map +7 -0
  871. package/dist/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.js +52 -0
  872. package/dist/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.js.map +7 -0
  873. package/dist/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.js +59 -0
  874. package/dist/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.js.map +7 -0
  875. package/dist/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.js +31 -0
  876. package/dist/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.js.map +7 -0
  877. package/dist/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.js +105 -0
  878. package/dist/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.js.map +7 -0
  879. package/dist/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.js +15 -0
  880. package/dist/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.js.map +7 -0
  881. package/dist/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.js +75 -0
  882. package/dist/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.js.map +7 -0
  883. package/dist/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.js +293 -0
  884. package/dist/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.js.map +7 -0
  885. package/dist/modules/forms/backend/forms/[id]/submissions/components/RowBadges.js +42 -0
  886. package/dist/modules/forms/backend/forms/[id]/submissions/components/RowBadges.js.map +7 -0
  887. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js +473 -0
  888. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js.map +7 -0
  889. package/dist/modules/forms/backend/forms/[id]/submissions/page.js +208 -0
  890. package/dist/modules/forms/backend/forms/[id]/submissions/page.js.map +7 -0
  891. package/dist/modules/forms/backend/forms/[id]/submissions/page.meta.js +12 -0
  892. package/dist/modules/forms/backend/forms/[id]/submissions/page.meta.js.map +7 -0
  893. package/dist/modules/forms/backend/forms/create/page.js +101 -0
  894. package/dist/modules/forms/backend/forms/create/page.js.map +7 -0
  895. package/dist/modules/forms/backend/forms/create/page.meta.js +15 -0
  896. package/dist/modules/forms/backend/forms/create/page.meta.js.map +7 -0
  897. package/dist/modules/forms/backend/forms/page.js +227 -0
  898. package/dist/modules/forms/backend/forms/page.js.map +7 -0
  899. package/dist/modules/forms/backend/forms/page.meta.js +33 -0
  900. package/dist/modules/forms/backend/forms/page.meta.js.map +7 -0
  901. package/dist/modules/forms/commands/distribution.js +417 -0
  902. package/dist/modules/forms/commands/distribution.js.map +7 -0
  903. package/dist/modules/forms/commands/form-version.js +640 -0
  904. package/dist/modules/forms/commands/form-version.js.map +7 -0
  905. package/dist/modules/forms/commands/form.js +406 -0
  906. package/dist/modules/forms/commands/form.js.map +7 -0
  907. package/dist/modules/forms/commands/index.js +11 -0
  908. package/dist/modules/forms/commands/index.js.map +7 -0
  909. package/dist/modules/forms/commands/invitation.js +329 -0
  910. package/dist/modules/forms/commands/invitation.js.map +7 -0
  911. package/dist/modules/forms/commands/shared.js +151 -0
  912. package/dist/modules/forms/commands/shared.js.map +7 -0
  913. package/dist/modules/forms/commands/submission.js +132 -0
  914. package/dist/modules/forms/commands/submission.js.map +7 -0
  915. package/dist/modules/forms/data/entities.js +654 -0
  916. package/dist/modules/forms/data/entities.js.map +7 -0
  917. package/dist/modules/forms/data/validators.js +254 -0
  918. package/dist/modules/forms/data/validators.js.map +7 -0
  919. package/dist/modules/forms/di.js +199 -0
  920. package/dist/modules/forms/di.js.map +7 -0
  921. package/dist/modules/forms/encryption.js +15 -0
  922. package/dist/modules/forms/encryption.js.map +7 -0
  923. package/dist/modules/forms/events-payloads.js +81 -0
  924. package/dist/modules/forms/events-payloads.js.map +7 -0
  925. package/dist/modules/forms/events.js +33 -0
  926. package/dist/modules/forms/events.js.map +7 -0
  927. package/dist/modules/forms/extension-points.js +36 -0
  928. package/dist/modules/forms/extension-points.js.map +7 -0
  929. package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.js +33 -0
  930. package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.js.map +7 -0
  931. package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.js +11 -0
  932. package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.js.map +7 -0
  933. package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.js +41 -0
  934. package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.js.map +7 -0
  935. package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.js +11 -0
  936. package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.js.map +7 -0
  937. package/dist/modules/forms/frontend/embed/[slug]/page.js +105 -0
  938. package/dist/modules/forms/frontend/embed/[slug]/page.js.map +7 -0
  939. package/dist/modules/forms/frontend/embed/[slug]/page.meta.js +11 -0
  940. package/dist/modules/forms/frontend/embed/[slug]/page.meta.js.map +7 -0
  941. package/dist/modules/forms/frontend/f/[slug]/page.js +12 -0
  942. package/dist/modules/forms/frontend/f/[slug]/page.js.map +7 -0
  943. package/dist/modules/forms/frontend/f/[slug]/page.meta.js +11 -0
  944. package/dist/modules/forms/frontend/f/[slug]/page.meta.js.map +7 -0
  945. package/dist/modules/forms/frontend/forms/[id]/run/page.js +62 -0
  946. package/dist/modules/forms/frontend/forms/[id]/run/page.js.map +7 -0
  947. package/dist/modules/forms/frontend/forms/[id]/run/page.meta.js +11 -0
  948. package/dist/modules/forms/frontend/forms/[id]/run/page.meta.js.map +7 -0
  949. package/dist/modules/forms/frontend/i/[token]/page.js +12 -0
  950. package/dist/modules/forms/frontend/i/[token]/page.js.map +7 -0
  951. package/dist/modules/forms/frontend/i/[token]/page.meta.js +11 -0
  952. package/dist/modules/forms/frontend/i/[token]/page.meta.js.map +7 -0
  953. package/dist/modules/forms/index.js +20 -0
  954. package/dist/modules/forms/index.js.map +7 -0
  955. package/dist/modules/forms/lib/embed-frame-policy.js +68 -0
  956. package/dist/modules/forms/lib/embed-frame-policy.js.map +7 -0
  957. package/dist/modules/forms/lib/form-definition-slicing.js +38 -0
  958. package/dist/modules/forms/lib/form-definition-slicing.js.map +7 -0
  959. package/dist/modules/forms/lib/log-redaction.js +38 -0
  960. package/dist/modules/forms/lib/log-redaction.js.map +7 -0
  961. package/dist/modules/forms/lib/navigable-url.js +24 -0
  962. package/dist/modules/forms/lib/navigable-url.js.map +7 -0
  963. package/dist/modules/forms/lib/retention.js +20 -0
  964. package/dist/modules/forms/lib/retention.js.map +7 -0
  965. package/dist/modules/forms/lib/runtime-principal.js +59 -0
  966. package/dist/modules/forms/lib/runtime-principal.js.map +7 -0
  967. package/dist/modules/forms/migrations/Migration20260508135459_forms.js +17 -0
  968. package/dist/modules/forms/migrations/Migration20260508135459_forms.js.map +7 -0
  969. package/dist/modules/forms/migrations/Migration20260508140932_forms.js +23 -0
  970. package/dist/modules/forms/migrations/Migration20260508140932_forms.js.map +7 -0
  971. package/dist/modules/forms/migrations/Migration20260508155737_forms.js +15 -0
  972. package/dist/modules/forms/migrations/Migration20260508155737_forms.js.map +7 -0
  973. package/dist/modules/forms/migrations/Migration20260520120000_forms.js +23 -0
  974. package/dist/modules/forms/migrations/Migration20260520120000_forms.js.map +7 -0
  975. package/dist/modules/forms/migrations/Migration20260521090000_forms.js +13 -0
  976. package/dist/modules/forms/migrations/Migration20260521090000_forms.js.map +7 -0
  977. package/dist/modules/forms/migrations/Migration20260521120000_forms.js +17 -0
  978. package/dist/modules/forms/migrations/Migration20260521120000_forms.js.map +7 -0
  979. package/dist/modules/forms/runner/FormRunner.js +786 -0
  980. package/dist/modules/forms/runner/FormRunner.js.map +7 -0
  981. package/dist/modules/forms/runner/MatrixField.js +150 -0
  982. package/dist/modules/forms/runner/MatrixField.js.map +7 -0
  983. package/dist/modules/forms/runner/RankingField.js +131 -0
  984. package/dist/modules/forms/runner/RankingField.js.map +7 -0
  985. package/dist/modules/forms/runner/ScaleField.js +78 -0
  986. package/dist/modules/forms/runner/ScaleField.js.map +7 -0
  987. package/dist/modules/forms/runner/SignatureField.js +132 -0
  988. package/dist/modules/forms/runner/SignatureField.js.map +7 -0
  989. package/dist/modules/forms/runner/tamper-check.js +85 -0
  990. package/dist/modules/forms/runner/tamper-check.js.map +7 -0
  991. package/dist/modules/forms/schema/address-countries.js +43 -0
  992. package/dist/modules/forms/schema/address-countries.js.map +7 -0
  993. package/dist/modules/forms/schema/field-type-patterns.js +15 -0
  994. package/dist/modules/forms/schema/field-type-patterns.js.map +7 -0
  995. package/dist/modules/forms/schema/field-type-registry-advanced.js +32 -0
  996. package/dist/modules/forms/schema/field-type-registry-advanced.js.map +7 -0
  997. package/dist/modules/forms/schema/field-type-registry.js +625 -0
  998. package/dist/modules/forms/schema/field-type-registry.js.map +7 -0
  999. package/dist/modules/forms/schema/file-field.js +64 -0
  1000. package/dist/modules/forms/schema/file-field.js.map +7 -0
  1001. package/dist/modules/forms/schema/group-field.js +143 -0
  1002. package/dist/modules/forms/schema/group-field.js.map +7 -0
  1003. package/dist/modules/forms/schema/jsonlogic-grammar.js +66 -0
  1004. package/dist/modules/forms/schema/jsonlogic-grammar.js.map +7 -0
  1005. package/dist/modules/forms/schema/jsonschema-extensions.js +739 -0
  1006. package/dist/modules/forms/schema/jsonschema-extensions.js.map +7 -0
  1007. package/dist/modules/forms/schema/layout-catalog.js +38 -0
  1008. package/dist/modules/forms/schema/layout-catalog.js.map +7 -0
  1009. package/dist/modules/forms/schema/signature-field.js +67 -0
  1010. package/dist/modules/forms/schema/signature-field.js.map +7 -0
  1011. package/dist/modules/forms/schema/style-extensions.js +77 -0
  1012. package/dist/modules/forms/schema/style-extensions.js.map +7 -0
  1013. package/dist/modules/forms/schema/style-validators.js +181 -0
  1014. package/dist/modules/forms/schema/style-validators.js.map +7 -0
  1015. package/dist/modules/forms/services/access-audit-logger.js +179 -0
  1016. package/dist/modules/forms/services/access-audit-logger.js.map +7 -0
  1017. package/dist/modules/forms/services/analytics-service.js +306 -0
  1018. package/dist/modules/forms/services/analytics-service.js.map +7 -0
  1019. package/dist/modules/forms/services/anonymize-service.js +123 -0
  1020. package/dist/modules/forms/services/anonymize-service.js.map +7 -0
  1021. package/dist/modules/forms/services/attachment-service.js +123 -0
  1022. package/dist/modules/forms/services/attachment-service.js.map +7 -0
  1023. package/dist/modules/forms/services/captcha-verifier.js +76 -0
  1024. package/dist/modules/forms/services/captcha-verifier.js.map +7 -0
  1025. package/dist/modules/forms/services/consent-record-service.js +96 -0
  1026. package/dist/modules/forms/services/consent-record-service.js.map +7 -0
  1027. package/dist/modules/forms/services/distribution-service.js +366 -0
  1028. package/dist/modules/forms/services/distribution-service.js.map +7 -0
  1029. package/dist/modules/forms/services/distribution-token.js +93 -0
  1030. package/dist/modules/forms/services/distribution-token.js.map +7 -0
  1031. package/dist/modules/forms/services/encryption-service.js +328 -0
  1032. package/dist/modules/forms/services/encryption-service.js.map +7 -0
  1033. package/dist/modules/forms/services/export-service.js +210 -0
  1034. package/dist/modules/forms/services/export-service.js.map +7 -0
  1035. package/dist/modules/forms/services/field-validation-service.js +168 -0
  1036. package/dist/modules/forms/services/field-validation-service.js.map +7 -0
  1037. package/dist/modules/forms/services/form-logic-evaluator.js +517 -0
  1038. package/dist/modules/forms/services/form-logic-evaluator.js.map +7 -0
  1039. package/dist/modules/forms/services/form-version-compiler.js +518 -0
  1040. package/dist/modules/forms/services/form-version-compiler.js.map +7 -0
  1041. package/dist/modules/forms/services/form-version-differ.js +89 -0
  1042. package/dist/modules/forms/services/form-version-differ.js.map +7 -0
  1043. package/dist/modules/forms/services/jsonlogic-evaluator.js +171 -0
  1044. package/dist/modules/forms/services/jsonlogic-evaluator.js.map +7 -0
  1045. package/dist/modules/forms/services/pdf-snapshot-service.js +526 -0
  1046. package/dist/modules/forms/services/pdf-snapshot-service.js.map +7 -0
  1047. package/dist/modules/forms/services/prefill-resolver.js +53 -0
  1048. package/dist/modules/forms/services/prefill-resolver.js.map +7 -0
  1049. package/dist/modules/forms/services/role-policy-service.js +55 -0
  1050. package/dist/modules/forms/services/role-policy-service.js.map +7 -0
  1051. package/dist/modules/forms/services/style-compiler.js +234 -0
  1052. package/dist/modules/forms/services/style-compiler.js.map +7 -0
  1053. package/dist/modules/forms/services/submission-service.js +858 -0
  1054. package/dist/modules/forms/services/submission-service.js.map +7 -0
  1055. package/dist/modules/forms/services/upload-scanner.js +9 -0
  1056. package/dist/modules/forms/services/upload-scanner.js.map +7 -0
  1057. package/dist/modules/forms/services/upload-validation.js +54 -0
  1058. package/dist/modules/forms/services/upload-validation.js.map +7 -0
  1059. package/dist/modules/forms/services/visibility-resolver.js +30 -0
  1060. package/dist/modules/forms/services/visibility-resolver.js.map +7 -0
  1061. package/dist/modules/forms/setup.js +18 -0
  1062. package/dist/modules/forms/setup.js.map +7 -0
  1063. package/dist/modules/forms/subscribers/forms-consent-projector.js +58 -0
  1064. package/dist/modules/forms/subscribers/forms-consent-projector.js.map +7 -0
  1065. package/dist/modules/forms/subscribers/forms-pdf-snapshot.js +68 -0
  1066. package/dist/modules/forms/subscribers/forms-pdf-snapshot.js.map +7 -0
  1067. package/dist/modules/forms/subscribers/forms-webhook-bridge-anonymized.js +29 -0
  1068. package/dist/modules/forms/subscribers/forms-webhook-bridge-anonymized.js.map +7 -0
  1069. package/dist/modules/forms/subscribers/forms-webhook-bridge.js +35 -0
  1070. package/dist/modules/forms/subscribers/forms-webhook-bridge.js.map +7 -0
  1071. package/dist/modules/forms/subscribers/invitation-email.js +100 -0
  1072. package/dist/modules/forms/subscribers/invitation-email.js.map +7 -0
  1073. package/dist/modules/forms/translations.js +10 -0
  1074. package/dist/modules/forms/translations.js.map +7 -0
  1075. package/dist/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.js +376 -0
  1076. package/dist/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.js.map +7 -0
  1077. package/dist/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.js +313 -0
  1078. package/dist/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.js.map +7 -0
  1079. package/dist/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.js +263 -0
  1080. package/dist/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.js.map +7 -0
  1081. package/dist/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.js +373 -0
  1082. package/dist/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.js.map +7 -0
  1083. package/dist/modules/forms/ui/public/EmbeddedForm.js +166 -0
  1084. package/dist/modules/forms/ui/public/EmbeddedForm.js.map +7 -0
  1085. package/dist/modules/forms/ui/public/FormRunner.js +359 -0
  1086. package/dist/modules/forms/ui/public/FormRunner.js.map +7 -0
  1087. package/dist/modules/forms/ui/public/FormTrigger.js +45 -0
  1088. package/dist/modules/forms/ui/public/FormTrigger.js.map +7 -0
  1089. package/dist/modules/forms/ui/public/PublicFormRunnerPage.js +14 -0
  1090. package/dist/modules/forms/ui/public/PublicFormRunnerPage.js.map +7 -0
  1091. package/dist/modules/forms/ui/public/components/CompletionScreen.js +55 -0
  1092. package/dist/modules/forms/ui/public/components/CompletionScreen.js.map +7 -0
  1093. package/dist/modules/forms/ui/public/components/LocaleSwitch.js +28 -0
  1094. package/dist/modules/forms/ui/public/components/LocaleSwitch.js.map +7 -0
  1095. package/dist/modules/forms/ui/public/components/ResumeGate.js +52 -0
  1096. package/dist/modules/forms/ui/public/components/ResumeGate.js.map +7 -0
  1097. package/dist/modules/forms/ui/public/components/ReviewStep.js +90 -0
  1098. package/dist/modules/forms/ui/public/components/ReviewStep.js.map +7 -0
  1099. package/dist/modules/forms/ui/public/components/SaveIndicator.js +65 -0
  1100. package/dist/modules/forms/ui/public/components/SaveIndicator.js.map +7 -0
  1101. package/dist/modules/forms/ui/public/components/SectionStepper.js +52 -0
  1102. package/dist/modules/forms/ui/public/components/SectionStepper.js.map +7 -0
  1103. package/dist/modules/forms/ui/public/index.js +38 -0
  1104. package/dist/modules/forms/ui/public/index.js.map +7 -0
  1105. package/dist/modules/forms/ui/public/renderers/index.js +737 -0
  1106. package/dist/modules/forms/ui/public/renderers/index.js.map +7 -0
  1107. package/dist/modules/forms/ui/public/state/logic-derivation.js +42 -0
  1108. package/dist/modules/forms/ui/public/state/logic-derivation.js.map +7 -0
  1109. package/dist/modules/forms/ui/public/state/runtime-client.js +306 -0
  1110. package/dist/modules/forms/ui/public/state/runtime-client.js.map +7 -0
  1111. package/dist/modules/forms/ui/public/state/useAutosave.js +64 -0
  1112. package/dist/modules/forms/ui/public/state/useAutosave.js.map +7 -0
  1113. package/dist/modules/forms/ui/public/state/useFormRunner.js +394 -0
  1114. package/dist/modules/forms/ui/public/state/useFormRunner.js.map +7 -0
  1115. package/dist/modules/forms/ui/public/style/LogoHeader.js +33 -0
  1116. package/dist/modules/forms/ui/public/style/LogoHeader.js.map +7 -0
  1117. package/dist/modules/forms/ui/public/style/applyStyle.js +12 -0
  1118. package/dist/modules/forms/ui/public/style/applyStyle.js.map +7 -0
  1119. package/dist/modules/forms/ui/public/types.js +18 -0
  1120. package/dist/modules/forms/ui/public/types.js.map +7 -0
  1121. package/dist/modules/forms/widgets/injection/access-audit-panel/widget.js +100 -0
  1122. package/dist/modules/forms/widgets/injection/access-audit-panel/widget.js.map +7 -0
  1123. package/dist/modules/forms/widgets/injection/anonymize-button/widget.js +148 -0
  1124. package/dist/modules/forms/widgets/injection/anonymize-button/widget.js.map +7 -0
  1125. package/dist/modules/forms/widgets/injection/audit-format.js +33 -0
  1126. package/dist/modules/forms/widgets/injection/audit-format.js.map +7 -0
  1127. package/dist/modules/forms/widgets/injection/context.js +5 -0
  1128. package/dist/modules/forms/widgets/injection/context.js.map +7 -0
  1129. package/dist/modules/forms/widgets/injection/embedded-form/widget.js +52 -0
  1130. package/dist/modules/forms/widgets/injection/embedded-form/widget.js.map +7 -0
  1131. package/dist/modules/forms/widgets/injection/pdf-download-button/widget.js +83 -0
  1132. package/dist/modules/forms/widgets/injection/pdf-download-button/widget.js.map +7 -0
  1133. package/dist/modules/forms/widgets/injection/self-audit-footer/widget.js +38 -0
  1134. package/dist/modules/forms/widgets/injection/self-audit-footer/widget.js.map +7 -0
  1135. package/dist/modules/forms/widgets/injection-table.js +32 -0
  1136. package/dist/modules/forms/widgets/injection-table.js.map +7 -0
  1137. package/dist/modules/forms/workers/pdf-snapshot.js +60 -0
  1138. package/dist/modules/forms/workers/pdf-snapshot.js.map +7 -0
  1139. package/dist/modules/forms/workers/retention-purge.js +112 -0
  1140. package/dist/modules/forms/workers/retention-purge.js.map +7 -0
  1141. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +7 -1
  1142. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  1143. package/dist/modules/inbox_ops/data/validators.js +2 -2
  1144. package/dist/modules/inbox_ops/data/validators.js.map +2 -2
  1145. package/dist/modules/inbox_ops/lib/llmProvider.js +5 -1
  1146. package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
  1147. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +2 -2
  1148. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
  1149. package/dist/modules/integrations/di.js +3 -1
  1150. package/dist/modules/integrations/di.js.map +2 -2
  1151. package/dist/modules/integrations/lib/credentials-service.js +29 -36
  1152. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  1153. package/dist/modules/messages/api/[id]/attachments/route.js +7 -3
  1154. package/dist/modules/messages/api/[id]/attachments/route.js.map +2 -2
  1155. package/dist/modules/messages/api/[id]/forward-preview/route.js +4 -3
  1156. package/dist/modules/messages/api/[id]/forward-preview/route.js.map +2 -2
  1157. package/dist/modules/messages/api/[id]/route.js +29 -3
  1158. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  1159. package/dist/modules/messages/api/route.js +91 -2
  1160. package/dist/modules/messages/api/route.js.map +2 -2
  1161. package/dist/modules/messages/commands/messages.js +40 -6
  1162. package/dist/modules/messages/commands/messages.js.map +2 -2
  1163. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -1
  1164. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  1165. package/dist/modules/messages/data/validators.js +11 -1
  1166. package/dist/modules/messages/data/validators.js.map +2 -2
  1167. package/dist/modules/messages/lib/channelThreadAccess.js +30 -0
  1168. package/dist/modules/messages/lib/channelThreadAccess.js.map +7 -0
  1169. package/dist/modules/messages/lib/composeSenderDelegation.js +87 -0
  1170. package/dist/modules/messages/lib/composeSenderDelegation.js.map +7 -0
  1171. package/dist/modules/messages/lib/composeSenderOptions.js +24 -0
  1172. package/dist/modules/messages/lib/composeSenderOptions.js.map +7 -0
  1173. package/dist/modules/messages/lib/email-sender.js +1 -11
  1174. package/dist/modules/messages/lib/email-sender.js.map +2 -2
  1175. package/dist/modules/messages/lib/forwarding.js +4 -4
  1176. package/dist/modules/messages/lib/forwarding.js.map +2 -2
  1177. package/dist/modules/messages/lib/markdownEmailBody.js +15 -0
  1178. package/dist/modules/messages/lib/markdownEmailBody.js.map +7 -0
  1179. package/dist/modules/messages/lib/messageSourceEntityTypes.js +7 -0
  1180. package/dist/modules/messages/lib/messageSourceEntityTypes.js.map +7 -0
  1181. package/dist/modules/messages/lib/routeHelpers.js +43 -0
  1182. package/dist/modules/messages/lib/routeHelpers.js.map +2 -2
  1183. package/dist/modules/messages/subscribers/message-notification.js +30 -1
  1184. package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
  1185. package/dist/modules/notifications/api/[id]/action/route.js +2 -1
  1186. package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
  1187. package/dist/modules/notifications/api/openapi.js +2 -1
  1188. package/dist/modules/notifications/api/openapi.js.map +2 -2
  1189. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +47 -28
  1190. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  1191. package/dist/modules/notifications/lib/deliveryConfig.js +27 -2
  1192. package/dist/modules/notifications/lib/deliveryConfig.js.map +2 -2
  1193. package/dist/modules/notifications/lib/notificationMapper.js +3 -1
  1194. package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
  1195. package/dist/modules/notifications/lib/safeHref.js +4 -0
  1196. package/dist/modules/notifications/lib/safeHref.js.map +2 -2
  1197. package/dist/modules/planner/api/availability-date-specific.js +2 -1
  1198. package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
  1199. package/dist/modules/planner/api/availability-weekly.js +2 -1
  1200. package/dist/modules/planner/api/availability-weekly.js.map +2 -2
  1201. package/dist/modules/progress/api/active/route.js +14 -2
  1202. package/dist/modules/progress/api/active/route.js.map +2 -2
  1203. package/dist/modules/progress/api/jobs/[id]/route.js +38 -22
  1204. package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
  1205. package/dist/modules/progress/api/jobs/route.js +20 -6
  1206. package/dist/modules/progress/api/jobs/route.js.map +2 -2
  1207. package/dist/modules/progress/api/requestScope.js +32 -0
  1208. package/dist/modules/progress/api/requestScope.js.map +7 -0
  1209. package/dist/modules/progress/lib/organizationScope.js +10 -0
  1210. package/dist/modules/progress/lib/organizationScope.js.map +7 -0
  1211. package/dist/modules/progress/lib/progressService.js.map +2 -2
  1212. package/dist/modules/progress/lib/progressServiceImpl.js +48 -15
  1213. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  1214. package/dist/modules/push_notifications/api/custom-send/route.js +4 -1
  1215. package/dist/modules/push_notifications/api/custom-send/route.js.map +2 -2
  1216. package/dist/modules/query_index/api/status.js +5 -3
  1217. package/dist/modules/query_index/api/status.js.map +2 -2
  1218. package/dist/modules/query_index/di.js +2 -1
  1219. package/dist/modules/query_index/di.js.map +2 -2
  1220. package/dist/modules/query_index/lib/engine.js +2 -1
  1221. package/dist/modules/query_index/lib/engine.js.map +2 -2
  1222. package/dist/modules/query_index/lib/request-encryption.js +16 -0
  1223. package/dist/modules/query_index/lib/request-encryption.js.map +7 -0
  1224. package/dist/modules/query_index/lib/search-tokens.js +31 -1
  1225. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  1226. package/dist/modules/resources/api/resources/tags/assign/route.js +2 -1
  1227. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  1228. package/dist/modules/resources/api/resources/tags/unassign/route.js +2 -1
  1229. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  1230. package/dist/modules/resources/commands/activities.js +2 -2
  1231. package/dist/modules/resources/commands/activities.js.map +2 -2
  1232. package/dist/modules/sales/api/document-history/route.js +5 -1
  1233. package/dist/modules/sales/api/document-history/route.js.map +2 -2
  1234. package/dist/modules/sales/api/document-numbers/route.js +2 -1
  1235. package/dist/modules/sales/api/document-numbers/route.js.map +2 -2
  1236. package/dist/modules/sales/api/quotes/convert/route.js +2 -1
  1237. package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
  1238. package/dist/modules/sales/api/quotes/send/route.js +2 -1
  1239. package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
  1240. package/dist/modules/sales/api/settings/document-numbers/route.js +7 -1
  1241. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  1242. package/dist/modules/sales/api/settings/order-editing/route.js +7 -1
  1243. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  1244. package/dist/modules/sales/commands/documents.js +354 -138
  1245. package/dist/modules/sales/commands/documents.js.map +3 -3
  1246. package/dist/modules/sales/commands/payments.js +2 -1
  1247. package/dist/modules/sales/commands/payments.js.map +2 -2
  1248. package/dist/modules/sales/commands/settings.js +25 -5
  1249. package/dist/modules/sales/commands/settings.js.map +2 -2
  1250. package/dist/modules/sales/commands/shipments.js +2 -1
  1251. package/dist/modules/sales/commands/shipments.js.map +2 -2
  1252. package/dist/modules/sales/components/DocumentNumberSettings.js +32 -21
  1253. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  1254. package/dist/modules/sales/components/documents/ItemsSection.js +49 -11
  1255. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  1256. package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
  1257. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  1258. package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
  1259. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  1260. package/dist/modules/sales/lib/lineSnapshots.js +19 -0
  1261. package/dist/modules/sales/lib/lineSnapshots.js.map +2 -2
  1262. package/dist/modules/sales/setup.js +4 -4
  1263. package/dist/modules/sales/setup.js.map +2 -2
  1264. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +2 -2
  1265. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
  1266. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +2 -2
  1267. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +2 -2
  1268. package/dist/modules/staff/api/leave-requests/accept/route.js +2 -1
  1269. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  1270. package/dist/modules/staff/api/leave-requests/reject/route.js +2 -1
  1271. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  1272. package/dist/modules/staff/api/leave-requests.js +2 -1
  1273. package/dist/modules/staff/api/leave-requests.js.map +2 -2
  1274. package/dist/modules/staff/api/team-members/self/route.js +2 -1
  1275. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  1276. package/dist/modules/staff/api/team-members/tags/assign/route.js +2 -1
  1277. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  1278. package/dist/modules/staff/api/team-members/tags/unassign/route.js +2 -1
  1279. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  1280. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +12 -11
  1281. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +2 -2
  1282. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +2 -1
  1283. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
  1284. package/dist/modules/staff/api/timesheets/my-projects/route.js +2 -1
  1285. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
  1286. package/dist/modules/staff/api/timesheets/my-work/route.js +2 -1
  1287. package/dist/modules/staff/api/timesheets/my-work/route.js.map +2 -2
  1288. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +2 -1
  1289. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
  1290. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +2 -1
  1291. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +2 -2
  1292. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +3 -2
  1293. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +2 -2
  1294. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +3 -2
  1295. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +2 -2
  1296. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +3 -2
  1297. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +2 -2
  1298. package/dist/modules/staff/api/timesheets/reports/preview/route.js +2 -1
  1299. package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +2 -2
  1300. package/dist/modules/staff/api/timesheets/reports/shared.js +2 -1
  1301. package/dist/modules/staff/api/timesheets/reports/shared.js.map +2 -2
  1302. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +2 -1
  1303. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +2 -2
  1304. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +2 -1
  1305. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +2 -2
  1306. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +2 -1
  1307. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +2 -2
  1308. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +2 -1
  1309. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
  1310. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +2 -1
  1311. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +2 -2
  1312. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +3 -2
  1313. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +2 -2
  1314. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +2 -1
  1315. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
  1316. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +2 -1
  1317. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
  1318. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +2 -1
  1319. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  1320. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +2 -1
  1321. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
  1322. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +2 -1
  1323. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
  1324. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +3 -2
  1325. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +2 -2
  1326. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +2 -1
  1327. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +2 -2
  1328. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +3 -2
  1329. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  1330. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +3 -2
  1331. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +2 -2
  1332. package/dist/modules/staff/api/timesheets/time-projects/route.js +2 -1
  1333. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
  1334. package/dist/modules/staff/commands/activities.js +2 -2
  1335. package/dist/modules/staff/commands/activities.js.map +2 -2
  1336. package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
  1337. package/dist/modules/staff/commands/timesheets-reports.js +55 -14
  1338. package/dist/modules/staff/commands/timesheets-reports.js.map +2 -2
  1339. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +2 -1
  1340. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +2 -2
  1341. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +32 -3
  1342. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +2 -2
  1343. package/dist/modules/staff/lib/timesheets-reports/reportPeriod.js +16 -0
  1344. package/dist/modules/staff/lib/timesheets-reports/reportPeriod.js.map +7 -0
  1345. package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js +15 -10
  1346. package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js.map +2 -2
  1347. package/dist/modules/sync_excel/api/import/route.js +35 -12
  1348. package/dist/modules/sync_excel/api/import/route.js.map +2 -2
  1349. package/dist/modules/sync_excel/api/upload/route.js +29 -4
  1350. package/dist/modules/sync_excel/api/upload/route.js.map +2 -2
  1351. package/dist/modules/translations/api/context.js +2 -1
  1352. package/dist/modules/translations/api/context.js.map +2 -2
  1353. package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -1
  1354. package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
  1355. package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +2 -1
  1356. package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +2 -2
  1357. package/dist/modules/warranty_claims/api/ai/suggest/route.js +2 -1
  1358. package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
  1359. package/dist/modules/warranty_claims/api/assign/route.js +2 -1
  1360. package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
  1361. package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -1
  1362. package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
  1363. package/dist/modules/warranty_claims/api/entitlement/route.js +2 -1
  1364. package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
  1365. package/dist/modules/warranty_claims/api/events/route.js +2 -1
  1366. package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
  1367. package/dist/modules/warranty_claims/api/external/claims/route.js +2 -1
  1368. package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
  1369. package/dist/modules/warranty_claims/api/receiving/route.js +2 -1
  1370. package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
  1371. package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -1
  1372. package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
  1373. package/dist/modules/warranty_claims/api/return-label/route.js +2 -1
  1374. package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
  1375. package/dist/modules/warranty_claims/api/risk/route.js +2 -1
  1376. package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
  1377. package/dist/modules/warranty_claims/api/sales-return/route.js +2 -1
  1378. package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
  1379. package/dist/modules/warranty_claims/api/settings-general/route.js +2 -1
  1380. package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
  1381. package/dist/modules/warranty_claims/api/stats/route.js +9 -5
  1382. package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
  1383. package/dist/modules/warranty_claims/api/submit/route.js +2 -1
  1384. package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
  1385. package/dist/modules/warranty_claims/api/transition/route.js +2 -1
  1386. package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
  1387. package/dist/modules/warranty_claims/api/vendor-recovery/route.js +2 -1
  1388. package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
  1389. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
  1390. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
  1391. package/dist/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.js +1 -1
  1392. package/dist/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.js.map +2 -2
  1393. package/dist/modules/wms/api/dashboard/operational/route.js +2 -1
  1394. package/dist/modules/wms/api/dashboard/operational/route.js.map +2 -2
  1395. package/dist/modules/wms/api/inventory/cycle-count/route.js +1 -0
  1396. package/dist/modules/wms/api/inventory/cycle-count/route.js.map +2 -2
  1397. package/dist/modules/wms/api/inventory/helpers.js +2 -1
  1398. package/dist/modules/wms/api/inventory/helpers.js.map +2 -2
  1399. package/dist/modules/wms/api/inventory/import/helpers.js +2 -1
  1400. package/dist/modules/wms/api/inventory/import/helpers.js.map +2 -2
  1401. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +2 -1
  1402. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
  1403. package/dist/modules/wms/commands/inventory-actions.js +39 -1
  1404. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  1405. package/dist/modules/wms/components/backend/CycleCountWizardDialog.js +14 -1
  1406. package/dist/modules/wms/components/backend/CycleCountWizardDialog.js.map +2 -2
  1407. package/dist/modules/wms/di.js +5 -0
  1408. package/dist/modules/wms/di.js.map +2 -2
  1409. package/dist/modules/wms/lib/availabilityCache.js +86 -0
  1410. package/dist/modules/wms/lib/availabilityCache.js.map +7 -0
  1411. package/dist/modules/wms/lib/availabilityCalculation.js +212 -0
  1412. package/dist/modules/wms/lib/availabilityCalculation.js.map +7 -0
  1413. package/dist/modules/wms/lib/availabilityProvider.js +24 -0
  1414. package/dist/modules/wms/lib/availabilityProvider.js.map +7 -0
  1415. package/dist/modules/wms/lib/loadOperationalDashboard.js +42 -5
  1416. package/dist/modules/wms/lib/loadOperationalDashboard.js.map +2 -2
  1417. package/dist/modules/wms/lib/lowStockBalanceFilter.js +23 -10
  1418. package/dist/modules/wms/lib/lowStockBalanceFilter.js.map +2 -2
  1419. package/dist/modules/wms/lib/sqlInClause.js +9 -0
  1420. package/dist/modules/wms/lib/sqlInClause.js.map +7 -0
  1421. package/dist/modules/wms/lib/tryResolve.js +11 -0
  1422. package/dist/modules/wms/lib/tryResolve.js.map +7 -0
  1423. package/dist/modules/wms/subscribers/invalidate-availability-cache-policy.js +14 -0
  1424. package/dist/modules/wms/subscribers/invalidate-availability-cache-policy.js.map +7 -0
  1425. package/dist/modules/wms/subscribers/invalidate-availability-cache-profile.js +14 -0
  1426. package/dist/modules/wms/subscribers/invalidate-availability-cache-profile.js.map +7 -0
  1427. package/dist/modules/wms/subscribers/invalidate-availability-cache.js +16 -0
  1428. package/dist/modules/wms/subscribers/invalidate-availability-cache.js.map +7 -0
  1429. package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js +2 -2
  1430. package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js.map +2 -2
  1431. package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js +4 -3
  1432. package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js.map +2 -2
  1433. package/dist/modules/workflows/api/commands/route.js +4 -1
  1434. package/dist/modules/workflows/api/commands/route.js.map +2 -2
  1435. package/dist/modules/workflows/api/definitions/[id]/callers/route.js +4 -1
  1436. package/dist/modules/workflows/api/definitions/[id]/callers/route.js.map +2 -2
  1437. package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js +4 -2
  1438. package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js.map +2 -2
  1439. package/dist/modules/workflows/api/definitions/[id]/customize/route.js +4 -1
  1440. package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
  1441. package/dist/modules/workflows/api/definitions/[id]/draft/route.js +6 -1
  1442. package/dist/modules/workflows/api/definitions/[id]/draft/route.js.map +2 -2
  1443. package/dist/modules/workflows/api/definitions/[id]/publish/route.js +6 -1
  1444. package/dist/modules/workflows/api/definitions/[id]/publish/route.js.map +2 -2
  1445. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +4 -1
  1446. package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
  1447. package/dist/modules/workflows/api/definitions/[id]/route.js +5 -3
  1448. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  1449. package/dist/modules/workflows/api/definitions/[id]/test-step/route.js +4 -2
  1450. package/dist/modules/workflows/api/definitions/[id]/test-step/route.js.map +2 -2
  1451. package/dist/modules/workflows/api/definitions/generate/route.js +4 -1
  1452. package/dist/modules/workflows/api/definitions/generate/route.js.map +2 -2
  1453. package/dist/modules/workflows/api/definitions/route.js +4 -2
  1454. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  1455. package/dist/modules/workflows/api/endpoints/route.js +4 -1
  1456. package/dist/modules/workflows/api/endpoints/route.js.map +2 -2
  1457. package/dist/modules/workflows/api/functions/route.js +4 -1
  1458. package/dist/modules/workflows/api/functions/route.js.map +2 -2
  1459. package/dist/modules/workflows/api/grantable-features/route.js +2 -1
  1460. package/dist/modules/workflows/api/grantable-features/route.js.map +2 -2
  1461. package/dist/modules/workflows/api/instances/[id]/advance/route.js +4 -1
  1462. package/dist/modules/workflows/api/instances/[id]/advance/route.js.map +2 -2
  1463. package/dist/modules/workflows/api/instances/[id]/cancel/route.js +4 -1
  1464. package/dist/modules/workflows/api/instances/[id]/cancel/route.js.map +2 -2
  1465. package/dist/modules/workflows/api/instances/[id]/context/route.js +2 -1
  1466. package/dist/modules/workflows/api/instances/[id]/context/route.js.map +2 -2
  1467. package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js +4 -1
  1468. package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js.map +2 -2
  1469. package/dist/modules/workflows/api/instances/[id]/retry/route.js +4 -1
  1470. package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
  1471. package/dist/modules/workflows/api/instances/[id]/signal/route.js +4 -1
  1472. package/dist/modules/workflows/api/instances/[id]/signal/route.js.map +2 -2
  1473. package/dist/modules/workflows/api/instances/[id]/step-through/route.js +4 -2
  1474. package/dist/modules/workflows/api/instances/[id]/step-through/route.js.map +2 -2
  1475. package/dist/modules/workflows/api/instances/route.js +4 -2
  1476. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  1477. package/dist/modules/workflows/api/instances/validate-start/route.js +4 -1
  1478. package/dist/modules/workflows/api/instances/validate-start/route.js.map +2 -2
  1479. package/dist/modules/workflows/api/rule-usage/route.js +4 -1
  1480. package/dist/modules/workflows/api/rule-usage/route.js.map +2 -2
  1481. package/dist/modules/workflows/api/signals/route.js +4 -1
  1482. package/dist/modules/workflows/api/signals/route.js.map +2 -2
  1483. package/dist/modules/workflows/api/tasks/[id]/claim/route.js +4 -1
  1484. package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
  1485. package/dist/modules/workflows/api/tasks/[id]/complete/route.js +4 -1
  1486. package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
  1487. package/dist/modules/workflows/api/tasks/[id]/reassign/route.js +2 -1
  1488. package/dist/modules/workflows/api/tasks/[id]/reassign/route.js.map +2 -2
  1489. package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js +4 -1
  1490. package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js.map +2 -2
  1491. package/dist/modules/workflows/api/templates/route.js +4 -1
  1492. package/dist/modules/workflows/api/templates/route.js.map +2 -2
  1493. package/dist/modules/workflows/api/work-inbox/next/route.js +4 -1
  1494. package/dist/modules/workflows/api/work-inbox/next/route.js.map +2 -2
  1495. package/dist/modules/workflows/lib/activity-executor.js +87 -20
  1496. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  1497. package/dist/modules/workflows/lib/activity-types.js +4 -4
  1498. package/dist/modules/workflows/lib/activity-types.js.map +2 -2
  1499. package/dist/modules/workflows/lib/activity-worker-handler.js +16 -11
  1500. package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
  1501. package/dist/modules/workflows/lib/event-trigger-service.js +8 -1
  1502. package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
  1503. package/dist/modules/workflows/lib/step-handler.js +4 -4
  1504. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  1505. package/dist/test-utils/transactionalEntityManagerDouble.js +37 -0
  1506. package/dist/test-utils/transactionalEntityManagerDouble.js.map +7 -0
  1507. package/generated/entities/availability_policy/index.ts +19 -0
  1508. package/generated/entities/communication_channel/index.ts +1 -0
  1509. package/generated/entities/customer_email_conversation_share/index.ts +9 -0
  1510. package/generated/entities/customer_group/index.ts +15 -0
  1511. package/generated/entities/customer_group_membership/index.ts +13 -0
  1512. package/generated/entities/customer_group_terms/index.ts +15 -0
  1513. package/generated/entities/customer_interaction/index.ts +1 -0
  1514. package/generated/entities/form/index.ts +16 -0
  1515. package/generated/entities/form_access_audit/index.ts +9 -0
  1516. package/generated/entities/form_attachment/index.ts +13 -0
  1517. package/generated/entities/form_consent_record/index.ts +17 -0
  1518. package/generated/entities/form_distribution/index.ts +22 -0
  1519. package/generated/entities/form_invitation/index.ts +23 -0
  1520. package/generated/entities/form_submission/index.ts +18 -0
  1521. package/generated/entities/form_submission_actor/index.ts +10 -0
  1522. package/generated/entities/form_submission_revision/index.ts +13 -0
  1523. package/generated/entities/form_version/index.ts +17 -0
  1524. package/generated/entities/forms_encryption_key/index.ts +6 -0
  1525. package/generated/entities.ids.generated.ts +27 -2
  1526. package/generated/entity-fields-registry.ts +269 -0
  1527. package/package.json +18 -7
  1528. package/src/helpers/integration/communicationChannelsFixtures.ts +43 -0
  1529. package/src/helpers/integration/customerGroupsFixtures.ts +171 -0
  1530. package/src/helpers/integration/formsFixtures.ts +757 -0
  1531. package/src/modules/api_docs/api/get/version.ts +28 -11
  1532. package/src/modules/api_keys/api/keys/route.ts +89 -199
  1533. package/src/modules/api_keys/backend/api-keys/create/page.tsx +2 -2
  1534. package/src/modules/api_keys/commands/keys.ts +172 -0
  1535. package/src/modules/api_keys/i18n/de.json +1 -0
  1536. package/src/modules/api_keys/i18n/en.json +1 -0
  1537. package/src/modules/api_keys/i18n/es.json +1 -0
  1538. package/src/modules/api_keys/i18n/ko.json +1 -0
  1539. package/src/modules/api_keys/i18n/pl.json +1 -0
  1540. package/src/modules/api_keys/services/apiKeyService.ts +82 -49
  1541. package/src/modules/attachments/api/file/[id]/route.ts +9 -4
  1542. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +6 -4
  1543. package/src/modules/attachments/api/route.ts +10 -4
  1544. package/src/modules/attachments/components/AttachmentLibrary.tsx +16 -5
  1545. package/src/modules/attachments/lib/access.ts +1 -3
  1546. package/src/modules/attachments/lib/requestScope.ts +35 -3
  1547. package/src/modules/audit_logs/api/audit-logs/actions/export/route.ts +6 -1
  1548. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +41 -6
  1549. package/src/modules/audit_logs/api/audit-logs/actions/route.ts +6 -1
  1550. package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +39 -4
  1551. package/src/modules/audit_logs/data/validators.ts +1 -0
  1552. package/src/modules/audit_logs/lib/actorSubject.ts +82 -0
  1553. package/src/modules/audit_logs/lib/replayAuthorization.ts +61 -0
  1554. package/src/modules/audit_logs/services/actionLogService.ts +191 -47
  1555. package/src/modules/auth/api/features.ts +1 -1
  1556. package/src/modules/auth/cli.ts +31 -17
  1557. package/src/modules/auth/commands/acl.ts +26 -36
  1558. package/src/modules/auth/commands/interceptors.ts +23 -0
  1559. package/src/modules/auth/commands/roles.ts +475 -148
  1560. package/src/modules/auth/commands/users.ts +693 -81
  1561. package/src/modules/auth/i18n/de.json +22 -0
  1562. package/src/modules/auth/i18n/en.json +22 -0
  1563. package/src/modules/auth/i18n/es.json +22 -0
  1564. package/src/modules/auth/i18n/ko.json +22 -0
  1565. package/src/modules/auth/i18n/pl.json +22 -0
  1566. package/src/modules/auth/lib/authorizationStateLocks.ts +340 -0
  1567. package/src/modules/auth/lib/commandReplay.ts +194 -0
  1568. package/src/modules/auth/lib/executionPrincipal.ts +36 -28
  1569. package/src/modules/auth/lib/grantChecks.ts +10 -2
  1570. package/src/modules/auth/lib/setup-app.ts +97 -50
  1571. package/src/modules/auth/lib/tokenHash.ts +16 -1
  1572. package/src/modules/auth/services/rbacService.ts +578 -6
  1573. package/src/modules/availability/acl.ts +12 -0
  1574. package/src/modules/availability/api/check/route.ts +136 -0
  1575. package/src/modules/availability/api/openapi.ts +28 -0
  1576. package/src/modules/availability/api/policies/resolve-preview/route.ts +83 -0
  1577. package/src/modules/availability/api/policies/route.ts +244 -0
  1578. package/src/modules/availability/backend/availability/check/page.meta.ts +12 -0
  1579. package/src/modules/availability/backend/availability/check/page.tsx +223 -0
  1580. package/src/modules/availability/backend/availability/policies/ResolutionPreviewPanel.tsx +112 -0
  1581. package/src/modules/availability/backend/availability/policies/ScopeFields.tsx +55 -0
  1582. package/src/modules/availability/backend/availability/policies/StockManagedField.tsx +37 -0
  1583. package/src/modules/availability/backend/availability/policies/[id]/page.meta.ts +8 -0
  1584. package/src/modules/availability/backend/availability/policies/[id]/page.tsx +202 -0
  1585. package/src/modules/availability/backend/availability/policies/create/page.meta.ts +8 -0
  1586. package/src/modules/availability/backend/availability/policies/create/page.tsx +131 -0
  1587. package/src/modules/availability/backend/availability/policies/formGroups.ts +83 -0
  1588. package/src/modules/availability/backend/availability/policies/page.meta.ts +12 -0
  1589. package/src/modules/availability/backend/availability/policies/page.tsx +232 -0
  1590. package/src/modules/availability/backend/availability/policies/policyPayload.ts +29 -0
  1591. package/src/modules/availability/commands/policies.ts +430 -0
  1592. package/src/modules/availability/commands/scope.ts +26 -0
  1593. package/src/modules/availability/data/entities.ts +93 -0
  1594. package/src/modules/availability/data/validators.ts +87 -0
  1595. package/src/modules/availability/di.ts +68 -0
  1596. package/src/modules/availability/events.ts +17 -0
  1597. package/src/modules/availability/i18n/de.json +92 -0
  1598. package/src/modules/availability/i18n/en.json +92 -0
  1599. package/src/modules/availability/i18n/es.json +92 -0
  1600. package/src/modules/availability/i18n/ko.json +92 -0
  1601. package/src/modules/availability/i18n/pl.json +92 -0
  1602. package/src/modules/availability/index.ts +13 -0
  1603. package/src/modules/availability/lib/catalogTarget.ts +72 -0
  1604. package/src/modules/availability/lib/organizationScope.ts +18 -0
  1605. package/src/modules/availability/lib/policyResolution.ts +289 -0
  1606. package/src/modules/availability/lib/tryResolve.ts +8 -0
  1607. package/src/modules/availability/migrations/.snapshot-open-mercato.json +375 -0
  1608. package/src/modules/availability/migrations/Migration20260922095844_availability.ts +13 -0
  1609. package/src/modules/availability/migrations/Migration20260930120000_availability.ts +22 -0
  1610. package/src/modules/availability/setup.ts +10 -0
  1611. package/src/modules/catalog/api/categories/bulk-create/route.ts +119 -0
  1612. package/src/modules/catalog/api/categories/route.ts +70 -8
  1613. package/src/modules/catalog/api/products/bulk-create/route.ts +119 -0
  1614. package/src/modules/catalog/api/products/route.ts +55 -24
  1615. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +8 -16
  1616. package/src/modules/catalog/backend/catalog/products/create/page.tsx +16 -10
  1617. package/src/modules/catalog/commands/categories.ts +1 -0
  1618. package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
  1619. package/src/modules/catalog/commands/products.ts +69 -24
  1620. package/src/modules/catalog/components/PriceKindSettings.tsx +6 -20
  1621. package/src/modules/catalog/components/products/ProductUomSection.tsx +26 -37
  1622. package/src/modules/catalog/components/products/productForm.ts +6 -4
  1623. package/src/modules/catalog/data/validators.ts +22 -0
  1624. package/src/modules/catalog/di.ts +2 -1
  1625. package/src/modules/catalog/events.ts +9 -3
  1626. package/src/modules/catalog/i18n/de.json +40 -0
  1627. package/src/modules/catalog/i18n/en.json +40 -0
  1628. package/src/modules/catalog/i18n/es.json +40 -0
  1629. package/src/modules/catalog/i18n/ko.json +40 -0
  1630. package/src/modules/catalog/i18n/pl.json +40 -0
  1631. package/src/modules/catalog/lib/bulkCreateCategories.ts +349 -0
  1632. package/src/modules/catalog/lib/bulkCreateCheckpoint.ts +176 -0
  1633. package/src/modules/catalog/lib/bulkCreateMutationGuards.ts +62 -0
  1634. package/src/modules/catalog/lib/bulkCreateProducts.ts +340 -0
  1635. package/src/modules/catalog/lib/bulkDelete.ts +10 -14
  1636. package/src/modules/catalog/lib/catalogQueue.ts +14 -0
  1637. package/src/modules/catalog/lib/currencyOptions.ts +28 -0
  1638. package/src/modules/catalog/lib/productSearch.ts +16 -0
  1639. package/src/modules/catalog/lib/seeds.ts +2 -43
  1640. package/src/modules/catalog/lib/unitLabels.ts +52 -0
  1641. package/src/modules/catalog/migrations/Migration20260914120000.ts +51 -0
  1642. package/src/modules/catalog/migrations/Migration20260914120100.ts +50 -0
  1643. package/src/modules/catalog/workers/catalog-category-bulk-create.ts +48 -0
  1644. package/src/modules/catalog/workers/catalog-product-bulk-create.ts +48 -0
  1645. package/src/modules/communication_channels/acl.ts +8 -0
  1646. package/src/modules/communication_channels/api/get/me/channels/route.ts +4 -0
  1647. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +23 -0
  1648. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +3 -4
  1649. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +9 -1
  1650. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
  1651. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
  1652. package/src/modules/communication_channels/api/post/test-seed/route.ts +109 -24
  1653. package/src/modules/communication_channels/api/put/channels/[id]/visibility/route.ts +165 -0
  1654. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +247 -129
  1655. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +2 -1
  1656. package/src/modules/communication_channels/commands/set-channel-visibility.ts +138 -0
  1657. package/src/modules/communication_channels/data/enrichers.ts +42 -19
  1658. package/src/modules/communication_channels/data/entities.ts +21 -0
  1659. package/src/modules/communication_channels/di.ts +9 -0
  1660. package/src/modules/communication_channels/events.ts +7 -0
  1661. package/src/modules/communication_channels/i18n/de.json +16 -3
  1662. package/src/modules/communication_channels/i18n/en.json +16 -3
  1663. package/src/modules/communication_channels/i18n/es.json +16 -3
  1664. package/src/modules/communication_channels/i18n/ko.json +16 -3
  1665. package/src/modules/communication_channels/i18n/pl.json +16 -3
  1666. package/src/modules/communication_channels/index.ts +1 -1
  1667. package/src/modules/communication_channels/lib/access-control.ts +4 -1
  1668. package/src/modules/communication_channels/lib/adapter.ts +16 -6
  1669. package/src/modules/communication_channels/lib/channel-thread-access.ts +133 -0
  1670. package/src/modules/communication_channels/lib/credential-refresh.ts +27 -5
  1671. package/src/modules/communication_channels/lib/inbound-message-origin.ts +62 -0
  1672. package/src/modules/communication_channels/lib/oauth-state.ts +96 -1
  1673. package/src/modules/communication_channels/lib/outbound-delivery-intent.ts +57 -0
  1674. package/src/modules/communication_channels/lib/send-as-user.ts +2 -2
  1675. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +18 -1
  1676. package/src/modules/communication_channels/migrations/Migration20260825141000_communication_channels.ts +22 -0
  1677. package/src/modules/communication_channels/setup.ts +4 -0
  1678. package/src/modules/communication_channels/subscribers/outbound-bridge.ts +34 -9
  1679. package/src/modules/communication_channels/workers/gmail-history-sync.ts +1 -1
  1680. package/src/modules/communication_channels/workers/poll-channel.ts +1 -1
  1681. package/src/modules/communication_channels/workers/reaction-processor.ts +2 -2
  1682. package/src/modules/configs/api/upgrade-actions/route.ts +2 -1
  1683. package/src/modules/configs/cli.ts +2 -2
  1684. package/src/modules/configs/i18n/de.json +2 -0
  1685. package/src/modules/configs/i18n/en.json +2 -0
  1686. package/src/modules/configs/i18n/es.json +2 -0
  1687. package/src/modules/configs/i18n/ko.json +2 -0
  1688. package/src/modules/configs/i18n/pl.json +2 -0
  1689. package/src/modules/configs/lib/upgrade-actions.ts +29 -6
  1690. package/src/modules/configs/services/upgradeActionsService.ts +45 -30
  1691. package/src/modules/currencies/api/currencies/route.ts +11 -5
  1692. package/src/modules/currencies/api/exchange-rates/route.ts +11 -5
  1693. package/src/modules/customer_groups/acl.ts +10 -0
  1694. package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +355 -0
  1695. package/src/modules/customer_groups/api/customer-groups/crud.ts +627 -0
  1696. package/src/modules/customer_groups/api/customer-groups/explain-terms/route.ts +195 -0
  1697. package/src/modules/customer_groups/api/customer-groups/memberships/crud.ts +525 -0
  1698. package/src/modules/customer_groups/api/customer-groups/memberships/route.ts +65 -0
  1699. package/src/modules/customer_groups/api/customer-groups/reconcile/adopt/route.ts +150 -0
  1700. package/src/modules/customer_groups/api/customer-groups/reconcile/route.ts +90 -0
  1701. package/src/modules/customer_groups/api/customer-groups/reorder/route.ts +147 -0
  1702. package/src/modules/customer_groups/api/customer-groups/route.ts +65 -0
  1703. package/src/modules/customer_groups/api/openapi.ts +28 -0
  1704. package/src/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.ts +12 -0
  1705. package/src/modules/customer_groups/backend/customer-groups/[id]/edit/page.tsx +392 -0
  1706. package/src/modules/customer_groups/backend/customer-groups/create/page.meta.ts +25 -0
  1707. package/src/modules/customer_groups/backend/customer-groups/create/page.tsx +279 -0
  1708. package/src/modules/customer_groups/backend/customer-groups/orphans/page.meta.ts +12 -0
  1709. package/src/modules/customer_groups/backend/customer-groups/orphans/page.tsx +216 -0
  1710. package/src/modules/customer_groups/backend/customer-groups/page.meta.ts +11 -0
  1711. package/src/modules/customer_groups/backend/customer-groups/page.tsx +478 -0
  1712. package/src/modules/customer_groups/cli.ts +87 -0
  1713. package/src/modules/customer_groups/commands/reorderGroups.ts +163 -0
  1714. package/src/modules/customer_groups/components/CustomerGroupDefaultField.tsx +55 -0
  1715. package/src/modules/customer_groups/components/CustomerGroupParentField.tsx +194 -0
  1716. package/src/modules/customer_groups/components/CustomerGroupTermsPriceKindField.tsx +111 -0
  1717. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +273 -0
  1718. package/src/modules/customer_groups/components/GroupPickerField.tsx +142 -0
  1719. package/src/modules/customer_groups/components/OrphanBanner.tsx +92 -0
  1720. package/src/modules/customer_groups/components/customerGroupTree.ts +84 -0
  1721. package/src/modules/customer_groups/data/entities.ts +244 -0
  1722. package/src/modules/customer_groups/data/validators.ts +232 -0
  1723. package/src/modules/customer_groups/di.ts +25 -0
  1724. package/src/modules/customer_groups/events.ts +22 -0
  1725. package/src/modules/customer_groups/i18n/de.json +199 -0
  1726. package/src/modules/customer_groups/i18n/en.json +199 -0
  1727. package/src/modules/customer_groups/i18n/es.json +199 -0
  1728. package/src/modules/customer_groups/i18n/ko.json +199 -0
  1729. package/src/modules/customer_groups/i18n/pl.json +199 -0
  1730. package/src/modules/customer_groups/index.ts +13 -0
  1731. package/src/modules/customer_groups/lib/createConflictRecheck.ts +39 -0
  1732. package/src/modules/customer_groups/lib/customerScope.ts +145 -0
  1733. package/src/modules/customer_groups/lib/groupEvents.ts +50 -0
  1734. package/src/modules/customer_groups/lib/priceKindScope.ts +22 -0
  1735. package/src/modules/customer_groups/lib/reconcile.ts +299 -0
  1736. package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +905 -0
  1737. package/src/modules/customer_groups/migrations/Migration20260922095316_customer_groups.ts +26 -0
  1738. package/src/modules/customer_groups/migrations/Migration20260922103033_customer_groups.ts +22 -0
  1739. package/src/modules/customer_groups/migrations/Migration20260922105444_customer_groups.ts +17 -0
  1740. package/src/modules/customer_groups/migrations/Migration20260923193330_customer_groups.ts +20 -0
  1741. package/src/modules/customer_groups/migrations/Migration20260929083000_customer_groups.ts +21 -0
  1742. package/src/modules/customer_groups/services/customerGroupsService.ts +358 -0
  1743. package/src/modules/customer_groups/setup.ts +11 -0
  1744. package/src/modules/customer_groups/widgets/injection/group-picker-field/widget.ts +41 -0
  1745. package/src/modules/customer_groups/widgets/injection/person-groups-explain-terms.tsx +227 -0
  1746. package/src/modules/customer_groups/widgets/injection/person-groups-tab/widget.ts +21 -0
  1747. package/src/modules/customer_groups/widgets/injection/person-groups-tab.tsx +573 -0
  1748. package/src/modules/customer_groups/widgets/injection-table.ts +35 -0
  1749. package/src/modules/customers/acl.ts +13 -0
  1750. package/src/modules/customers/api/activities/route.ts +10 -0
  1751. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -1
  1752. package/src/modules/customers/api/companies/[id]/route.ts +20 -6
  1753. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -1
  1754. package/src/modules/customers/api/deals/[id]/people/route.ts +84 -4
  1755. package/src/modules/customers/api/deals/map/route.ts +11 -10
  1756. package/src/modules/customers/api/dictionaries/context.ts +2 -1
  1757. package/src/modules/customers/api/entity-roles-factory.ts +3 -2
  1758. package/src/modules/customers/api/interactions/[id]/visibility/route.ts +2 -1
  1759. package/src/modules/customers/api/interactions/cancel/route.ts +2 -1
  1760. package/src/modules/customers/api/interactions/complete/route.ts +2 -1
  1761. package/src/modules/customers/api/interactions/conflicts/route.ts +10 -10
  1762. package/src/modules/customers/api/interactions/counts/route.ts +25 -7
  1763. package/src/modules/customers/api/interactions/route.ts +34 -10
  1764. package/src/modules/customers/api/labels/assign/route.ts +2 -1
  1765. package/src/modules/customers/api/labels/route.ts +3 -2
  1766. package/src/modules/customers/api/labels/unassign/route.ts +2 -1
  1767. package/src/modules/customers/api/people/[id]/companies/context.ts +11 -2
  1768. package/src/modules/customers/api/people/[id]/email-share/route.ts +276 -0
  1769. package/src/modules/customers/api/people/[id]/email-threads/route.ts +14 -1
  1770. package/src/modules/customers/api/people/[id]/emails/route.ts +2 -1
  1771. package/src/modules/customers/api/people/[id]/route.ts +31 -22
  1772. package/src/modules/customers/api/people/check-phone/route.ts +2 -1
  1773. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +3 -2
  1774. package/src/modules/customers/api/pipeline-stages/route.ts +3 -3
  1775. package/src/modules/customers/api/pipelines/route.ts +3 -3
  1776. package/src/modules/customers/api/settings/address-format/route.ts +2 -1
  1777. package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +2 -1
  1778. package/src/modules/customers/api/settings/stuck-threshold/route.ts +2 -1
  1779. package/src/modules/customers/api/tags/assign/route.ts +2 -1
  1780. package/src/modules/customers/api/tags/unassign/route.ts +2 -1
  1781. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +4 -2
  1782. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +10 -35
  1783. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -18
  1784. package/src/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.tsx +115 -0
  1785. package/src/modules/customers/backend/customers/deals/page.tsx +121 -1
  1786. package/src/modules/customers/backend/customers/people/[id]/page.tsx +4 -2
  1787. package/src/modules/customers/commands/addresses.ts +117 -15
  1788. package/src/modules/customers/commands/companies.ts +10 -4
  1789. package/src/modules/customers/commands/emailConversationShares.ts +261 -0
  1790. package/src/modules/customers/commands/index.ts +1 -0
  1791. package/src/modules/customers/commands/interactions.ts +61 -21
  1792. package/src/modules/customers/commands/people.ts +3 -2
  1793. package/src/modules/customers/commands/shared.ts +4 -0
  1794. package/src/modules/customers/components/calendar/types.ts +1 -1
  1795. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +97 -399
  1796. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
  1797. package/src/modules/customers/components/detail/DealForm.tsx +118 -5
  1798. package/src/modules/customers/components/detail/DealOwnerSelect.tsx +161 -0
  1799. package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
  1800. package/src/modules/customers/components/detail/DealsSection.tsx +8 -14
  1801. package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
  1802. package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
  1803. package/src/modules/customers/components/detail/PersonEmailShareControl.tsx +273 -0
  1804. package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +26 -0
  1805. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +10 -2
  1806. package/src/modules/customers/components/detail/create/CreateDealForm.tsx +30 -0
  1807. package/src/modules/customers/components/detail/create/DealDetailsFields.tsx +12 -0
  1808. package/src/modules/customers/components/detail/create/DealSectionCard.tsx +7 -2
  1809. package/src/modules/customers/components/detail/create/dealFormTypes.ts +3 -0
  1810. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -4
  1811. package/src/modules/customers/components/formConfig.tsx +3 -0
  1812. package/src/modules/customers/components/linking/LinkEntityDialog.tsx +8 -0
  1813. package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
  1814. package/src/modules/customers/data/enrichers.ts +64 -5
  1815. package/src/modules/customers/data/entities.ts +105 -1
  1816. package/src/modules/customers/data/validators.ts +22 -0
  1817. package/src/modules/customers/events.ts +5 -0
  1818. package/src/modules/customers/i18n/de.json +49 -13
  1819. package/src/modules/customers/i18n/en.json +44 -8
  1820. package/src/modules/customers/i18n/es.json +44 -8
  1821. package/src/modules/customers/i18n/ko.json +44 -8
  1822. package/src/modules/customers/i18n/pl.json +44 -8
  1823. package/src/modules/customers/lib/assignableStaff.ts +17 -0
  1824. package/src/modules/customers/lib/calendar/editorPayload.ts +20 -13
  1825. package/src/modules/customers/lib/calendar/mapItem.ts +1 -0
  1826. package/src/modules/customers/lib/conversationShares.ts +250 -0
  1827. package/src/modules/customers/lib/dealsOrganizationScope.ts +5 -3
  1828. package/src/modules/customers/lib/interactionRequestContext.ts +2 -1
  1829. package/src/modules/customers/lib/link-channel-message-handler.ts +27 -4
  1830. package/src/modules/customers/lib/personDetailCacheTags.ts +68 -0
  1831. package/src/modules/customers/lib/personEmailThreads.ts +61 -6
  1832. package/src/modules/customers/lib/reassignDealOwner.ts +51 -0
  1833. package/src/modules/customers/lib/userNames.ts +39 -0
  1834. package/src/modules/customers/lib/visibilityFilter.ts +170 -3
  1835. package/src/modules/customers/migrations/.snapshot-open-mercato.json +240 -0
  1836. package/src/modules/customers/migrations/Migration20260825120000_customers.ts +19 -0
  1837. package/src/modules/customers/migrations/Migration20260825140000_customers.ts +41 -0
  1838. package/src/modules/customers/setup.ts +1 -0
  1839. package/src/modules/customers/subscribers/channel-visibility-changed.ts +50 -0
  1840. package/src/modules/dashboards/api/widgets/data/batch/route.ts +3 -8
  1841. package/src/modules/dashboards/api/widgets/data/route.ts +12 -18
  1842. package/src/modules/dashboards/lib/aggregations.ts +1 -1
  1843. package/src/modules/dashboards/lib/widgetScope.ts +2 -7
  1844. package/src/modules/data_sync/AGENTS.md +35 -1
  1845. package/src/modules/data_sync/api/runs/[id]/cancel.ts +5 -1
  1846. package/src/modules/data_sync/backend/data-sync/page.tsx +292 -19
  1847. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +205 -21
  1848. package/src/modules/data_sync/components/useDataSyncRunAccess.ts +27 -0
  1849. package/src/modules/data_sync/i18n/de.json +24 -0
  1850. package/src/modules/data_sync/i18n/en.json +24 -0
  1851. package/src/modules/data_sync/i18n/es.json +24 -0
  1852. package/src/modules/data_sync/i18n/ko.json +24 -0
  1853. package/src/modules/data_sync/i18n/pl.json +24 -0
  1854. package/src/modules/data_sync/lib/adapter.ts +37 -0
  1855. package/src/modules/data_sync/lib/resume-point.ts +63 -0
  1856. package/src/modules/data_sync/lib/start-run.ts +61 -2
  1857. package/src/modules/data_sync/lib/sync-run-service.ts +27 -34
  1858. package/src/modules/devices/api/[id]/route.ts +3 -2
  1859. package/src/modules/devices/api/admin/devices/route.ts +2 -1
  1860. package/src/modules/devices/api/route.ts +2 -1
  1861. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +3 -5
  1862. package/src/modules/dictionaries/api/context.ts +2 -1
  1863. package/src/modules/dictionaries/api/route.ts +4 -0
  1864. package/src/modules/dictionaries/components/DictionariesManager.tsx +4 -3
  1865. package/src/modules/dictionaries/data/validators.ts +9 -1
  1866. package/src/modules/dictionaries/i18n/de.json +3 -3
  1867. package/src/modules/dictionaries/i18n/en.json +3 -3
  1868. package/src/modules/dictionaries/i18n/es.json +3 -3
  1869. package/src/modules/dictionaries/i18n/ko.json +3 -3
  1870. package/src/modules/dictionaries/i18n/pl.json +3 -3
  1871. package/src/modules/directory/api/organization-branding/route.ts +10 -1
  1872. package/src/modules/directory/api/organizations/route.ts +1 -1
  1873. package/src/modules/directory/backend/directory/branding/page.tsx +24 -2
  1874. package/src/modules/directory/commands/organizations.ts +173 -80
  1875. package/src/modules/directory/i18n/de.json +1 -0
  1876. package/src/modules/directory/i18n/en.json +1 -0
  1877. package/src/modules/directory/i18n/es.json +1 -0
  1878. package/src/modules/directory/i18n/ko.json +1 -0
  1879. package/src/modules/directory/i18n/pl.json +1 -0
  1880. package/src/modules/directory/lib/hierarchy.ts +22 -1
  1881. package/src/modules/directory/utils/organizationScope.ts +102 -15
  1882. package/src/modules/directory/utils/organizationScopeFilter.ts +6 -3
  1883. package/src/modules/directory/utils/organizationScopeGuard.ts +9 -5
  1884. package/src/modules/entities/api/encryption.ts +73 -27
  1885. package/src/modules/entities/api/records.ts +9 -8
  1886. package/src/modules/entities/cli.ts +46 -24
  1887. package/src/modules/entities/data/entities.ts +15 -0
  1888. package/src/modules/entities/lib/definition-scope.ts +5 -1
  1889. package/src/modules/entities/lib/encryption-maps.ts +164 -0
  1890. package/src/modules/entities/lib/helpers.ts +95 -8
  1891. package/src/modules/entities/migrations/.snapshot-open-mercato.json +21 -0
  1892. package/src/modules/entities/migrations/.snapshot-openmercato.json +12 -0
  1893. package/src/modules/entities/migrations/Migration20260910120000.ts +42 -0
  1894. package/src/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.ts +130 -0
  1895. package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +2 -1
  1896. package/src/modules/eudr/api/plots/import/route.ts +2 -1
  1897. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +2 -1
  1898. package/src/modules/eudr/api/product-mappings/suggestions/route.ts +2 -1
  1899. package/src/modules/eudr/api/reports/annual/route.ts +2 -1
  1900. package/src/modules/eudr/api/statements/[id]/export/route.ts +2 -1
  1901. package/src/modules/eudr/api/statements/[id]/readiness/route.ts +2 -1
  1902. package/src/modules/eudr/api/suppliers/compliance/route.ts +2 -1
  1903. package/src/modules/feature_toggles/commands/global.ts +18 -15
  1904. package/src/modules/feature_toggles/lib/feature-flag-check.ts +8 -3
  1905. package/src/modules/forms/acl.ts +10 -0
  1906. package/src/modules/forms/api/[id]/analytics/route.ts +137 -0
  1907. package/src/modules/forms/api/[id]/distributions/route.ts +192 -0
  1908. package/src/modules/forms/api/[id]/route.ts +245 -0
  1909. package/src/modules/forms/api/[id]/run/context/route.ts +142 -0
  1910. package/src/modules/forms/api/[id]/run/submissions/route.ts +147 -0
  1911. package/src/modules/forms/api/[id]/submissions/route.ts +100 -0
  1912. package/src/modules/forms/api/[id]/theme-logo/route.ts +147 -0
  1913. package/src/modules/forms/api/[id]/versions/[versionId]/diff/route.ts +137 -0
  1914. package/src/modules/forms/api/[id]/versions/[versionId]/publish/route.ts +108 -0
  1915. package/src/modules/forms/api/[id]/versions/[versionId]/route.ts +200 -0
  1916. package/src/modules/forms/api/[id]/versions/fork/route.ts +98 -0
  1917. package/src/modules/forms/api/attachment-helpers.ts +160 -0
  1918. package/src/modules/forms/api/by-key/[key]/active/route.ts +165 -0
  1919. package/src/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.ts +92 -0
  1920. package/src/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.ts +96 -0
  1921. package/src/modules/forms/api/distributions/[distributionId]/invitations/route.ts +207 -0
  1922. package/src/modules/forms/api/distributions/[distributionId]/route.ts +213 -0
  1923. package/src/modules/forms/api/form-submissions/[id]/attachments/route.ts +114 -0
  1924. package/src/modules/forms/api/form-submissions/[id]/resume-token/route.ts +140 -0
  1925. package/src/modules/forms/api/form-submissions/[id]/route.ts +176 -0
  1926. package/src/modules/forms/api/form-submissions/[id]/submit/route.ts +91 -0
  1927. package/src/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.ts +93 -0
  1928. package/src/modules/forms/api/form-submissions/route.ts +133 -0
  1929. package/src/modules/forms/api/helpers.ts +175 -0
  1930. package/src/modules/forms/api/public/distributions/[slug]/embed-policy/route.ts +74 -0
  1931. package/src/modules/forms/api/public/distributions/[slug]/route.ts +101 -0
  1932. package/src/modules/forms/api/public/embed-loader/route.ts +136 -0
  1933. package/src/modules/forms/api/public/invitations/[token]/route.ts +107 -0
  1934. package/src/modules/forms/api/public/rate-limit.ts +69 -0
  1935. package/src/modules/forms/api/public/start/route.ts +195 -0
  1936. package/src/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.ts +86 -0
  1937. package/src/modules/forms/api/public/submissions/[id]/attachments/route.ts +108 -0
  1938. package/src/modules/forms/api/public/submissions/[id]/pdf/route.ts +96 -0
  1939. package/src/modules/forms/api/public/submissions/[id]/route.ts +189 -0
  1940. package/src/modules/forms/api/public/submissions/[id]/submit/route.ts +159 -0
  1941. package/src/modules/forms/api/route.ts +240 -0
  1942. package/src/modules/forms/api/runtime-helpers.ts +208 -0
  1943. package/src/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.ts +178 -0
  1944. package/src/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.ts +135 -0
  1945. package/src/modules/forms/api/submissions/[submissionId]/access-audit/route.ts +100 -0
  1946. package/src/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.ts +70 -0
  1947. package/src/modules/forms/api/submissions/[submissionId]/actors/route.ts +92 -0
  1948. package/src/modules/forms/api/submissions/[submissionId]/anonymize/route.ts +134 -0
  1949. package/src/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.ts +90 -0
  1950. package/src/modules/forms/api/submissions/[submissionId]/export/route.ts +111 -0
  1951. package/src/modules/forms/api/submissions/[submissionId]/pdf/route.ts +118 -0
  1952. package/src/modules/forms/api/submissions/[submissionId]/reopen/route.ts +69 -0
  1953. package/src/modules/forms/api/submissions/[submissionId]/revisions/route.ts +75 -0
  1954. package/src/modules/forms/api/submissions/[submissionId]/route.ts +90 -0
  1955. package/src/modules/forms/backend/forms/[id]/FormStudio.tsx +3269 -0
  1956. package/src/modules/forms/backend/forms/[id]/analytics/page.meta.ts +15 -0
  1957. package/src/modules/forms/backend/forms/[id]/analytics/page.tsx +289 -0
  1958. package/src/modules/forms/backend/forms/[id]/distributions/page.meta.ts +15 -0
  1959. package/src/modules/forms/backend/forms/[id]/distributions/page.tsx +16 -0
  1960. package/src/modules/forms/backend/forms/[id]/history/page.meta.ts +11 -0
  1961. package/src/modules/forms/backend/forms/[id]/history/page.tsx +229 -0
  1962. package/src/modules/forms/backend/forms/[id]/page.meta.ts +11 -0
  1963. package/src/modules/forms/backend/forms/[id]/page.tsx +17 -0
  1964. package/src/modules/forms/backend/forms/[id]/studio/YesNoRow.tsx +65 -0
  1965. package/src/modules/forms/backend/forms/[id]/studio/autosave-guard.ts +25 -0
  1966. package/src/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.tsx +24 -0
  1967. package/src/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.tsx +36 -0
  1968. package/src/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.tsx +308 -0
  1969. package/src/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.tsx +265 -0
  1970. package/src/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.tsx +176 -0
  1971. package/src/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.tsx +473 -0
  1972. package/src/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.ts +25 -0
  1973. package/src/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.ts +128 -0
  1974. package/src/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.ts +15 -0
  1975. package/src/modules/forms/backend/forms/[id]/studio/canvas/resize-math.ts +36 -0
  1976. package/src/modules/forms/backend/forms/[id]/studio/canvas/row-layout.ts +110 -0
  1977. package/src/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.tsx +264 -0
  1978. package/src/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.tsx +121 -0
  1979. package/src/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.tsx +237 -0
  1980. package/src/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.tsx +87 -0
  1981. package/src/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.tsx +260 -0
  1982. package/src/modules/forms/backend/forms/[id]/studio/logic/condition-model.ts +207 -0
  1983. package/src/modules/forms/backend/forms/[id]/studio/lucide-icons.ts +87 -0
  1984. package/src/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.tsx +561 -0
  1985. package/src/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.tsx +52 -0
  1986. package/src/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.tsx +378 -0
  1987. package/src/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.tsx +40 -0
  1988. package/src/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.tsx +225 -0
  1989. package/src/modules/forms/backend/forms/[id]/studio/palette/entries.ts +102 -0
  1990. package/src/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.tsx +1459 -0
  1991. package/src/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.tsx +49 -0
  1992. package/src/modules/forms/backend/forms/[id]/studio/recall.ts +0 -0
  1993. package/src/modules/forms/backend/forms/[id]/studio/schema-helpers.ts +2525 -0
  1994. package/src/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.tsx +137 -0
  1995. package/src/modules/forms/backend/forms/[id]/studio/style/ColorControl.tsx +257 -0
  1996. package/src/modules/forms/backend/forms/[id]/studio/style/contrast.ts +114 -0
  1997. package/src/modules/forms/backend/forms/[id]/studio/style/presets.ts +104 -0
  1998. package/src/modules/forms/backend/forms/[id]/studio/type-label.ts +13 -0
  1999. package/src/modules/forms/backend/forms/[id]/studio/types.ts +31 -0
  2000. package/src/modules/forms/backend/forms/[id]/studio/undo-controller.ts +52 -0
  2001. package/src/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.tsx +89 -0
  2002. package/src/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.tsx +167 -0
  2003. package/src/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.tsx +67 -0
  2004. package/src/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.tsx +166 -0
  2005. package/src/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.tsx +184 -0
  2006. package/src/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.tsx +83 -0
  2007. package/src/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.tsx +60 -0
  2008. package/src/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.tsx +65 -0
  2009. package/src/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.tsx +46 -0
  2010. package/src/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.tsx +122 -0
  2011. package/src/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.tsx +28 -0
  2012. package/src/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.tsx +84 -0
  2013. package/src/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.tsx +369 -0
  2014. package/src/modules/forms/backend/forms/[id]/submissions/components/RowBadges.ts +56 -0
  2015. package/src/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.tsx +604 -0
  2016. package/src/modules/forms/backend/forms/[id]/submissions/page.meta.ts +10 -0
  2017. package/src/modules/forms/backend/forms/[id]/submissions/page.tsx +264 -0
  2018. package/src/modules/forms/backend/forms/create/page.meta.ts +11 -0
  2019. package/src/modules/forms/backend/forms/create/page.tsx +113 -0
  2020. package/src/modules/forms/backend/forms/page.meta.ts +31 -0
  2021. package/src/modules/forms/backend/forms/page.tsx +269 -0
  2022. package/src/modules/forms/commands/distribution.ts +502 -0
  2023. package/src/modules/forms/commands/form-version.ts +806 -0
  2024. package/src/modules/forms/commands/form.ts +472 -0
  2025. package/src/modules/forms/commands/index.ts +18 -0
  2026. package/src/modules/forms/commands/invitation.ts +424 -0
  2027. package/src/modules/forms/commands/shared.ts +261 -0
  2028. package/src/modules/forms/commands/submission.ts +209 -0
  2029. package/src/modules/forms/data/entities.ts +686 -0
  2030. package/src/modules/forms/data/validators.ts +483 -0
  2031. package/src/modules/forms/di.ts +246 -0
  2032. package/src/modules/forms/encryption.ts +31 -0
  2033. package/src/modules/forms/events-payloads.ts +103 -0
  2034. package/src/modules/forms/events.ts +50 -0
  2035. package/src/modules/forms/extension-points.ts +41 -0
  2036. package/src/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.ts +9 -0
  2037. package/src/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.tsx +45 -0
  2038. package/src/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.ts +9 -0
  2039. package/src/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.tsx +56 -0
  2040. package/src/modules/forms/frontend/embed/[slug]/page.meta.ts +42 -0
  2041. package/src/modules/forms/frontend/embed/[slug]/page.tsx +171 -0
  2042. package/src/modules/forms/frontend/f/[slug]/page.meta.ts +9 -0
  2043. package/src/modules/forms/frontend/f/[slug]/page.tsx +10 -0
  2044. package/src/modules/forms/frontend/forms/[id]/run/page.meta.ts +17 -0
  2045. package/src/modules/forms/frontend/forms/[id]/run/page.tsx +83 -0
  2046. package/src/modules/forms/frontend/i/[token]/page.meta.ts +9 -0
  2047. package/src/modules/forms/frontend/i/[token]/page.tsx +10 -0
  2048. package/src/modules/forms/i18n/de.json +800 -0
  2049. package/src/modules/forms/i18n/en.json +800 -0
  2050. package/src/modules/forms/i18n/es.json +800 -0
  2051. package/src/modules/forms/i18n/ko.json +800 -0
  2052. package/src/modules/forms/i18n/pl.json +800 -0
  2053. package/src/modules/forms/index.ts +18 -0
  2054. package/src/modules/forms/lib/embed-frame-policy.ts +144 -0
  2055. package/src/modules/forms/lib/form-definition-slicing.ts +54 -0
  2056. package/src/modules/forms/lib/log-redaction.ts +91 -0
  2057. package/src/modules/forms/lib/navigable-url.ts +57 -0
  2058. package/src/modules/forms/lib/retention.ts +73 -0
  2059. package/src/modules/forms/lib/runtime-principal.ts +101 -0
  2060. package/src/modules/forms/migrations/.snapshot-open-mercato.json +3185 -0
  2061. package/src/modules/forms/migrations/Migration20260508135459_forms.ts +17 -0
  2062. package/src/modules/forms/migrations/Migration20260508140932_forms.ts +26 -0
  2063. package/src/modules/forms/migrations/Migration20260508155737_forms.ts +15 -0
  2064. package/src/modules/forms/migrations/Migration20260520120000_forms.ts +26 -0
  2065. package/src/modules/forms/migrations/Migration20260521090000_forms.ts +15 -0
  2066. package/src/modules/forms/migrations/Migration20260521120000_forms.ts +21 -0
  2067. package/src/modules/forms/runner/FormRunner.tsx +924 -0
  2068. package/src/modules/forms/runner/MatrixField.tsx +210 -0
  2069. package/src/modules/forms/runner/RankingField.tsx +207 -0
  2070. package/src/modules/forms/runner/ScaleField.tsx +102 -0
  2071. package/src/modules/forms/runner/SignatureField.tsx +158 -0
  2072. package/src/modules/forms/runner/tamper-check.ts +131 -0
  2073. package/src/modules/forms/schema/address-countries.ts +60 -0
  2074. package/src/modules/forms/schema/field-type-patterns.ts +21 -0
  2075. package/src/modules/forms/schema/field-type-registry-advanced.ts +58 -0
  2076. package/src/modules/forms/schema/field-type-registry.ts +819 -0
  2077. package/src/modules/forms/schema/file-field.ts +102 -0
  2078. package/src/modules/forms/schema/group-field.ts +235 -0
  2079. package/src/modules/forms/schema/jsonlogic-grammar.ts +95 -0
  2080. package/src/modules/forms/schema/jsonschema-extensions.ts +1060 -0
  2081. package/src/modules/forms/schema/layout-catalog.ts +68 -0
  2082. package/src/modules/forms/schema/signature-field.ts +120 -0
  2083. package/src/modules/forms/schema/style-extensions.ts +194 -0
  2084. package/src/modules/forms/schema/style-validators.ts +252 -0
  2085. package/src/modules/forms/services/access-audit-logger.ts +275 -0
  2086. package/src/modules/forms/services/analytics-service.ts +498 -0
  2087. package/src/modules/forms/services/anonymize-service.ts +177 -0
  2088. package/src/modules/forms/services/attachment-service.ts +223 -0
  2089. package/src/modules/forms/services/captcha-verifier.ts +156 -0
  2090. package/src/modules/forms/services/consent-record-service.ts +162 -0
  2091. package/src/modules/forms/services/distribution-service.ts +509 -0
  2092. package/src/modules/forms/services/distribution-token.ts +153 -0
  2093. package/src/modules/forms/services/encryption-service.ts +500 -0
  2094. package/src/modules/forms/services/export-service.ts +360 -0
  2095. package/src/modules/forms/services/field-validation-service.ts +300 -0
  2096. package/src/modules/forms/services/form-logic-evaluator.ts +718 -0
  2097. package/src/modules/forms/services/form-version-compiler.ts +829 -0
  2098. package/src/modules/forms/services/form-version-differ.ts +143 -0
  2099. package/src/modules/forms/services/jsonlogic-evaluator.ts +228 -0
  2100. package/src/modules/forms/services/pdf-snapshot-service.ts +782 -0
  2101. package/src/modules/forms/services/prefill-resolver.ts +125 -0
  2102. package/src/modules/forms/services/role-policy-service.ts +90 -0
  2103. package/src/modules/forms/services/style-compiler.ts +378 -0
  2104. package/src/modules/forms/services/submission-service.ts +1259 -0
  2105. package/src/modules/forms/services/upload-scanner.ts +36 -0
  2106. package/src/modules/forms/services/upload-validation.ts +94 -0
  2107. package/src/modules/forms/services/visibility-resolver.ts +63 -0
  2108. package/src/modules/forms/setup.ts +16 -0
  2109. package/src/modules/forms/subscribers/forms-consent-projector.ts +100 -0
  2110. package/src/modules/forms/subscribers/forms-pdf-snapshot.ts +112 -0
  2111. package/src/modules/forms/subscribers/forms-webhook-bridge-anonymized.ts +36 -0
  2112. package/src/modules/forms/subscribers/forms-webhook-bridge.ts +56 -0
  2113. package/src/modules/forms/subscribers/invitation-email.ts +180 -0
  2114. package/src/modules/forms/translations.ts +14 -0
  2115. package/src/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.tsx +401 -0
  2116. package/src/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.tsx +381 -0
  2117. package/src/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.tsx +332 -0
  2118. package/src/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.tsx +460 -0
  2119. package/src/modules/forms/ui/public/EmbeddedForm.tsx +248 -0
  2120. package/src/modules/forms/ui/public/FormRunner.tsx +491 -0
  2121. package/src/modules/forms/ui/public/FormTrigger.tsx +80 -0
  2122. package/src/modules/forms/ui/public/PublicFormRunnerPage.tsx +34 -0
  2123. package/src/modules/forms/ui/public/components/CompletionScreen.tsx +97 -0
  2124. package/src/modules/forms/ui/public/components/LocaleSwitch.tsx +42 -0
  2125. package/src/modules/forms/ui/public/components/ResumeGate.tsx +61 -0
  2126. package/src/modules/forms/ui/public/components/ReviewStep.tsx +153 -0
  2127. package/src/modules/forms/ui/public/components/SaveIndicator.tsx +69 -0
  2128. package/src/modules/forms/ui/public/components/SectionStepper.tsx +60 -0
  2129. package/src/modules/forms/ui/public/index.ts +48 -0
  2130. package/src/modules/forms/ui/public/renderers/index.tsx +889 -0
  2131. package/src/modules/forms/ui/public/state/logic-derivation.ts +91 -0
  2132. package/src/modules/forms/ui/public/state/runtime-client.ts +495 -0
  2133. package/src/modules/forms/ui/public/state/useAutosave.ts +97 -0
  2134. package/src/modules/forms/ui/public/state/useFormRunner.ts +547 -0
  2135. package/src/modules/forms/ui/public/style/LogoHeader.tsx +43 -0
  2136. package/src/modules/forms/ui/public/style/applyStyle.ts +26 -0
  2137. package/src/modules/forms/ui/public/types.ts +228 -0
  2138. package/src/modules/forms/widgets/injection/access-audit-panel/widget.tsx +144 -0
  2139. package/src/modules/forms/widgets/injection/anonymize-button/widget.tsx +177 -0
  2140. package/src/modules/forms/widgets/injection/audit-format.ts +60 -0
  2141. package/src/modules/forms/widgets/injection/context.ts +24 -0
  2142. package/src/modules/forms/widgets/injection/embedded-form/widget.tsx +102 -0
  2143. package/src/modules/forms/widgets/injection/pdf-download-button/widget.tsx +111 -0
  2144. package/src/modules/forms/widgets/injection/self-audit-footer/widget.tsx +56 -0
  2145. package/src/modules/forms/widgets/injection-table.ts +54 -0
  2146. package/src/modules/forms/workers/pdf-snapshot.ts +110 -0
  2147. package/src/modules/forms/workers/retention-purge.ts +169 -0
  2148. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +7 -1
  2149. package/src/modules/inbox_ops/data/validators.ts +2 -1
  2150. package/src/modules/inbox_ops/lib/llmProvider.ts +11 -0
  2151. package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +2 -2
  2152. package/src/modules/integrations/di.ts +5 -1
  2153. package/src/modules/integrations/lib/credentials-service.ts +45 -36
  2154. package/src/modules/messages/api/[id]/attachments/route.ts +12 -3
  2155. package/src/modules/messages/api/[id]/forward-preview/route.ts +9 -3
  2156. package/src/modules/messages/api/[id]/route.ts +83 -4
  2157. package/src/modules/messages/api/route.ts +123 -2
  2158. package/src/modules/messages/commands/messages.ts +81 -7
  2159. package/src/modules/messages/components/ComposeMessagePageClient.tsx +29 -1
  2160. package/src/modules/messages/data/validators.ts +10 -0
  2161. package/src/modules/messages/i18n/de.json +8 -0
  2162. package/src/modules/messages/i18n/en.json +8 -0
  2163. package/src/modules/messages/i18n/es.json +8 -0
  2164. package/src/modules/messages/i18n/ko.json +8 -0
  2165. package/src/modules/messages/i18n/pl.json +8 -0
  2166. package/src/modules/messages/lib/channelThreadAccess.ts +101 -0
  2167. package/src/modules/messages/lib/composeSenderDelegation.ts +182 -0
  2168. package/src/modules/messages/lib/composeSenderOptions.ts +56 -0
  2169. package/src/modules/messages/lib/email-sender.ts +1 -17
  2170. package/src/modules/messages/lib/forwarding.ts +17 -2
  2171. package/src/modules/messages/lib/markdownEmailBody.ts +25 -0
  2172. package/src/modules/messages/lib/messageSourceEntityTypes.ts +12 -0
  2173. package/src/modules/messages/lib/routeHelpers.ts +92 -0
  2174. package/src/modules/messages/subscribers/message-notification.ts +47 -1
  2175. package/src/modules/notifications/api/[id]/action/route.ts +2 -1
  2176. package/src/modules/notifications/api/openapi.ts +1 -0
  2177. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +77 -48
  2178. package/src/modules/notifications/i18n/de.json +1 -0
  2179. package/src/modules/notifications/i18n/en.json +1 -0
  2180. package/src/modules/notifications/i18n/es.json +1 -0
  2181. package/src/modules/notifications/i18n/ko.json +1 -0
  2182. package/src/modules/notifications/i18n/pl.json +1 -0
  2183. package/src/modules/notifications/lib/deliveryConfig.ts +31 -2
  2184. package/src/modules/notifications/lib/notificationMapper.ts +2 -0
  2185. package/src/modules/notifications/lib/safeHref.ts +7 -0
  2186. package/src/modules/planner/api/availability-date-specific.ts +2 -1
  2187. package/src/modules/planner/api/availability-weekly.ts +2 -1
  2188. package/src/modules/progress/AGENTS.md +2 -0
  2189. package/src/modules/progress/api/active/route.ts +14 -2
  2190. package/src/modules/progress/api/jobs/[id]/route.ts +44 -27
  2191. package/src/modules/progress/api/jobs/route.ts +22 -8
  2192. package/src/modules/progress/api/requestScope.ts +43 -0
  2193. package/src/modules/progress/lib/organizationScope.ts +17 -0
  2194. package/src/modules/progress/lib/progressService.ts +3 -2
  2195. package/src/modules/progress/lib/progressServiceImpl.ts +63 -14
  2196. package/src/modules/push_notifications/api/custom-send/route.ts +4 -1
  2197. package/src/modules/query_index/api/status.ts +4 -9
  2198. package/src/modules/query_index/di.ts +2 -1
  2199. package/src/modules/query_index/lib/engine.ts +2 -0
  2200. package/src/modules/query_index/lib/request-encryption.ts +31 -0
  2201. package/src/modules/query_index/lib/search-tokens.ts +42 -1
  2202. package/src/modules/resources/api/resources/tags/assign/route.ts +2 -1
  2203. package/src/modules/resources/api/resources/tags/unassign/route.ts +2 -1
  2204. package/src/modules/resources/commands/activities.ts +2 -2
  2205. package/src/modules/sales/api/document-history/route.ts +5 -1
  2206. package/src/modules/sales/api/document-numbers/route.ts +2 -1
  2207. package/src/modules/sales/api/quotes/convert/route.ts +2 -1
  2208. package/src/modules/sales/api/quotes/send/route.ts +2 -1
  2209. package/src/modules/sales/api/settings/document-numbers/route.ts +7 -1
  2210. package/src/modules/sales/api/settings/order-editing/route.ts +7 -1
  2211. package/src/modules/sales/commands/documents.ts +451 -131
  2212. package/src/modules/sales/commands/payments.ts +2 -1
  2213. package/src/modules/sales/commands/settings.ts +34 -6
  2214. package/src/modules/sales/commands/shipments.ts +2 -1
  2215. package/src/modules/sales/components/DocumentNumberSettings.tsx +41 -28
  2216. package/src/modules/sales/components/documents/ItemsSection.tsx +58 -13
  2217. package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
  2218. package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
  2219. package/src/modules/sales/i18n/de.json +2 -0
  2220. package/src/modules/sales/i18n/en.json +2 -0
  2221. package/src/modules/sales/i18n/es.json +2 -0
  2222. package/src/modules/sales/i18n/ko.json +2 -0
  2223. package/src/modules/sales/i18n/pl.json +2 -0
  2224. package/src/modules/sales/lib/lineSnapshots.ts +36 -0
  2225. package/src/modules/sales/setup.ts +4 -4
  2226. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +2 -2
  2227. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +2 -2
  2228. package/src/modules/staff/api/leave-requests/accept/route.ts +2 -1
  2229. package/src/modules/staff/api/leave-requests/reject/route.ts +2 -1
  2230. package/src/modules/staff/api/leave-requests.ts +2 -1
  2231. package/src/modules/staff/api/team-members/self/route.ts +2 -1
  2232. package/src/modules/staff/api/team-members/tags/assign/route.ts +2 -1
  2233. package/src/modules/staff/api/team-members/tags/unassign/route.ts +2 -1
  2234. package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +16 -16
  2235. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +2 -1
  2236. package/src/modules/staff/api/timesheets/my-projects/route.ts +2 -1
  2237. package/src/modules/staff/api/timesheets/my-work/route.ts +2 -1
  2238. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +2 -1
  2239. package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +2 -1
  2240. package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +3 -2
  2241. package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +3 -2
  2242. package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +3 -2
  2243. package/src/modules/staff/api/timesheets/reports/preview/route.ts +2 -1
  2244. package/src/modules/staff/api/timesheets/reports/shared.ts +2 -1
  2245. package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +2 -1
  2246. package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +2 -1
  2247. package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +2 -1
  2248. package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +2 -1
  2249. package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +2 -1
  2250. package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +3 -2
  2251. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +2 -1
  2252. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +2 -1
  2253. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +2 -1
  2254. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +2 -1
  2255. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +2 -1
  2256. package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +3 -2
  2257. package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +2 -1
  2258. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +3 -2
  2259. package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +3 -2
  2260. package/src/modules/staff/api/timesheets/time-projects/route.ts +2 -1
  2261. package/src/modules/staff/commands/activities.ts +2 -2
  2262. package/src/modules/staff/commands/timesheets-entries.ts +3 -2
  2263. package/src/modules/staff/commands/timesheets-reports.ts +88 -15
  2264. package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +1 -0
  2265. package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +69 -8
  2266. package/src/modules/staff/lib/timesheets-reports/reportPeriod.ts +18 -0
  2267. package/src/modules/staff/lib/timesheets-ui/CalendarPicker.tsx +21 -10
  2268. package/src/modules/sync_excel/api/import/route.ts +40 -12
  2269. package/src/modules/sync_excel/api/upload/route.ts +34 -4
  2270. package/src/modules/translations/api/context.ts +2 -1
  2271. package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -1
  2272. package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -1
  2273. package/src/modules/warranty_claims/api/ai/suggest/route.ts +2 -1
  2274. package/src/modules/warranty_claims/api/assign/route.ts +2 -1
  2275. package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -1
  2276. package/src/modules/warranty_claims/api/entitlement/route.ts +2 -1
  2277. package/src/modules/warranty_claims/api/events/route.ts +2 -1
  2278. package/src/modules/warranty_claims/api/external/claims/route.ts +2 -1
  2279. package/src/modules/warranty_claims/api/receiving/route.ts +2 -1
  2280. package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -1
  2281. package/src/modules/warranty_claims/api/return-label/route.ts +2 -1
  2282. package/src/modules/warranty_claims/api/risk/route.ts +2 -1
  2283. package/src/modules/warranty_claims/api/sales-return/route.ts +2 -1
  2284. package/src/modules/warranty_claims/api/settings-general/route.ts +2 -1
  2285. package/src/modules/warranty_claims/api/stats/route.ts +11 -11
  2286. package/src/modules/warranty_claims/api/submit/route.ts +2 -1
  2287. package/src/modules/warranty_claims/api/transition/route.ts +2 -1
  2288. package/src/modules/warranty_claims/api/vendor-recovery/route.ts +2 -1
  2289. package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -1
  2290. package/src/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.tsx +1 -1
  2291. package/src/modules/wms/api/dashboard/operational/route.ts +2 -1
  2292. package/src/modules/wms/api/inventory/cycle-count/route.ts +1 -0
  2293. package/src/modules/wms/api/inventory/helpers.ts +2 -1
  2294. package/src/modules/wms/api/inventory/import/helpers.ts +2 -1
  2295. package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +2 -1
  2296. package/src/modules/wms/commands/inventory-actions.ts +45 -1
  2297. package/src/modules/wms/components/backend/CycleCountWizardDialog.tsx +14 -1
  2298. package/src/modules/wms/di.ts +6 -0
  2299. package/src/modules/wms/i18n/de.json +1 -0
  2300. package/src/modules/wms/i18n/en.json +1 -0
  2301. package/src/modules/wms/i18n/es.json +1 -0
  2302. package/src/modules/wms/i18n/ko.json +1 -0
  2303. package/src/modules/wms/i18n/pl.json +1 -0
  2304. package/src/modules/wms/lib/availabilityCache.ts +137 -0
  2305. package/src/modules/wms/lib/availabilityCalculation.ts +351 -0
  2306. package/src/modules/wms/lib/availabilityProvider.ts +47 -0
  2307. package/src/modules/wms/lib/loadOperationalDashboard.ts +51 -3
  2308. package/src/modules/wms/lib/lowStockBalanceFilter.ts +23 -10
  2309. package/src/modules/wms/lib/sqlInClause.ts +21 -0
  2310. package/src/modules/wms/lib/tryResolve.ts +8 -0
  2311. package/src/modules/wms/subscribers/invalidate-availability-cache-policy.ts +18 -0
  2312. package/src/modules/wms/subscribers/invalidate-availability-cache-profile.ts +18 -0
  2313. package/src/modules/wms/subscribers/invalidate-availability-cache.ts +30 -0
  2314. package/src/modules/wms/widgets/notifications/WmsLowStockRenderer.tsx +2 -2
  2315. package/src/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.tsx +4 -3
  2316. package/src/modules/workflows/AGENTS.md +7 -5
  2317. package/src/modules/workflows/api/commands/route.ts +4 -1
  2318. package/src/modules/workflows/api/definitions/[id]/callers/route.ts +4 -1
  2319. package/src/modules/workflows/api/definitions/[id]/context-schema/route.ts +4 -2
  2320. package/src/modules/workflows/api/definitions/[id]/customize/route.ts +4 -1
  2321. package/src/modules/workflows/api/definitions/[id]/draft/route.ts +6 -1
  2322. package/src/modules/workflows/api/definitions/[id]/publish/route.ts +10 -1
  2323. package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +4 -1
  2324. package/src/modules/workflows/api/definitions/[id]/route.ts +5 -3
  2325. package/src/modules/workflows/api/definitions/[id]/test-step/route.ts +4 -2
  2326. package/src/modules/workflows/api/definitions/generate/route.ts +4 -1
  2327. package/src/modules/workflows/api/definitions/route.ts +4 -2
  2328. package/src/modules/workflows/api/endpoints/route.ts +4 -1
  2329. package/src/modules/workflows/api/functions/route.ts +4 -1
  2330. package/src/modules/workflows/api/grantable-features/route.ts +2 -1
  2331. package/src/modules/workflows/api/instances/[id]/advance/route.ts +4 -1
  2332. package/src/modules/workflows/api/instances/[id]/cancel/route.ts +4 -1
  2333. package/src/modules/workflows/api/instances/[id]/context/route.ts +2 -1
  2334. package/src/modules/workflows/api/instances/[id]/rerun-step/route.ts +4 -1
  2335. package/src/modules/workflows/api/instances/[id]/retry/route.ts +4 -1
  2336. package/src/modules/workflows/api/instances/[id]/signal/route.ts +4 -1
  2337. package/src/modules/workflows/api/instances/[id]/step-through/route.ts +4 -2
  2338. package/src/modules/workflows/api/instances/route.ts +4 -2
  2339. package/src/modules/workflows/api/instances/validate-start/route.ts +4 -1
  2340. package/src/modules/workflows/api/rule-usage/route.ts +4 -1
  2341. package/src/modules/workflows/api/signals/route.ts +4 -1
  2342. package/src/modules/workflows/api/tasks/[id]/claim/route.ts +4 -1
  2343. package/src/modules/workflows/api/tasks/[id]/complete/route.ts +4 -1
  2344. package/src/modules/workflows/api/tasks/[id]/reassign/route.ts +2 -1
  2345. package/src/modules/workflows/api/tasks/[id]/unclaim/route.ts +4 -1
  2346. package/src/modules/workflows/api/templates/route.ts +4 -1
  2347. package/src/modules/workflows/api/work-inbox/next/route.ts +4 -1
  2348. package/src/modules/workflows/lib/activity-executor.ts +125 -28
  2349. package/src/modules/workflows/lib/activity-types.ts +8 -4
  2350. package/src/modules/workflows/lib/activity-worker-handler.ts +16 -12
  2351. package/src/modules/workflows/lib/event-trigger-service.ts +18 -2
  2352. package/src/modules/workflows/lib/step-handler.ts +13 -8
  2353. package/src/test-utils/transactionalEntityManagerDouble.ts +51 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/bulk/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { emitCrudSideEffects, flushCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTeamMember, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport {\n staffTimeEntryBulkSaveSchema,\n type StaffTimeEntryBulkSaveInput,\n} from '../../../../data/validators'\nimport {\n buildTimeEntryLockedError,\n rebaseTimeEntryIntervalToDate,\n reconcileTimeEntryInterval,\n resolveTimeEntryBillable,\n resolveTimeEntryNotesInput,\n resolveTimeEntryProjectId,\n resolveTimeEntrySettings,\n roundedMinutesFor,\n timeEntryTaskMatchesProject,\n toStoredTimeEntryRateOverride,\n type LockedTimeEntryRef,\n} from '../../../../commands/timesheets-entries'\nimport { staffTimeEntryCrudEvents } from '../../../../lib/crud'\nimport { emitStaffEvent } from '../../../../events'\nimport { invalidateStaffTimeEntryCache } from '../../../../lib/timesheets/timeEntryCacheInvalidation'\nimport {\n STAFF_TIME_TRACKING_RESOURCE_KINDS,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\n/**\n * A grid cell is addressed by the day the work started, never by the timestamp,\n * so both sides of the comparison collapse to `YYYY-MM-DD` (D-8).\n */\nfunction cellDateKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\ntype BulkRowError = { path: string; message: string; value?: unknown }\n\n/**\n * The batch answers with a per-row error list rather than a thrown 422: one grid\n * save spans a week of cells, so the client needs to know WHICH row it must fix.\n * The single-entry command raises the same refusals as `CrudHttpError` field\n * errors \u2014 same rule, different reporting shape.\n */\nfunction bulkValidationError(errors: BulkRowError[]): NextResponse {\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n}\n\n/**\n * A row as the write loop needs it: the payload plus the two things that must be\n * settled before the lock gate can address a cell \u2014 the task it names (validated\n * in scope) and the project the entry actually lands on, which a task-only row\n * inherits from its task exactly as the single-entry path does.\n */\ntype BulkEntryInput = StaffTimeEntryBulkSaveInput['entries'][number]\n\ntype ResolvedBulkRow = {\n entry: BulkEntryInput\n task: { id: string; timeProjectId: string } | null\n timeProjectId: string\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeEntryBulkSaveSchema.safeParse(session.body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }))\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n }\n\n const { entries } = parsed.data\n\n // Tags are REFUSED here rather than accepted and dropped.\n //\n // The single-entry path assigns tags by dispatching the tag commands, which\n // fork their own EntityManager so they can carry their own audit entry, undo\n // and lock check. Inside this route's `em.transactional` that fork would not\n // see the rows the transaction has not committed yet, and dispatching AFTER\n // the commit would answer `{ ok: true, created: n }` for a batch whose tags\n // silently failed \u2014 a response carrying only counts cannot say which cell\n // lost them, which is the very silent-success class this route is being\n // fixed for. So the row is rejected and the user is pointed at the entry\n // dialog, which writes tags through the commands with all of their\n // guarantees intact.\n const taggedRows = entries.filter((entry) => entry.tagIds !== undefined)\n if (taggedRows.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.bulkTagsUnsupported',\n 'Tags cannot be changed from the timesheet grid. Open the time entry to edit its tags.',\n )\n return bulkValidationError(taggedRows.map(() => ({ path: 'entries[].tagIds', message })))\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const staffMember = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!staffMember) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.') })\n }\n const staffMemberId = staffMember.id\n\n // Validate that all referenced taskIds exist and are in-scope, in one query\n // for the whole batch. Same rule the single-entry path applies through\n // `requireTaskInScope`: a dangling task reference is refused rather than\n // stored, because the task rollups aggregate entries by `task_id` and a stale\n // or foreign UUID would silently move hours onto another project's card.\n const referencedTaskIds = [\n ...new Set(\n entries\n .map((entry) => entry.taskId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n ]\n const tasksById = new Map<string, { id: string; timeProjectId: string }>()\n if (referencedTaskIds.length > 0) {\n const validTasks = await em.find(StaffTimeTask, {\n id: { $in: referencedTaskIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'timeProjectId'] })\n for (const task of validTasks) tasksById.set(task.id, { id: task.id, timeProjectId: task.timeProjectId })\n const invalidTaskIds = referencedTaskIds.filter((id) => !tasksById.has(id))\n if (invalidTaskIds.length > 0) {\n const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')\n return bulkValidationError(\n invalidTaskIds.map((id) => ({ path: 'entries[].taskId', message, value: id })),\n )\n }\n }\n\n // Every row is pinned to the project it will actually land on BEFORE the lock\n // gate runs, because the gate addresses a cell by (project, date) \u2014 a task row\n // that inherited its project must be checked against that inherited cell, not\n // against a blank one.\n const resolvedRows: ResolvedBulkRow[] = []\n const missingProjectErrors: BulkRowError[] = []\n const taskProjectMismatchErrors: BulkRowError[] = []\n for (const entry of entries) {\n const task = entry.taskId ? tasksById.get(entry.taskId) ?? null : null\n const timeProjectId = resolveTimeEntryProjectId(entry.timeProjectId, task)\n if (!timeProjectId) {\n missingProjectErrors.push({\n path: 'entries[].timeProjectId',\n message: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),\n })\n continue\n }\n if (!timeEntryTaskMatchesProject(task, timeProjectId)) {\n taskProjectMismatchErrors.push({\n path: 'entries[].taskId',\n message: translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.'),\n value: entry.taskId,\n })\n continue\n }\n resolvedRows.push({ entry, task, timeProjectId })\n }\n if (missingProjectErrors.length > 0 || taskProjectMismatchErrors.length > 0) {\n return bulkValidationError([...missingProjectErrors, ...taskProjectMismatchErrors])\n }\n\n // Validate that all referenced timeProjectIds exist and are in-scope. The\n // project rows are kept, not just their ids: a created entry snapshots the\n // project's currency and inherits its billable default (D-3), so one query\n // serves validation and both defaults.\n const referencedProjectIds = [...new Set(resolvedRows.map((row) => row.timeProjectId))]\n const projectsById = new Map<string, { currencyCode?: string | null; billableByDefault?: boolean | null }>()\n if (referencedProjectIds.length > 0) {\n const validProjects = await em.find(StaffTimeProject, {\n id: { $in: referencedProjectIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'currencyCode', 'billableByDefault'] })\n for (const project of validProjects) {\n projectsById.set(project.id, {\n currencyCode: project.currencyCode ?? null,\n billableByDefault: project.billableByDefault ?? null,\n })\n }\n const invalidIds = referencedProjectIds.filter((id) => !projectsById.has(id))\n if (invalidIds.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.projectNotFound',\n 'Time project not found or not accessible.',\n )\n return bulkValidationError(\n invalidIds.map((id) => ({ path: 'entries[].timeProjectId', message, value: id })),\n )\n }\n }\n\n const guardInput = {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update' as const,\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n }\n const guardResult = await runStaffMutationGuards(container, guardInput)\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const existingIds = resolvedRows\n .map((row) => row.entry.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n\n // Validate referenced entry IDs upfront: a stale or foreign UUID would\n // otherwise fall through to the create branch in the loop below and insert\n // a duplicate row with that ID-less new identity. Reject as 422 instead.\n if (existingIds.length > 0) {\n const resolvedExisting = await em.find(\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n { fields: ['id'] },\n )\n const resolvedIdSet = new Set(resolvedExisting.map((entry) => entry.id))\n const invalidIds = existingIds.filter((id) => !resolvedIdSet.has(id))\n if (invalidIds.length > 0) {\n return NextResponse.json(\n {\n ok: false,\n errors: invalidIds.map((id) => ({\n path: 'entries[].id',\n message: translate(\n 'staff.timesheets.errors.entryNotFound',\n 'Time entry not found, deleted, or not owned by you.',\n ),\n value: id,\n })),\n },\n { status: 422 },\n )\n }\n }\n\n // --- Lock gate (risk R3) -------------------------------------------------\n //\n // This is the endpoint the lock is most likely to leak through, because the\n // grid addresses a cell by (project, date) and only carries an id when the\n // client happened to have one loaded. So both shapes are checked:\n //\n // * a row naming an id that resolves to a frozen entry, and\n // * an id-less row landing on a (project, date) cell a frozen entry already\n // occupies \u2014 the grid renders one summed value per cell, so creating a\n // second row there silently restates hours a closed report has billed.\n //\n // The batch fails WHOLESALE, before the transaction opens. A grid save is one\n // user gesture spanning a week of cells and the response carries only counts,\n // so applying the unlocked rows would leave the client unable to tell which\n // cells landed \u2014 and the route already rejects the whole payload for an\n // unknown project or a stale entry id, so a partial 409 would be the odd one\n // out. The 409 names every offending id and the reports that froze them, so\n // the grid can mark exactly those cells read-only and the user retries the\n // rest after reloading (which is also what makes the lock badge appear).\n //\n // The cell is addressed by the RESOLVED project, so a task row that inherited\n // its project is checked against the cell it will really occupy.\n const idlessRows = resolvedRows.filter((row) => !row.entry.id)\n const idlessCells = idlessRows.map((row) => `${cellDateKey(row.entry.date)}|${row.timeProjectId}`)\n const lockConditions: Record<string, unknown>[] = []\n if (existingIds.length > 0) lockConditions.push({ id: { $in: existingIds } })\n if (idlessCells.length > 0) {\n lockConditions.push({\n date: { $in: idlessRows.map((row) => row.entry.date) },\n timeProjectId: { $in: [...new Set(idlessRows.map((row) => row.timeProjectId))] },\n })\n }\n if (lockConditions.length > 0) {\n const existingIdSet = new Set(existingIds)\n const idlessCellSet = new Set(idlessCells)\n const lockedCandidates = await em.find(\n StaffTimeEntry,\n {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n lockedReportId: { $ne: null },\n $or: lockConditions,\n },\n { fields: ['id', 'lockedReportId', 'date', 'timeProjectId'] },\n )\n // The (date \u00D7 project) condition above is a cross product, so it can match a\n // locked entry on a cell the payload never touches \u2014 narrowed here.\n const blocked: LockedTimeEntryRef[] = lockedCandidates.filter(\n (row) =>\n existingIdSet.has(row.id) ||\n idlessCellSet.has(`${cellDateKey(row.date)}|${row.timeProjectId ?? ''}`),\n )\n if (blocked.length > 0) {\n throw buildTimeEntryLockedError(\n blocked.map((row) => ({ id: row.id, lockedReportId: row.lockedReportId })),\n translate,\n )\n }\n }\n\n type PendingChange = {\n action: 'created' | 'updated' | 'deleted'\n entity: StaffTimeEntry\n }\n\n // D-7 makes `rounded_minutes` the only input to cost, and this route writes\n // durations outside the entries command \u2014 so every row it touches is rounded\n // with the same tenant rule the command uses. The settings read is hoisted out\n // of the loop because it is tenant-scoped and identical for every row; the\n // billable default a created row falls back to comes from the same snapshot.\n const settings = await resolveTimeEntrySettings(container, tenantId)\n\n const { counts, pendingChanges } = await em.transactional(async (trx) => {\n let created = 0\n let updated = 0\n let deleted = 0\n const changes: PendingChange[] = []\n\n const existingEntries = existingIds.length > 0\n ? await findWithDecryption(\n trx,\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n\n const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]))\n\n for (const { entry, task, timeProjectId } of resolvedRows) {\n const project = projectsById.get(timeProjectId) ?? null\n // Both branches read the five fields the schema has always accepted\n // through the SAME helpers the single-entry command uses, so a grid write\n // and a dialog write cannot settle them differently (#silent-no-op).\n const notes = resolveTimeEntryNotesInput(entry)\n if (entry.id && existingMap.has(entry.id)) {\n const existing = existingMap.get(entry.id)!\n if (entry.durationMinutes === 0) {\n existing.deletedAt = new Date()\n deleted++\n changes.push({ action: 'deleted', entity: existing })\n } else {\n // T4.10(a). A grid cell carries a duration and nothing else, so writing\n // it straight onto an entry that has clocks would store a two-hour\n // duration against a 09:00\u201310:00 span \u2014 exactly the contradiction the\n // single-entry path already refuses to store. The same reconciliation\n // is called here rather than re-derived: only-a-duration means\n // `started_at` anchors and `ended_at` shifts to match, a clock that\n // does not exist is never manufactured (so a cell whose entry has no\n // clocks keeps having none, and a running timer is not stopped), and a\n // re-dated row takes its clocks with it.\n const interval = reconcileTimeEntryInterval({\n stored: rebaseTimeEntryIntervalToDate({\n stored: {\n startedAt: existing.startedAt ?? null,\n endedAt: existing.endedAt ?? null,\n durationMinutes: existing.durationMinutes,\n },\n fromDate: existing.date,\n toDate: entry.date,\n }),\n startedAt: undefined,\n endedAt: undefined,\n durationMinutes: entry.durationMinutes,\n })\n const projectChanged = timeProjectId !== (existing.timeProjectId ?? null)\n existing.date = entry.date\n existing.timeProjectId = timeProjectId\n // Snapshotted, never joined at read time, so a later project currency\n // change cannot re-denominate money already reported (D-3) \u2014 and only\n // restated when the entry actually moves between projects, exactly as\n // the single-entry update command does it.\n if (projectChanged) existing.rateCurrencyCode = project?.currencyCode ?? null\n existing.startedAt = interval.startedAt\n existing.endedAt = interval.endedAt\n existing.durationMinutes = interval.durationMinutes\n // D-7 keeps `rounded_minutes` the only input to cost, so it is restated\n // from the effective duration the reconciliation settled on.\n existing.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {\n tenantId,\n organizationId,\n })\n if (entry.taskId !== undefined) existing.taskId = task?.id ?? null\n if (entry.isBillable !== undefined) existing.isBillable = entry.isBillable\n if (entry.rateOverrideAmount !== undefined) {\n existing.rateOverrideAmount = toStoredTimeEntryRateOverride(entry.rateOverrideAmount)\n }\n if (notes !== undefined) existing.notes = notes\n existing.updatedAt = new Date()\n updated++\n changes.push({ action: 'updated', entity: existing })\n }\n } else {\n const now = new Date()\n const newEntry = trx.create(StaffTimeEntry, {\n tenantId,\n organizationId,\n staffMemberId,\n date: entry.date,\n timeProjectId,\n taskId: task?.id ?? null,\n durationMinutes: entry.durationMinutes,\n roundedMinutes: roundedMinutesFor(entry.durationMinutes, settings, {\n tenantId,\n organizationId,\n }),\n notes: notes ?? null,\n isBillable: resolveTimeEntryBillable({\n requested: entry.isBillable,\n project,\n settings,\n scope: { tenantId, organizationId },\n }),\n rateOverrideAmount: toStoredTimeEntryRateOverride(entry.rateOverrideAmount),\n rateCurrencyCode: project?.currencyCode ?? null,\n source: 'manual',\n createdAt: now,\n updatedAt: now,\n })\n created++\n changes.push({ action: 'created', entity: newEntry })\n }\n }\n\n await trx.flush()\n return { counts: { created, updated, deleted }, pendingChanges: changes }\n })\n\n const dataEngine = container.resolve<DataEngine>('dataEngine')\n for (const change of pendingChanges) {\n await emitCrudSideEffects({\n dataEngine,\n action: change.action,\n entity: change.entity,\n identifiers: {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n events: staffTimeEntryCrudEvents,\n })\n }\n await flushCrudSideEffects(dataEngine)\n\n const invalidatedRecordIds = new Set<string>()\n for (const change of pendingChanges) {\n if (invalidatedRecordIds.has(change.entity.id)) continue\n invalidatedRecordIds.add(change.entity.id)\n await invalidateStaffTimeEntryCache(\n container,\n {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n tenantId,\n `bulk:${change.action}`,\n )\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n // One batch-level event beside the per-row CRUD events already flushed above.\n // It carries no `id`, so the entry-scoped subscribers that key off one record\n // skip it and only the batch-aware ones (payroll exports, audit mirrors) react.\n void emitStaffEvent('staff.timesheets.time_entry.bulk_updated', {\n tenantId,\n organizationId,\n staffMemberId,\n ...counts,\n entryIds: pendingChanges.map((change) => change.entity.id),\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.bulk_updated failed', { err })\n })\n\n return session.respond(200, { ok: true, ...counts })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.bulk failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.bulkSave', 'Failed to bulk save time entries.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Bulk save time entries',\n methods: {\n POST: {\n summary: 'Bulk save time entries',\n description:\n 'Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 \u2014 tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets \u2014 by id, or by landing on its (project, date) cell \u2014 an entry frozen in a closed report.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryBulkSaveSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Bulk save completed',\n schema: z.object({\n ok: z.literal(true),\n created: z.number(),\n updated: z.number(),\n deleted: z.number(),\n }),\n },\n {\n status: 422,\n description: 'Validation error',\n schema: z.object({\n ok: z.literal(false),\n errors: z.array(z.object({ path: z.string(), message: z.string() })),\n }),\n },\n {\n status: 409,\n description: 'One or more rows target a time entry locked in a closed report; nothing was saved',\n schema: z.object({\n code: z.literal('time_entry_locked'),\n error: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n lockedEntryIds: z.array(z.string().uuid()),\n lockedReportIds: z.array(z.string().uuid()),\n }),\n },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { emitCrudSideEffects, flushCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTeamMember, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport {\n staffTimeEntryBulkSaveSchema,\n type StaffTimeEntryBulkSaveInput,\n} from '../../../../data/validators'\nimport {\n buildTimeEntryLockedError,\n rebaseTimeEntryIntervalToDate,\n reconcileTimeEntryInterval,\n resolveTimeEntryBillable,\n resolveTimeEntryNotesInput,\n resolveTimeEntryProjectId,\n resolveTimeEntrySettings,\n roundedMinutesFor,\n timeEntryTaskMatchesProject,\n toStoredTimeEntryRateOverride,\n type LockedTimeEntryRef,\n} from '../../../../commands/timesheets-entries'\nimport { staffTimeEntryCrudEvents } from '../../../../lib/crud'\nimport { emitStaffEvent } from '../../../../events'\nimport { invalidateStaffTimeEntryCache } from '../../../../lib/timesheets/timeEntryCacheInvalidation'\nimport {\n STAFF_TIME_TRACKING_RESOURCE_KINDS,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\n/**\n * A grid cell is addressed by the day the work started, never by the timestamp,\n * so both sides of the comparison collapse to `YYYY-MM-DD` (D-8).\n */\nfunction cellDateKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\ntype BulkRowError = { path: string; message: string; value?: unknown }\n\n/**\n * The batch answers with a per-row error list rather than a thrown 422: one grid\n * save spans a week of cells, so the client needs to know WHICH row it must fix.\n * The single-entry command raises the same refusals as `CrudHttpError` field\n * errors \u2014 same rule, different reporting shape.\n */\nfunction bulkValidationError(errors: BulkRowError[]): NextResponse {\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n}\n\n/**\n * A row as the write loop needs it: the payload plus the two things that must be\n * settled before the lock gate can address a cell \u2014 the task it names (validated\n * in scope) and the project the entry actually lands on, which a task-only row\n * inherits from its task exactly as the single-entry path does.\n */\ntype BulkEntryInput = StaffTimeEntryBulkSaveInput['entries'][number]\n\ntype ResolvedBulkRow = {\n entry: BulkEntryInput\n task: { id: string; timeProjectId: string } | null\n timeProjectId: string\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeEntryBulkSaveSchema.safeParse(session.body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }))\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n }\n\n const { entries } = parsed.data\n\n // Tags are REFUSED here rather than accepted and dropped.\n //\n // The single-entry path assigns tags by dispatching the tag commands, which\n // fork their own EntityManager so they can carry their own audit entry, undo\n // and lock check. Inside this route's `em.transactional` that fork would not\n // see the rows the transaction has not committed yet, and dispatching AFTER\n // the commit would answer `{ ok: true, created: n }` for a batch whose tags\n // silently failed \u2014 a response carrying only counts cannot say which cell\n // lost them, which is the very silent-success class this route is being\n // fixed for. So the row is rejected and the user is pointed at the entry\n // dialog, which writes tags through the commands with all of their\n // guarantees intact.\n const taggedRows = entries.filter((entry) => entry.tagIds !== undefined)\n if (taggedRows.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.bulkTagsUnsupported',\n 'Tags cannot be changed from the timesheet grid. Open the time entry to edit its tags.',\n )\n return bulkValidationError(taggedRows.map(() => ({ path: 'entries[].tagIds', message })))\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const staffMember = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!staffMember) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.') })\n }\n const staffMemberId = staffMember.id\n\n // Validate that all referenced taskIds exist and are in-scope, in one query\n // for the whole batch. Same rule the single-entry path applies through\n // `requireTaskInScope`: a dangling task reference is refused rather than\n // stored, because the task rollups aggregate entries by `task_id` and a stale\n // or foreign UUID would silently move hours onto another project's card.\n const referencedTaskIds = [\n ...new Set(\n entries\n .map((entry) => entry.taskId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n ]\n const tasksById = new Map<string, { id: string; timeProjectId: string }>()\n if (referencedTaskIds.length > 0) {\n const validTasks = await em.find(StaffTimeTask, {\n id: { $in: referencedTaskIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'timeProjectId'] })\n for (const task of validTasks) tasksById.set(task.id, { id: task.id, timeProjectId: task.timeProjectId })\n const invalidTaskIds = referencedTaskIds.filter((id) => !tasksById.has(id))\n if (invalidTaskIds.length > 0) {\n const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')\n return bulkValidationError(\n invalidTaskIds.map((id) => ({ path: 'entries[].taskId', message, value: id })),\n )\n }\n }\n\n // Every row is pinned to the project it will actually land on BEFORE the lock\n // gate runs, because the gate addresses a cell by (project, date) \u2014 a task row\n // that inherited its project must be checked against that inherited cell, not\n // against a blank one.\n const resolvedRows: ResolvedBulkRow[] = []\n const missingProjectErrors: BulkRowError[] = []\n const taskProjectMismatchErrors: BulkRowError[] = []\n for (const entry of entries) {\n const task = entry.taskId ? tasksById.get(entry.taskId) ?? null : null\n const timeProjectId = resolveTimeEntryProjectId(entry.timeProjectId, task)\n if (!timeProjectId) {\n missingProjectErrors.push({\n path: 'entries[].timeProjectId',\n message: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),\n })\n continue\n }\n if (!timeEntryTaskMatchesProject(task, timeProjectId)) {\n taskProjectMismatchErrors.push({\n path: 'entries[].taskId',\n message: translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.'),\n value: entry.taskId,\n })\n continue\n }\n resolvedRows.push({ entry, task, timeProjectId })\n }\n if (missingProjectErrors.length > 0 || taskProjectMismatchErrors.length > 0) {\n return bulkValidationError([...missingProjectErrors, ...taskProjectMismatchErrors])\n }\n\n // Validate that all referenced timeProjectIds exist and are in-scope. The\n // project rows are kept, not just their ids: a created entry snapshots the\n // project's currency and inherits its billable default (D-3), so one query\n // serves validation and both defaults.\n const referencedProjectIds = [...new Set(resolvedRows.map((row) => row.timeProjectId))]\n const projectsById = new Map<string, { currencyCode?: string | null; billableByDefault?: boolean | null }>()\n if (referencedProjectIds.length > 0) {\n const validProjects = await em.find(StaffTimeProject, {\n id: { $in: referencedProjectIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'currencyCode', 'billableByDefault'] })\n for (const project of validProjects) {\n projectsById.set(project.id, {\n currencyCode: project.currencyCode ?? null,\n billableByDefault: project.billableByDefault ?? null,\n })\n }\n const invalidIds = referencedProjectIds.filter((id) => !projectsById.has(id))\n if (invalidIds.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.projectNotFound',\n 'Time project not found or not accessible.',\n )\n return bulkValidationError(\n invalidIds.map((id) => ({ path: 'entries[].timeProjectId', message, value: id })),\n )\n }\n }\n\n const guardInput = {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update' as const,\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n }\n const guardResult = await runStaffMutationGuards(container, guardInput)\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const existingIds = resolvedRows\n .map((row) => row.entry.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n\n // Validate referenced entry IDs upfront: a stale or foreign UUID would\n // otherwise fall through to the create branch in the loop below and insert\n // a duplicate row with that ID-less new identity. Reject as 422 instead.\n if (existingIds.length > 0) {\n const resolvedExisting = await em.find(\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n { fields: ['id'] },\n )\n const resolvedIdSet = new Set(resolvedExisting.map((entry) => entry.id))\n const invalidIds = existingIds.filter((id) => !resolvedIdSet.has(id))\n if (invalidIds.length > 0) {\n return NextResponse.json(\n {\n ok: false,\n errors: invalidIds.map((id) => ({\n path: 'entries[].id',\n message: translate(\n 'staff.timesheets.errors.entryNotFound',\n 'Time entry not found, deleted, or not owned by you.',\n ),\n value: id,\n })),\n },\n { status: 422 },\n )\n }\n }\n\n // --- Lock gate (risk R3) -------------------------------------------------\n //\n // This is the endpoint the lock is most likely to leak through, because the\n // grid addresses a cell by (project, date) and only carries an id when the\n // client happened to have one loaded. So both shapes are checked:\n //\n // * a row naming an id that resolves to a frozen entry, and\n // * an id-less row landing on a (project, date) cell a frozen entry already\n // occupies \u2014 the grid renders one summed value per cell, so creating a\n // second row there silently restates hours a closed report has billed.\n //\n // The batch fails WHOLESALE, before the transaction opens. A grid save is one\n // user gesture spanning a week of cells and the response carries only counts,\n // so applying the unlocked rows would leave the client unable to tell which\n // cells landed \u2014 and the route already rejects the whole payload for an\n // unknown project or a stale entry id, so a partial 409 would be the odd one\n // out. The 409 names every offending id and the reports that froze them, so\n // the grid can mark exactly those cells read-only and the user retries the\n // rest after reloading (which is also what makes the lock badge appear).\n //\n // The cell is addressed by the RESOLVED project, so a task row that inherited\n // its project is checked against the cell it will really occupy.\n const idlessRows = resolvedRows.filter((row) => !row.entry.id)\n const idlessCells = idlessRows.map((row) => `${cellDateKey(row.entry.date)}|${row.timeProjectId}`)\n const lockConditions: Record<string, unknown>[] = []\n if (existingIds.length > 0) lockConditions.push({ id: { $in: existingIds } })\n if (idlessCells.length > 0) {\n lockConditions.push({\n date: { $in: idlessRows.map((row) => row.entry.date) },\n timeProjectId: { $in: [...new Set(idlessRows.map((row) => row.timeProjectId))] },\n })\n }\n if (lockConditions.length > 0) {\n const existingIdSet = new Set(existingIds)\n const idlessCellSet = new Set(idlessCells)\n const lockedCandidates = await em.find(\n StaffTimeEntry,\n {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n lockedReportId: { $ne: null },\n $or: lockConditions,\n },\n { fields: ['id', 'lockedReportId', 'date', 'timeProjectId'] },\n )\n // The (date \u00D7 project) condition above is a cross product, so it can match a\n // locked entry on a cell the payload never touches \u2014 narrowed here.\n const blocked: LockedTimeEntryRef[] = lockedCandidates.filter(\n (row) =>\n existingIdSet.has(row.id) ||\n idlessCellSet.has(`${cellDateKey(row.date)}|${row.timeProjectId ?? ''}`),\n )\n if (blocked.length > 0) {\n throw buildTimeEntryLockedError(\n blocked.map((row) => ({ id: row.id, lockedReportId: row.lockedReportId })),\n translate,\n )\n }\n }\n\n type PendingChange = {\n action: 'created' | 'updated' | 'deleted'\n entity: StaffTimeEntry\n }\n\n // D-7 makes `rounded_minutes` the only input to cost, and this route writes\n // durations outside the entries command \u2014 so every row it touches is rounded\n // with the same tenant rule the command uses. The settings read is hoisted out\n // of the loop because it is tenant-scoped and identical for every row; the\n // billable default a created row falls back to comes from the same snapshot.\n const settings = await resolveTimeEntrySettings(container, tenantId)\n\n const { counts, pendingChanges } = await em.transactional(async (trx) => {\n let created = 0\n let updated = 0\n let deleted = 0\n const changes: PendingChange[] = []\n\n const existingEntries = existingIds.length > 0\n ? await findWithDecryption(\n trx,\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n\n const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]))\n\n for (const { entry, task, timeProjectId } of resolvedRows) {\n const project = projectsById.get(timeProjectId) ?? null\n // Both branches read the five fields the schema has always accepted\n // through the SAME helpers the single-entry command uses, so a grid write\n // and a dialog write cannot settle them differently (#silent-no-op).\n const notes = resolveTimeEntryNotesInput(entry)\n if (entry.id && existingMap.has(entry.id)) {\n const existing = existingMap.get(entry.id)!\n if (entry.durationMinutes === 0) {\n existing.deletedAt = new Date()\n deleted++\n changes.push({ action: 'deleted', entity: existing })\n } else {\n // T4.10(a). A grid cell carries a duration and nothing else, so writing\n // it straight onto an entry that has clocks would store a two-hour\n // duration against a 09:00\u201310:00 span \u2014 exactly the contradiction the\n // single-entry path already refuses to store. The same reconciliation\n // is called here rather than re-derived: only-a-duration means\n // `started_at` anchors and `ended_at` shifts to match, a clock that\n // does not exist is never manufactured (so a cell whose entry has no\n // clocks keeps having none, and a running timer is not stopped), and a\n // re-dated row takes its clocks with it.\n const interval = reconcileTimeEntryInterval({\n stored: rebaseTimeEntryIntervalToDate({\n stored: {\n startedAt: existing.startedAt ?? null,\n endedAt: existing.endedAt ?? null,\n durationMinutes: existing.durationMinutes,\n },\n fromDate: existing.date,\n toDate: entry.date,\n }),\n startedAt: undefined,\n endedAt: undefined,\n durationMinutes: entry.durationMinutes,\n })\n const projectChanged = timeProjectId !== (existing.timeProjectId ?? null)\n existing.date = entry.date\n existing.timeProjectId = timeProjectId\n // Snapshotted, never joined at read time, so a later project currency\n // change cannot re-denominate money already reported (D-3) \u2014 and only\n // restated when the entry actually moves between projects, exactly as\n // the single-entry update command does it.\n if (projectChanged) existing.rateCurrencyCode = project?.currencyCode ?? null\n existing.startedAt = interval.startedAt\n existing.endedAt = interval.endedAt\n existing.durationMinutes = interval.durationMinutes\n // D-7 keeps `rounded_minutes` the only input to cost, so it is restated\n // from the effective duration the reconciliation settled on.\n existing.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {\n tenantId,\n organizationId,\n })\n if (entry.taskId !== undefined) existing.taskId = task?.id ?? null\n if (entry.isBillable !== undefined) existing.isBillable = entry.isBillable\n if (entry.rateOverrideAmount !== undefined) {\n existing.rateOverrideAmount = toStoredTimeEntryRateOverride(entry.rateOverrideAmount)\n }\n if (notes !== undefined) existing.notes = notes\n existing.updatedAt = new Date()\n updated++\n changes.push({ action: 'updated', entity: existing })\n }\n } else {\n const now = new Date()\n const newEntry = trx.create(StaffTimeEntry, {\n tenantId,\n organizationId,\n staffMemberId,\n date: entry.date,\n timeProjectId,\n taskId: task?.id ?? null,\n durationMinutes: entry.durationMinutes,\n roundedMinutes: roundedMinutesFor(entry.durationMinutes, settings, {\n tenantId,\n organizationId,\n }),\n notes: notes ?? null,\n isBillable: resolveTimeEntryBillable({\n requested: entry.isBillable,\n project,\n settings,\n scope: { tenantId, organizationId },\n }),\n rateOverrideAmount: toStoredTimeEntryRateOverride(entry.rateOverrideAmount),\n rateCurrencyCode: project?.currencyCode ?? null,\n source: 'manual',\n createdAt: now,\n updatedAt: now,\n })\n created++\n changes.push({ action: 'created', entity: newEntry })\n }\n }\n\n await trx.flush()\n return { counts: { created, updated, deleted }, pendingChanges: changes }\n })\n\n const dataEngine = container.resolve<DataEngine>('dataEngine')\n for (const change of pendingChanges) {\n await emitCrudSideEffects({\n dataEngine,\n action: change.action,\n entity: change.entity,\n identifiers: {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n events: staffTimeEntryCrudEvents,\n })\n }\n await flushCrudSideEffects(dataEngine)\n\n const invalidatedRecordIds = new Set<string>()\n for (const change of pendingChanges) {\n if (invalidatedRecordIds.has(change.entity.id)) continue\n invalidatedRecordIds.add(change.entity.id)\n await invalidateStaffTimeEntryCache(\n container,\n {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n tenantId,\n `bulk:${change.action}`,\n )\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n // One batch-level event beside the per-row CRUD events already flushed above.\n // It carries no `id`, so the entry-scoped subscribers that key off one record\n // skip it and only the batch-aware ones (payroll exports, audit mirrors) react.\n void emitStaffEvent('staff.timesheets.time_entry.bulk_updated', {\n tenantId,\n organizationId,\n staffMemberId,\n ...counts,\n entryIds: pendingChanges.map((change) => change.entity.id),\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.bulk_updated failed', { err })\n })\n\n return session.respond(200, { ok: true, ...counts })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.bulk failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.bulkSave', 'Failed to bulk save time entries.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Bulk save time entries',\n methods: {\n POST: {\n summary: 'Bulk save time entries',\n description:\n 'Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 \u2014 tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets \u2014 by id, or by landing on its (project, date) cell \u2014 an entry frozen in a closed report.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryBulkSaveSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Bulk save completed',\n schema: z.object({\n ok: z.literal(true),\n created: z.number(),\n updated: z.number(),\n deleted: z.number(),\n }),\n },\n {\n status: 422,\n description: 'Validation error',\n schema: z.object({\n ok: z.literal(false),\n errors: z.array(z.object({ path: z.string(), message: z.string() })),\n }),\n },\n {\n status: 409,\n description: 'One or more rows target a time entry locked in a closed report; nothing was saved',\n schema: z.object({\n code: z.literal('time_entry_locked'),\n error: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n lockedEntryIds: z.array(z.string().uuid()),\n lockedReportIds: z.array(z.string().uuid()),\n }),\n },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,yCAAyC;AAClD,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB,4BAA4B;AAI1D,SAAS,gBAAgB,iBAAiB,kBAAkB,qBAAqB;AACjF;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qCAAqC;AAC9C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,OAAO;AAMnC,SAAS,YAAY,OAAiD;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,iBAAiB,OAAO,QAAQ,IAAI,KAAK,KAAK;AAC3D,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,IAAI,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AACtF;AAUA,SAAS,oBAAoB,QAAsC;AACjE,SAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AACjE;AAgBO,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,cAAc,EAAE,CAAC;AAEzG,UAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,UAAM,WAAW,OAAO,YAAY,KAAK,YAAY;AACrD,UAAM,iBAAiB,kCAAkC,OAAO,IAAI,KAAK;AACzE,QAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,uCAAuC,EAAE,CAAC;AAAA,IACzH;AAEA,UAAM,eAAe,MAAM,yBAAyB;AAAA,MAClD,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO,EAAE,WAAW,QAAQ,KAAK,KAAK,UAAU,eAAe;AAAA,MAC/D,MAAM,MAAM,aAAsC,KAAK,CAAC,CAAC;AAAA,IAC3D,CAAC;AACD,QAAI,CAAC,aAAa,GAAI,QAAO,aAAa;AAC1C,UAAM,EAAE,QAAQ,IAAI;AAEpB,UAAM,SAAS,6BAA6B,UAAU,QAAQ,IAAI;AAClE,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,WAAW;AAAA,QACjD,MAAM,MAAM,KAAK,KAAK,GAAG;AAAA,QACzB,SAAS,MAAM;AAAA,MACjB,EAAE;AACF,aAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,EAAE,QAAQ,IAAI,OAAO;AAc3B,UAAM,aAAa,QAAQ,OAAO,CAAC,UAAU,MAAM,WAAW,MAAS;AACvE,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,UAAU;AAAA,QACd;AAAA,QACA;AAAA,MACF;AACA,aAAO,oBAAoB,WAAW,IAAI,OAAO,EAAE,MAAM,oBAAoB,QAAQ,EAAE,CAAC;AAAA,IAC1F;AAEA,UAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,UAAM,WAAW,EAAE,UAAU,eAAe;AAE5C,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,KAAK,KAAK,UAAU,gBAAgB,WAAW,KAAK;AAAA,MAC9D,CAAC;AAAA,MACD;AAAA,IACF;AACA,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,yCAAyC,yCAAyC,EAAE,CAAC;AAAA,IACvI;AACA,UAAM,gBAAgB,YAAY;AAOlC,UAAM,oBAAoB;AAAA,MACxB,GAAG,IAAI;AAAA,QACL,QACG,IAAI,CAAC,UAAU,MAAM,MAAM,EAC3B,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AAAA,MACzE;AAAA,IACF;AACA,UAAM,YAAY,oBAAI,IAAmD;AACzE,QAAI,kBAAkB,SAAS,GAAG;AAChC,YAAM,aAAa,MAAM,GAAG,KAAK,eAAe;AAAA,QAC9C,IAAI,EAAE,KAAK,kBAAkB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb,GAAG,EAAE,QAAQ,CAAC,MAAM,eAAe,EAAE,CAAC;AACtC,iBAAW,QAAQ,WAAY,WAAU,IAAI,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,eAAe,KAAK,cAAc,CAAC;AACxG,YAAM,iBAAiB,kBAAkB,OAAO,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;AAC1E,UAAI,eAAe,SAAS,GAAG;AAC7B,cAAM,UAAU,UAAU,wCAAwC,mCAAmC;AACrG,eAAO;AAAA,UACL,eAAe,IAAI,CAAC,QAAQ,EAAE,MAAM,oBAAoB,SAAS,OAAO,GAAG,EAAE;AAAA,QAC/E;AAAA,MACF;AAAA,IACF;AAMA,UAAM,eAAkC,CAAC;AACzC,UAAM,uBAAuC,CAAC;AAC9C,UAAM,4BAA4C,CAAC;AACnD,eAAW,SAAS,SAAS;AAC3B,YAAM,OAAO,MAAM,SAAS,UAAU,IAAI,MAAM,MAAM,KAAK,OAAO;AAClE,YAAM,gBAAgB,0BAA0B,MAAM,eAAe,IAAI;AACzE,UAAI,CAAC,eAAe;AAClB,6BAAqB,KAAK;AAAA,UACxB,MAAM;AAAA,UACN,SAAS,UAAU,2CAA2C,8BAA8B;AAAA,QAC9F,CAAC;AACD;AAAA,MACF;AACA,UAAI,CAAC,4BAA4B,MAAM,aAAa,GAAG;AACrD,kCAA0B,KAAK;AAAA,UAC7B,MAAM;AAAA,UACN,SAAS,UAAU,wCAAwC,mCAAmC;AAAA,UAC9F,OAAO,MAAM;AAAA,QACf,CAAC;AACD;AAAA,MACF;AACA,mBAAa,KAAK,EAAE,OAAO,MAAM,cAAc,CAAC;AAAA,IAClD;AACA,QAAI,qBAAqB,SAAS,KAAK,0BAA0B,SAAS,GAAG;AAC3E,aAAO,oBAAoB,CAAC,GAAG,sBAAsB,GAAG,yBAAyB,CAAC;AAAA,IACpF;AAMA,UAAM,uBAAuB,CAAC,GAAG,IAAI,IAAI,aAAa,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC,CAAC;AACtF,UAAM,eAAe,oBAAI,IAAkF;AAC3G,QAAI,qBAAqB,SAAS,GAAG;AACnC,YAAM,gBAAgB,MAAM,GAAG,KAAK,kBAAkB;AAAA,QACpD,IAAI,EAAE,KAAK,qBAAqB;AAAA,QAChC;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb,GAAG,EAAE,QAAQ,CAAC,MAAM,gBAAgB,mBAAmB,EAAE,CAAC;AAC1D,iBAAW,WAAW,eAAe;AACnC,qBAAa,IAAI,QAAQ,IAAI;AAAA,UAC3B,cAAc,QAAQ,gBAAgB;AAAA,UACtC,mBAAmB,QAAQ,qBAAqB;AAAA,QAClD,CAAC;AAAA,MACH;AACA,YAAM,aAAa,qBAAqB,OAAO,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;AAC5E,UAAI,WAAW,SAAS,GAAG;AACzB,cAAM,UAAU;AAAA,UACd;AAAA,UACA;AAAA,QACF;AACA,eAAO;AAAA,UACL,WAAW,IAAI,CAAC,QAAQ,EAAE,MAAM,2BAA2B,SAAS,OAAO,GAAG,EAAE;AAAA,QAClF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA,QAAQ,KAAK,OAAO;AAAA,MACpB,cAAc,mCAAmC;AAAA,MACjD,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB,OAAO;AAAA,IAC1B;AACA,UAAM,cAAc,MAAM,uBAAuB,WAAW,UAAU;AACtE,QAAI,CAAC,YAAY,IAAI;AACnB,aAAO,aAAa;AAAA,QAClB,YAAY,aAAa,EAAE,OAAO,6BAA6B;AAAA,QAC/D,EAAE,QAAQ,YAAY,eAAe,IAAI;AAAA,MAC3C;AAAA,IACF;AAEA,UAAM,cAAc,aACjB,IAAI,CAAC,QAAQ,IAAI,MAAM,EAAE,EACzB,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AAKvE,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,mBAAmB,MAAM,GAAG;AAAA,QAChC;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,YAAY,GAAG,UAAU,gBAAgB,eAAe,WAAW,KAAK;AAAA,QACrF,EAAE,QAAQ,CAAC,IAAI,EAAE;AAAA,MACnB;AACA,YAAM,gBAAgB,IAAI,IAAI,iBAAiB,IAAI,CAAC,UAAU,MAAM,EAAE,CAAC;AACvE,YAAM,aAAa,YAAY,OAAO,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;AACpE,UAAI,WAAW,SAAS,GAAG;AACzB,eAAO,aAAa;AAAA,UAClB;AAAA,YACE,IAAI;AAAA,YACJ,QAAQ,WAAW,IAAI,CAAC,QAAQ;AAAA,cAC9B,MAAM;AAAA,cACN,SAAS;AAAA,gBACP;AAAA,gBACA;AAAA,cACF;AAAA,cACA,OAAO;AAAA,YACT,EAAE;AAAA,UACJ;AAAA,UACA,EAAE,QAAQ,IAAI;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAwBA,UAAM,aAAa,aAAa,OAAO,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE;AAC7D,UAAM,cAAc,WAAW,IAAI,CAAC,QAAQ,GAAG,YAAY,IAAI,MAAM,IAAI,CAAC,IAAI,IAAI,aAAa,EAAE;AACjG,UAAM,iBAA4C,CAAC;AACnD,QAAI,YAAY,SAAS,EAAG,gBAAe,KAAK,EAAE,IAAI,EAAE,KAAK,YAAY,EAAE,CAAC;AAC5E,QAAI,YAAY,SAAS,GAAG;AAC1B,qBAAe,KAAK;AAAA,QAClB,MAAM,EAAE,KAAK,WAAW,IAAI,CAAC,QAAQ,IAAI,MAAM,IAAI,EAAE;AAAA,QACrD,eAAe,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI,WAAW,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC,CAAC,EAAE;AAAA,MACjF,CAAC;AAAA,IACH;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,gBAAgB,IAAI,IAAI,WAAW;AACzC,YAAM,gBAAgB,IAAI,IAAI,WAAW;AACzC,YAAM,mBAAmB,MAAM,GAAG;AAAA,QAChC;AAAA,QACA;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW;AAAA,UACX,gBAAgB,EAAE,KAAK,KAAK;AAAA,UAC5B,KAAK;AAAA,QACP;AAAA,QACA,EAAE,QAAQ,CAAC,MAAM,kBAAkB,QAAQ,eAAe,EAAE;AAAA,MAC9D;AAGA,YAAM,UAAgC,iBAAiB;AAAA,QACrD,CAAC,QACC,cAAc,IAAI,IAAI,EAAE,KACxB,cAAc,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC,IAAI,IAAI,iBAAiB,EAAE,EAAE;AAAA,MAC3E;AACA,UAAI,QAAQ,SAAS,GAAG;AACtB,cAAM;AAAA,UACJ,QAAQ,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,IAAI,gBAAgB,IAAI,eAAe,EAAE;AAAA,UACzE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAYA,UAAM,WAAW,MAAM,yBAAyB,WAAW,QAAQ;AAEnE,UAAM,EAAE,QAAQ,eAAe,IAAI,MAAM,GAAG,cAAc,OAAO,QAAQ;AACvE,UAAI,UAAU;AACd,UAAI,UAAU;AACd,UAAI,UAAU;AACd,YAAM,UAA2B,CAAC;AAElC,YAAM,kBAAkB,YAAY,SAAS,IACzC,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,YAAY,GAAG,UAAU,gBAAgB,eAAe,WAAW,KAAK;AAAA,QACrF,CAAC;AAAA,QACD;AAAA,MACF,IACA,CAAC;AAEL,YAAM,cAAc,IAAI,IAAI,gBAAgB,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AAE7E,iBAAW,EAAE,OAAO,MAAM,cAAc,KAAK,cAAc;AACzD,cAAM,UAAU,aAAa,IAAI,aAAa,KAAK;AAInD,cAAM,QAAQ,2BAA2B,KAAK;AAC9C,YAAI,MAAM,MAAM,YAAY,IAAI,MAAM,EAAE,GAAG;AACzC,gBAAM,WAAW,YAAY,IAAI,MAAM,EAAE;AACzC,cAAI,MAAM,oBAAoB,GAAG;AAC/B,qBAAS,YAAY,oBAAI,KAAK;AAC9B;AACA,oBAAQ,KAAK,EAAE,QAAQ,WAAW,QAAQ,SAAS,CAAC;AAAA,UACtD,OAAO;AAUL,kBAAM,WAAW,2BAA2B;AAAA,cAC1C,QAAQ,8BAA8B;AAAA,gBACpC,QAAQ;AAAA,kBACN,WAAW,SAAS,aAAa;AAAA,kBACjC,SAAS,SAAS,WAAW;AAAA,kBAC7B,iBAAiB,SAAS;AAAA,gBAC5B;AAAA,gBACA,UAAU,SAAS;AAAA,gBACnB,QAAQ,MAAM;AAAA,cAChB,CAAC;AAAA,cACD,WAAW;AAAA,cACX,SAAS;AAAA,cACT,iBAAiB,MAAM;AAAA,YACzB,CAAC;AACD,kBAAM,iBAAiB,mBAAmB,SAAS,iBAAiB;AACpE,qBAAS,OAAO,MAAM;AACtB,qBAAS,gBAAgB;AAKzB,gBAAI,eAAgB,UAAS,mBAAmB,SAAS,gBAAgB;AACzE,qBAAS,YAAY,SAAS;AAC9B,qBAAS,UAAU,SAAS;AAC5B,qBAAS,kBAAkB,SAAS;AAGpC,qBAAS,iBAAiB,kBAAkB,SAAS,iBAAiB,UAAU;AAAA,cAC9E;AAAA,cACA;AAAA,YACF,CAAC;AACD,gBAAI,MAAM,WAAW,OAAW,UAAS,SAAS,MAAM,MAAM;AAC9D,gBAAI,MAAM,eAAe,OAAW,UAAS,aAAa,MAAM;AAChE,gBAAI,MAAM,uBAAuB,QAAW;AAC1C,uBAAS,qBAAqB,8BAA8B,MAAM,kBAAkB;AAAA,YACtF;AACA,gBAAI,UAAU,OAAW,UAAS,QAAQ;AAC1C,qBAAS,YAAY,oBAAI,KAAK;AAC9B;AACA,oBAAQ,KAAK,EAAE,QAAQ,WAAW,QAAQ,SAAS,CAAC;AAAA,UACtD;AAAA,QACF,OAAO;AACL,gBAAM,MAAM,oBAAI,KAAK;AACrB,gBAAM,WAAW,IAAI,OAAO,gBAAgB;AAAA,YAC1C;AAAA,YACA;AAAA,YACA;AAAA,YACA,MAAM,MAAM;AAAA,YACZ;AAAA,YACA,QAAQ,MAAM,MAAM;AAAA,YACpB,iBAAiB,MAAM;AAAA,YACvB,gBAAgB,kBAAkB,MAAM,iBAAiB,UAAU;AAAA,cACjE;AAAA,cACA;AAAA,YACF,CAAC;AAAA,YACD,OAAO,SAAS;AAAA,YAChB,YAAY,yBAAyB;AAAA,cACnC,WAAW,MAAM;AAAA,cACjB;AAAA,cACA;AAAA,cACA,OAAO,EAAE,UAAU,eAAe;AAAA,YACpC,CAAC;AAAA,YACD,oBAAoB,8BAA8B,MAAM,kBAAkB;AAAA,YAC1E,kBAAkB,SAAS,gBAAgB;AAAA,YAC3C,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,WAAW;AAAA,UACb,CAAC;AACD;AACA,kBAAQ,KAAK,EAAE,QAAQ,WAAW,QAAQ,SAAS,CAAC;AAAA,QACtD;AAAA,MACF;AAEA,YAAM,IAAI,MAAM;AAChB,aAAO,EAAE,QAAQ,EAAE,SAAS,SAAS,QAAQ,GAAG,gBAAgB,QAAQ;AAAA,IAC1E,CAAC;AAED,UAAM,aAAa,UAAU,QAAoB,YAAY;AAC7D,eAAW,UAAU,gBAAgB;AACnC,YAAM,oBAAoB;AAAA,QACxB;AAAA,QACA,QAAQ,OAAO;AAAA,QACf,QAAQ,OAAO;AAAA,QACf,aAAa;AAAA,UACX,IAAI,OAAO,OAAO;AAAA,UAClB,gBAAgB,OAAO,OAAO;AAAA,UAC9B,UAAU,OAAO,OAAO;AAAA,QAC1B;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM,qBAAqB,UAAU;AAErC,UAAM,uBAAuB,oBAAI,IAAY;AAC7C,eAAW,UAAU,gBAAgB;AACnC,UAAI,qBAAqB,IAAI,OAAO,OAAO,EAAE,EAAG;AAChD,2BAAqB,IAAI,OAAO,OAAO,EAAE;AACzC,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,UACE,IAAI,OAAO,OAAO;AAAA,UAClB,gBAAgB,OAAO,OAAO;AAAA,UAC9B,UAAU,OAAO,OAAO;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,QAAQ,OAAO,MAAM;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,YAAY,sBAAsB,QAAQ;AAC5C,YAAM,kCAAkC,YAAY,uBAAuB;AAAA,QACzE;AAAA,QACA;AAAA,QACA,QAAQ,KAAK,OAAO;AAAA,QACpB,cAAc,mCAAmC;AAAA,QACjD,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,MACtB,CAAC;AAAA,IACH;AAKA,SAAK,eAAe,4CAA4C;AAAA,MAC9D;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH,UAAU,eAAe,IAAI,CAAC,WAAW,OAAO,OAAO,EAAE;AAAA,IAC3D,GAAG,EAAE,YAAY,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ;AACtC,aAAO,MAAM,wDAAwD,EAAE,IAAI,CAAC;AAAA,IAC9E,CAAC;AAED,WAAO,QAAQ,QAAQ,KAAK,EAAE,IAAI,MAAM,GAAG,OAAO,CAAC;AAAA,EACrD,SAAS,KAAK;AACZ,QAAI,eAAe,eAAe;AAChC,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,MAAM,6CAA6C,EAAE,IAAI,CAAC;AACjE,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,UAAU,oCAAoC,mCAAmC,EAAE;AAAA,MAC5F,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aACE;AAAA,MACF,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,YACf,IAAI,EAAE,QAAQ,IAAI;AAAA,YAClB,SAAS,EAAE,OAAO;AAAA,YAClB,SAAS,EAAE,OAAO;AAAA,YAClB,SAAS,EAAE,OAAO;AAAA,UACpB,CAAC;AAAA,QACH;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,YACf,IAAI,EAAE,QAAQ,KAAK;AAAA,YACnB,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;AAAA,UACrE,CAAC;AAAA,QACH;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,YACf,MAAM,EAAE,QAAQ,mBAAmB;AAAA,YACnC,OAAO,EAAE,OAAO;AAAA,YAChB,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,YAC3C,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,YACzC,iBAAiB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,UAC5C,CAAC;AAAA,QACH;AAAA,QACA,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,QACpF,EAAE,QAAQ,KAAK,aAAa,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,MACnF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import { z } from "zod";
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
6
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
+ import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
7
8
  import { CrudHttpError, forbidden, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
8
9
  import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
9
10
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
@@ -124,7 +125,7 @@ async function POST(req) {
124
125
  }
125
126
  const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
126
127
  const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
127
- const organizationId = scope?.selectedId ?? auth.orgId ?? null;
128
+ const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null;
128
129
  if (!tenantId || !organizationId) {
129
130
  throw new CrudHttpError(400, {
130
131
  error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
@@ -243,7 +244,7 @@ async function POST(req) {
243
244
  container,
244
245
  auth,
245
246
  organizationScope: scope,
246
- selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,
247
+ selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,
247
248
  organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
248
249
  request: req
249
250
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/copy-day/route.ts"],
4
- "sourcesContent": ["/**\n * US-D6 \u2014 \"Kopiuj wczorajszy dzie\u0144\" (screen 1, note 5).\n *\n * The note is the design constraint: the button \"duplicates the previous working\n * day's entries **as versions to correct**\". So this endpoint is not a silent\n * commit \u2014 it answers with the entries it created, in full, so the caller can put\n * them straight in front of the user for review and adjustment rather than\n * reporting a count and leaving them to hunt for what landed.\n *\n * What \"the previous working day\" means is the CALLER's business. Weekends,\n * holidays and four-day weeks are calendar questions this module has no data for,\n * and a server-side weekend rule would be wrong for every tenant that does not\n * work Monday-to-Friday. The endpoint therefore takes two explicit days.\n *\n * Three decisions worth their reasoning:\n *\n * 1. **A locked SOURCE is skipped and reported, not fatal.** The lock protects\n * the numbers a closed report has already billed (D-7), and a copy never\n * writes to its source \u2014 the target day is a fresh, unlocked draft. This is\n * what makes it different from the grid bulk save, where the rows being\n * WRITTEN are the locked ones and the whole batch must fail. Here the caller\n * did not hand-pick rows: they asked for a day. Failing the whole day because\n * one of its entries sits in a closed report would break the button for\n * exactly the people who report most regularly. Every skipped source comes\n * back in `skipped[]` carrying the shared `time_entry_locked` code and the\n * report that froze it, so the UI can say what was left behind.\n * 2. **Idempotency: a non-empty target day is refused.** A day of entries has no\n * natural key to deduplicate on \u2014 two identical 30-minute stand-ups on one day\n * are legitimate data \u2014 so a second call cannot tell a re-run from a genuine\n * repeat, and would silently double the day into something the user cannot\n * untangle from the grid. So the default is to refuse with `409\n * copy_day_target_not_empty`, naming what is already there. `allowDuplicates:\n * true` is the deliberate opt-in for the real case the refusal would\n * otherwise block: the user already logged something today and still wants\n * yesterday's set on top. Refusing is also what makes a partially completed\n * run visible instead of compounding it.\n * 3. **The copy itself is the existing duplicate command, once per source.** It\n * already resolves the source, runs the lock gate and delegates the write to\n * the create command, so every copy inherits ownership, project access, the\n * billable and currency defaults, rounding (D-7), tags and audit unchanged.\n * The loop is NOT wrapped in a route-level transaction: the command bus forks\n * its own EntityManager per command, so an outer `em.transactional` would\n * enclose nothing and the atomicity would be a fiction. Each copy is atomic in\n * itself; a copy that fails is reported in `skipped[]` and the rest still\n * land, which is the behaviour a day of drafts wants.\n */\n\nimport { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../events'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeEntryTag } from '../../../../data/entities'\nimport { staffTimeEntryCommandIds, TIME_ENTRY_LOCKED_CODE } from '../../../../commands/timesheets-entries'\nimport { resolveProjectAccess, type ProjectAccess } from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/copy-day' })\n\nconst MANAGE_OWN_FEATURE = 'staff.timesheets.manage_own'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry\n\n/** The 409 a second \"copy yesterday\" click gets, rather than a doubled day. */\nexport const COPY_DAY_TARGET_NOT_EMPTY_CODE = 'copy_day_target_not_empty'\n\n/** Reason codes on `skipped[]`. The locked one is the shared code, not a second spelling of it. */\nconst COPY_FAILED_REASON = 'copy_failed'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] },\n}\n\nconst copyDayRequestSchema = z.object({\n fromDate: z.coerce.date(),\n toDate: z.coerce.date(),\n /** Only a `staff.timesheets.manage_all` holder may copy somebody else's day. */\n staffMemberId: z.string().uuid().optional(),\n allowDuplicates: z.boolean().optional().default(false),\n})\n\nconst copiedEntrySchema = z.object({\n id: z.string().uuid(),\n sourceId: z.string().uuid(),\n date: z.string(),\n durationMinutes: z.number(),\n roundedMinutes: z.number().nullable(),\n timeProjectId: z.string().uuid().nullable(),\n taskId: z.string().uuid().nullable(),\n isBillable: z.boolean(),\n description: z.string().nullable(),\n tagIds: z.array(z.string().uuid()),\n})\n\nconst skippedEntrySchema = z.object({\n id: z.string().uuid(),\n reason: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n error: z.string().nullable(),\n})\n\nconst copyDayResponseSchema = z.object({\n ok: z.literal(true),\n fromDate: z.string(),\n toDate: z.string(),\n staffMemberId: z.string().uuid(),\n copied: z.number(),\n created: z.array(copiedEntrySchema),\n skipped: z.array(skippedEntrySchema),\n})\n\nconst targetNotEmptySchema = z.object({\n code: z.literal(COPY_DAY_TARGET_NOT_EMPTY_CODE),\n error: z.string(),\n toDate: z.string(),\n existingEntryCount: z.number(),\n existingEntryIds: z.array(z.string().uuid()),\n})\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\ntype SkippedSource = {\n id: string\n reason: string\n lockedReportId: string | null\n error: string | null\n}\n\n/**\n * A day is addressed by its calendar day, never by a timestamp (D-8), so both the\n * request echo and the copied rows collapse to `YYYY-MM-DD`.\n */\nfunction dayKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string,\n organizationId: string,\n grantedFeatures: readonly string[],\n): Promise<ProjectAccess> {\n const em = container.resolve('em') as EntityManager\n return resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n}\n\n/**\n * The per-row form of the list route's `$or` intersection: an entry on a project\n * is visible to the members of that project, and a project-less entry is visible\n * only to the member who logged it. A manager copying somebody else's day still\n * only copies the part of it their own project access covers.\n */\nfunction isTimeEntryVisible(\n entry: { staffMemberId: string; timeProjectId?: string | null },\n access: ProjectAccess,\n): boolean {\n if (access.canManageAll) return true\n const projectId = entry.timeProjectId ?? null\n if (projectId) return access.projectIds.includes(projectId)\n return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId\n}\n\nasync function loadTagIdsByEntry(\n em: EntityManager,\n entryIds: string[],\n tenantId: string,\n organizationId: string,\n): Promise<Map<string, string[]>> {\n const byEntryId = new Map<string, string[]>()\n if (entryIds.length === 0) return byEntryId\n const assignments = await em.find(StaffTimeEntryTag, {\n timeEntryId: { $in: entryIds },\n tenantId,\n organizationId,\n })\n for (const assignment of assignments) {\n const list = byEntryId.get(assignment.timeEntryId) ?? []\n list.push(assignment.tagId)\n byEntryId.set(assignment.timeEntryId, list)\n }\n return byEntryId\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // Both decisions come from the module's single RBAC authority, which asks the\n // service and fails closed on every path. The nullable grant array they were\n // matched against could not say whether an empty answer meant \"no grants\" or\n // \"could not ask\", and `grantedFeatures ? \u2026 : false` silently answered the\n // second as the first.\n const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId })\n if (!manageOwn.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = manageOwn.grantedFeatures\n const hasManageAll = (\n await resolveFeatureAccess(container, actorId, [MANAGE_ALL_FEATURE], { tenantId, organizationId })\n ).allowed\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = copyDayRequestSchema.parse(session.body)\n const fromDay = dayKey(parsed.fromDate)\n const toDay = dayKey(parsed.toDate)\n if (fromDay === toDay) {\n // Copying a day onto itself is always a mistake, never intent, and\n // `allowDuplicates` would turn it into a silent doubling.\n throw new CrudHttpError(400, {\n error: translate(\n 'staff.timesheets.errors.copyDaySameDate',\n 'The source and target day must be different.',\n ),\n })\n }\n\n const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures)\n // Ownership: the copies are attributed by the create command, which silently\n // files them under the caller unless they hold `staff.timesheets.manage_all`.\n // Refusing here rather than letting that happen keeps a projects-manager from\n // asking for a colleague's day and quietly receiving it under their own name.\n if (parsed.staffMemberId && parsed.staffMemberId !== access.staffMemberId && !hasManageAll) {\n throw forbidden(\n translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n )\n }\n const staffMemberId = parsed.staffMemberId ?? access.staffMemberId\n if (!staffMemberId) {\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const sources = (\n await findWithDecryption(\n em,\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.fromDate, deletedAt: null },\n {},\n scopeCtx,\n )\n ).filter((entry) => isTimeEntryVisible(entry, access))\n\n if (sources.length === 0) {\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: 0,\n created: [],\n skipped: [],\n })\n }\n\n if (!parsed.allowDuplicates) {\n const existing = await em.find(\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.toDate, deletedAt: null },\n { fields: ['id'] },\n )\n if (existing.length > 0) {\n throw new CrudHttpError(409, {\n code: COPY_DAY_TARGET_NOT_EMPTY_CODE,\n error: translate(\n 'staff.timesheets.errors.copyDayTargetNotEmpty',\n 'The target day already has time entries. Review them first, or confirm to add these copies anyway.',\n ),\n toDate: toDay,\n existingEntryCount: existing.length,\n existingEntryIds: existing.map((entry) => entry.id),\n })\n }\n }\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: staffMemberId,\n operation: 'create',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const skipped: SkippedSource[] = []\n const copyable: StaffTimeEntry[] = []\n for (const entry of sources) {\n if (entry.lockedReportId) {\n skipped.push({\n id: entry.id,\n reason: TIME_ENTRY_LOCKED_CODE,\n lockedReportId: entry.lockedReportId,\n error: null,\n })\n continue\n }\n copyable.push(entry)\n }\n\n // Tags are read here rather than inside the command: the command writes tags\n // only through the tag commands and never touches `staff_time_entry_tags`, so\n // \u2014 exactly as with the single-entry duplicate, where the client supplies the\n // `tags[]` it already holds \u2014 the caller of the command supplies them.\n const tagIdsByEntry = await loadTagIdsByEntry(em, copyable.map((entry) => entry.id), tenantId, organizationId)\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const copiedIdBySource = new Map<string, string>()\n for (const entry of copyable) {\n try {\n const { result } = await commandBus.execute<unknown, { timeEntryId: string }>(\n staffTimeEntryCommandIds.duplicate,\n {\n input: {\n id: entry.id,\n date: parsed.toDate,\n tagIds: tagIdsByEntry.get(entry.id) ?? [],\n },\n ctx,\n },\n )\n if (result?.timeEntryId) copiedIdBySource.set(entry.id, result.timeEntryId)\n } catch (err) {\n // A source can stop being copyable between the read and the write \u2014 its\n // project soft-deleted, say. The day is a set of drafts, so one source\n // that cannot be copied is reported beside the locked ones instead of\n // discarding the copies that already landed.\n //\n // A command interceptor that refuses one source with an explicit status is\n // that same per-source refusal, so it is reported here rather than in the\n // route-level catch: this is the clause the rejection actually reaches, and\n // aborting the day from it would strand the copies already committed. The\n // batch contract carries no per-item status, so the interceptor's message\n // travels on the skipped row instead of degrading to `null`.\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n const skippedError = interceptorRejection\n ? String(interceptorRejection.body.error ?? '') || null\n : isCrudHttpError(err)\n ? String((err.body as { error?: unknown })?.error ?? '') || null\n : null\n logger.warn('staff.timesheets.time-entries.copy-day source copy failed', { err, sourceId: entry.id })\n skipped.push({\n id: entry.id,\n reason: COPY_FAILED_REASON,\n lockedReportId: null,\n error: skippedError,\n })\n }\n }\n\n await guardResult.runAfterSuccess()\n\n for (const [sourceId, createdId] of copiedIdBySource) {\n void emitStaffEvent('staff.timesheets.time_entry.copied', {\n id: createdId,\n sourceId,\n tenantId,\n organizationId,\n staffMemberId,\n date: toDay,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.copied failed', { err, sourceId })\n })\n }\n\n // Screen 1 note 5: these are drafts to correct, so the response carries the\n // rows themselves rather than a count the UI would have to go and re-fetch.\n const createdIds = Array.from(copiedIdBySource.values())\n const createdRows = createdIds.length\n ? await findWithDecryption(\n em.fork(),\n StaffTimeEntry,\n { id: { $in: createdIds }, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n const createdRowById = new Map(createdRows.map((row) => [row.id, row]))\n\n const created = Array.from(copiedIdBySource.entries()).flatMap(([sourceId, createdId]) => {\n const row = createdRowById.get(createdId)\n if (!row) return []\n return [\n {\n id: row.id,\n sourceId,\n date: dayKey(row.date),\n durationMinutes: row.durationMinutes,\n roundedMinutes: row.roundedMinutes ?? null,\n timeProjectId: row.timeProjectId ?? null,\n taskId: row.taskId ?? null,\n isBillable: row.isBillable,\n description: row.notes ?? null,\n tagIds: tagIdsByEntry.get(sourceId) ?? [],\n },\n ]\n })\n\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: created.length,\n created,\n skipped,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.copy-day failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Copy a day of time entries',\n methods: {\n POST: {\n summary: 'Copy a day of time entries',\n description:\n 'US-D6, screen 1 note 5. Copies every unlocked time entry the caller owns on `fromDate` onto `toDate` as editable drafts, and returns the created rows so they can be reviewed and corrected. Which day is \"the previous working day\" is the caller decision \u2014 this endpoint takes explicit dates and applies no weekend or holiday rule. A source entry locked into a closed report is skipped and reported in `skipped[]` with the shared `time_entry_locked` code (nothing is written to it, so the batch is not failed). The request is refused with 409 `copy_day_target_not_empty` when the target day already holds entries, so a repeated call cannot silently double a day; pass `allowDuplicates: true` to add the copies anyway. `staffMemberId` targets another member and requires `staff.timesheets.manage_all`. Every copy is written through the duplicate command and therefore through the create command, so ownership, project access, the billable and currency defaults and the tenant rounding rule apply to it exactly as they do to a hand-made entry.',\n requestBody: {\n contentType: 'application/json',\n schema: copyDayRequestSchema,\n },\n responses: [\n { status: 200, description: 'Copies created, with the skipped sources reported', schema: copyDayResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, missing scope, or fromDate equal to toDate' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.manage_own, no linked staff member, or another member day without staff.timesheets.manage_all' },\n {\n status: 409,\n description: 'The target day already has entries; nothing was copied',\n schema: targetNotEmptySchema,\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * US-D6 \u2014 \"Kopiuj wczorajszy dzie\u0144\" (screen 1, note 5).\n *\n * The note is the design constraint: the button \"duplicates the previous working\n * day's entries **as versions to correct**\". So this endpoint is not a silent\n * commit \u2014 it answers with the entries it created, in full, so the caller can put\n * them straight in front of the user for review and adjustment rather than\n * reporting a count and leaving them to hunt for what landed.\n *\n * What \"the previous working day\" means is the CALLER's business. Weekends,\n * holidays and four-day weeks are calendar questions this module has no data for,\n * and a server-side weekend rule would be wrong for every tenant that does not\n * work Monday-to-Friday. The endpoint therefore takes two explicit days.\n *\n * Three decisions worth their reasoning:\n *\n * 1. **A locked SOURCE is skipped and reported, not fatal.** The lock protects\n * the numbers a closed report has already billed (D-7), and a copy never\n * writes to its source \u2014 the target day is a fresh, unlocked draft. This is\n * what makes it different from the grid bulk save, where the rows being\n * WRITTEN are the locked ones and the whole batch must fail. Here the caller\n * did not hand-pick rows: they asked for a day. Failing the whole day because\n * one of its entries sits in a closed report would break the button for\n * exactly the people who report most regularly. Every skipped source comes\n * back in `skipped[]` carrying the shared `time_entry_locked` code and the\n * report that froze it, so the UI can say what was left behind.\n * 2. **Idempotency: a non-empty target day is refused.** A day of entries has no\n * natural key to deduplicate on \u2014 two identical 30-minute stand-ups on one day\n * are legitimate data \u2014 so a second call cannot tell a re-run from a genuine\n * repeat, and would silently double the day into something the user cannot\n * untangle from the grid. So the default is to refuse with `409\n * copy_day_target_not_empty`, naming what is already there. `allowDuplicates:\n * true` is the deliberate opt-in for the real case the refusal would\n * otherwise block: the user already logged something today and still wants\n * yesterday's set on top. Refusing is also what makes a partially completed\n * run visible instead of compounding it.\n * 3. **The copy itself is the existing duplicate command, once per source.** It\n * already resolves the source, runs the lock gate and delegates the write to\n * the create command, so every copy inherits ownership, project access, the\n * billable and currency defaults, rounding (D-7), tags and audit unchanged.\n * The loop is NOT wrapped in a route-level transaction: the command bus forks\n * its own EntityManager per command, so an outer `em.transactional` would\n * enclose nothing and the atomicity would be a fiction. Each copy is atomic in\n * itself; a copy that fails is reported in `skipped[]` and the rest still\n * land, which is the behaviour a day of drafts wants.\n */\n\nimport { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../events'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeEntryTag } from '../../../../data/entities'\nimport { staffTimeEntryCommandIds, TIME_ENTRY_LOCKED_CODE } from '../../../../commands/timesheets-entries'\nimport { resolveProjectAccess, type ProjectAccess } from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/copy-day' })\n\nconst MANAGE_OWN_FEATURE = 'staff.timesheets.manage_own'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry\n\n/** The 409 a second \"copy yesterday\" click gets, rather than a doubled day. */\nexport const COPY_DAY_TARGET_NOT_EMPTY_CODE = 'copy_day_target_not_empty'\n\n/** Reason codes on `skipped[]`. The locked one is the shared code, not a second spelling of it. */\nconst COPY_FAILED_REASON = 'copy_failed'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] },\n}\n\nconst copyDayRequestSchema = z.object({\n fromDate: z.coerce.date(),\n toDate: z.coerce.date(),\n /** Only a `staff.timesheets.manage_all` holder may copy somebody else's day. */\n staffMemberId: z.string().uuid().optional(),\n allowDuplicates: z.boolean().optional().default(false),\n})\n\nconst copiedEntrySchema = z.object({\n id: z.string().uuid(),\n sourceId: z.string().uuid(),\n date: z.string(),\n durationMinutes: z.number(),\n roundedMinutes: z.number().nullable(),\n timeProjectId: z.string().uuid().nullable(),\n taskId: z.string().uuid().nullable(),\n isBillable: z.boolean(),\n description: z.string().nullable(),\n tagIds: z.array(z.string().uuid()),\n})\n\nconst skippedEntrySchema = z.object({\n id: z.string().uuid(),\n reason: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n error: z.string().nullable(),\n})\n\nconst copyDayResponseSchema = z.object({\n ok: z.literal(true),\n fromDate: z.string(),\n toDate: z.string(),\n staffMemberId: z.string().uuid(),\n copied: z.number(),\n created: z.array(copiedEntrySchema),\n skipped: z.array(skippedEntrySchema),\n})\n\nconst targetNotEmptySchema = z.object({\n code: z.literal(COPY_DAY_TARGET_NOT_EMPTY_CODE),\n error: z.string(),\n toDate: z.string(),\n existingEntryCount: z.number(),\n existingEntryIds: z.array(z.string().uuid()),\n})\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\ntype SkippedSource = {\n id: string\n reason: string\n lockedReportId: string | null\n error: string | null\n}\n\n/**\n * A day is addressed by its calendar day, never by a timestamp (D-8), so both the\n * request echo and the copied rows collapse to `YYYY-MM-DD`.\n */\nfunction dayKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string,\n organizationId: string,\n grantedFeatures: readonly string[],\n): Promise<ProjectAccess> {\n const em = container.resolve('em') as EntityManager\n return resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n}\n\n/**\n * The per-row form of the list route's `$or` intersection: an entry on a project\n * is visible to the members of that project, and a project-less entry is visible\n * only to the member who logged it. A manager copying somebody else's day still\n * only copies the part of it their own project access covers.\n */\nfunction isTimeEntryVisible(\n entry: { staffMemberId: string; timeProjectId?: string | null },\n access: ProjectAccess,\n): boolean {\n if (access.canManageAll) return true\n const projectId = entry.timeProjectId ?? null\n if (projectId) return access.projectIds.includes(projectId)\n return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId\n}\n\nasync function loadTagIdsByEntry(\n em: EntityManager,\n entryIds: string[],\n tenantId: string,\n organizationId: string,\n): Promise<Map<string, string[]>> {\n const byEntryId = new Map<string, string[]>()\n if (entryIds.length === 0) return byEntryId\n const assignments = await em.find(StaffTimeEntryTag, {\n timeEntryId: { $in: entryIds },\n tenantId,\n organizationId,\n })\n for (const assignment of assignments) {\n const list = byEntryId.get(assignment.timeEntryId) ?? []\n list.push(assignment.tagId)\n byEntryId.set(assignment.timeEntryId, list)\n }\n return byEntryId\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // Both decisions come from the module's single RBAC authority, which asks the\n // service and fails closed on every path. The nullable grant array they were\n // matched against could not say whether an empty answer meant \"no grants\" or\n // \"could not ask\", and `grantedFeatures ? \u2026 : false` silently answered the\n // second as the first.\n const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId })\n if (!manageOwn.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = manageOwn.grantedFeatures\n const hasManageAll = (\n await resolveFeatureAccess(container, actorId, [MANAGE_ALL_FEATURE], { tenantId, organizationId })\n ).allowed\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = copyDayRequestSchema.parse(session.body)\n const fromDay = dayKey(parsed.fromDate)\n const toDay = dayKey(parsed.toDate)\n if (fromDay === toDay) {\n // Copying a day onto itself is always a mistake, never intent, and\n // `allowDuplicates` would turn it into a silent doubling.\n throw new CrudHttpError(400, {\n error: translate(\n 'staff.timesheets.errors.copyDaySameDate',\n 'The source and target day must be different.',\n ),\n })\n }\n\n const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures)\n // Ownership: the copies are attributed by the create command, which silently\n // files them under the caller unless they hold `staff.timesheets.manage_all`.\n // Refusing here rather than letting that happen keeps a projects-manager from\n // asking for a colleague's day and quietly receiving it under their own name.\n if (parsed.staffMemberId && parsed.staffMemberId !== access.staffMemberId && !hasManageAll) {\n throw forbidden(\n translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n )\n }\n const staffMemberId = parsed.staffMemberId ?? access.staffMemberId\n if (!staffMemberId) {\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const sources = (\n await findWithDecryption(\n em,\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.fromDate, deletedAt: null },\n {},\n scopeCtx,\n )\n ).filter((entry) => isTimeEntryVisible(entry, access))\n\n if (sources.length === 0) {\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: 0,\n created: [],\n skipped: [],\n })\n }\n\n if (!parsed.allowDuplicates) {\n const existing = await em.find(\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.toDate, deletedAt: null },\n { fields: ['id'] },\n )\n if (existing.length > 0) {\n throw new CrudHttpError(409, {\n code: COPY_DAY_TARGET_NOT_EMPTY_CODE,\n error: translate(\n 'staff.timesheets.errors.copyDayTargetNotEmpty',\n 'The target day already has time entries. Review them first, or confirm to add these copies anyway.',\n ),\n toDate: toDay,\n existingEntryCount: existing.length,\n existingEntryIds: existing.map((entry) => entry.id),\n })\n }\n }\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: staffMemberId,\n operation: 'create',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const skipped: SkippedSource[] = []\n const copyable: StaffTimeEntry[] = []\n for (const entry of sources) {\n if (entry.lockedReportId) {\n skipped.push({\n id: entry.id,\n reason: TIME_ENTRY_LOCKED_CODE,\n lockedReportId: entry.lockedReportId,\n error: null,\n })\n continue\n }\n copyable.push(entry)\n }\n\n // Tags are read here rather than inside the command: the command writes tags\n // only through the tag commands and never touches `staff_time_entry_tags`, so\n // \u2014 exactly as with the single-entry duplicate, where the client supplies the\n // `tags[]` it already holds \u2014 the caller of the command supplies them.\n const tagIdsByEntry = await loadTagIdsByEntry(em, copyable.map((entry) => entry.id), tenantId, organizationId)\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const copiedIdBySource = new Map<string, string>()\n for (const entry of copyable) {\n try {\n const { result } = await commandBus.execute<unknown, { timeEntryId: string }>(\n staffTimeEntryCommandIds.duplicate,\n {\n input: {\n id: entry.id,\n date: parsed.toDate,\n tagIds: tagIdsByEntry.get(entry.id) ?? [],\n },\n ctx,\n },\n )\n if (result?.timeEntryId) copiedIdBySource.set(entry.id, result.timeEntryId)\n } catch (err) {\n // A source can stop being copyable between the read and the write \u2014 its\n // project soft-deleted, say. The day is a set of drafts, so one source\n // that cannot be copied is reported beside the locked ones instead of\n // discarding the copies that already landed.\n //\n // A command interceptor that refuses one source with an explicit status is\n // that same per-source refusal, so it is reported here rather than in the\n // route-level catch: this is the clause the rejection actually reaches, and\n // aborting the day from it would strand the copies already committed. The\n // batch contract carries no per-item status, so the interceptor's message\n // travels on the skipped row instead of degrading to `null`.\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n const skippedError = interceptorRejection\n ? String(interceptorRejection.body.error ?? '') || null\n : isCrudHttpError(err)\n ? String((err.body as { error?: unknown })?.error ?? '') || null\n : null\n logger.warn('staff.timesheets.time-entries.copy-day source copy failed', { err, sourceId: entry.id })\n skipped.push({\n id: entry.id,\n reason: COPY_FAILED_REASON,\n lockedReportId: null,\n error: skippedError,\n })\n }\n }\n\n await guardResult.runAfterSuccess()\n\n for (const [sourceId, createdId] of copiedIdBySource) {\n void emitStaffEvent('staff.timesheets.time_entry.copied', {\n id: createdId,\n sourceId,\n tenantId,\n organizationId,\n staffMemberId,\n date: toDay,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.copied failed', { err, sourceId })\n })\n }\n\n // Screen 1 note 5: these are drafts to correct, so the response carries the\n // rows themselves rather than a count the UI would have to go and re-fetch.\n const createdIds = Array.from(copiedIdBySource.values())\n const createdRows = createdIds.length\n ? await findWithDecryption(\n em.fork(),\n StaffTimeEntry,\n { id: { $in: createdIds }, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n const createdRowById = new Map(createdRows.map((row) => [row.id, row]))\n\n const created = Array.from(copiedIdBySource.entries()).flatMap(([sourceId, createdId]) => {\n const row = createdRowById.get(createdId)\n if (!row) return []\n return [\n {\n id: row.id,\n sourceId,\n date: dayKey(row.date),\n durationMinutes: row.durationMinutes,\n roundedMinutes: row.roundedMinutes ?? null,\n timeProjectId: row.timeProjectId ?? null,\n taskId: row.taskId ?? null,\n isBillable: row.isBillable,\n description: row.notes ?? null,\n tagIds: tagIdsByEntry.get(sourceId) ?? [],\n },\n ]\n })\n\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: created.length,\n created,\n skipped,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.copy-day failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Copy a day of time entries',\n methods: {\n POST: {\n summary: 'Copy a day of time entries',\n description:\n 'US-D6, screen 1 note 5. Copies every unlocked time entry the caller owns on `fromDate` onto `toDate` as editable drafts, and returns the created rows so they can be reviewed and corrected. Which day is \"the previous working day\" is the caller decision \u2014 this endpoint takes explicit dates and applies no weekend or holiday rule. A source entry locked into a closed report is skipped and reported in `skipped[]` with the shared `time_entry_locked` code (nothing is written to it, so the batch is not failed). The request is refused with 409 `copy_day_target_not_empty` when the target day already holds entries, so a repeated call cannot silently double a day; pass `allowDuplicates: true` to add the copies anyway. `staffMemberId` targets another member and requires `staff.timesheets.manage_all`. Every copy is written through the duplicate command and therefore through the create command, so ownership, project access, the billable and currency defaults and the tenant rounding rule apply to it exactly as they do to a hand-made entry.',\n requestBody: {\n contentType: 'application/json',\n schema: copyDayRequestSchema,\n },\n responses: [\n { status: 200, description: 'Copies created, with the skipped sources reported', schema: copyDayResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, missing scope, or fromDate equal to toDate' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.manage_own, no linked staff member, or another member day without staff.timesheets.manage_all' },\n {\n status: 409,\n description: 'The target day already has entries; nothing was copied',\n schema: targetNotEmptySchema,\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
6
7
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
8
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
8
9
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
@@ -177,7 +178,7 @@ async function GET(req) {
177
178
  }
178
179
  const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
179
180
  const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null;
180
- const organizationId = orgScope?.selectedId ?? auth.orgId ?? null;
181
+ const organizationId = resolveSingleOrganizationIdOrDeny(orgScope, auth) ?? null;
181
182
  if (!tenantId || !organizationId) {
182
183
  throw new CrudHttpError(400, {
183
184
  error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/overlaps/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport { deriveInterval, formatClock, parseClock } from '../../../../lib/time-tracking/interval'\nimport {\n evaluateOverlapPolicies,\n findOverlaps,\n type OverlapSpan,\n} from '../../../../lib/time-tracking/overlap'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport {\n MANAGE_PROJECTS_FEATURE,\n assertProjectAccess,\n resolveProjectAccess,\n} from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport {\n readSearchParamsRecord,\n runTimesheetInterceptors,\n} from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/overlaps' })\n\nconst VIEW_FEATURE = 'staff.timesheets.view'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\n/**\n * Upper bound for the day-window scan. One staff member can only hold a handful\n * of entries across three days, so this is a guard against pathological data\n * rather than a paging contract.\n */\nconst MAX_WINDOW_ROWS = 500\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n}\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst DATE_PREFIX_PATTERN = /^(\\d{4}-\\d{2}-\\d{2})/\n\nconst querySchema = z.object({\n date: z.string().regex(DATE_PATTERN),\n startedAt: z.string().optional(),\n endedAt: z.string().optional(),\n durationMinutes: z.coerce.number().int().min(0).max(1440).optional(),\n excludeId: z.string().uuid().optional(),\n staffMemberId: z.string().uuid().optional(),\n})\n\nconst overlapItemSchema = z.object({\n id: z.string().uuid(),\n staff_member_id: z.string().uuid(),\n date: z.string(),\n started_at: z.string(),\n ended_at: z.string(),\n duration_minutes: z.number(),\n overlap_minutes: z.number(),\n crosses_midnight: z.boolean(),\n task_id: z.string().uuid().nullable(),\n task_title: z.string().nullable(),\n time_project_id: z.string().uuid().nullable(),\n project_name: z.string().nullable(),\n notes: z.string().nullable(),\n})\n\nconst overlapsResponseSchema = z.object({\n items: z.array(overlapItemSchema),\n total: z.number(),\n /**\n * EP-38. `warn` is what this route has always meant; the field exists so a\n * contributed policy that escalates to `block` reaches the form, and so a\n * caller can tell \"the tenant turned the warning off\" from \"nothing overlaps\".\n */\n decision: z.enum(['allow', 'warn', 'block']),\n})\n\ntype OverlapItem = z.infer<typeof overlapItemSchema>\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\ntype EntryRow = Pick<\n StaffTimeEntry,\n | 'id'\n | 'staffMemberId'\n | 'durationMinutes'\n | 'startedAt'\n | 'endedAt'\n | 'notes'\n | 'taskId'\n | 'timeProjectId'\n> & { date: unknown }\n\nconst EMPTY_OVERLAP_RESULT = { items: [], total: 0, decision: 'allow' as const }\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDate(value: Date): string {\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n}\n\nfunction shiftDate(date: string, days: number): string {\n const [year, month, day] = date.split('-').map(Number)\n const shifted = new Date(Date.UTC(year, month - 1, day + days))\n return `${shifted.getUTCFullYear()}-${pad(shifted.getUTCMonth() + 1)}-${pad(shifted.getUTCDate())}`\n}\n\nfunction toDayString(value: unknown): string | null {\n if (typeof value === 'string') {\n const match = DATE_PREFIX_PATTERN.exec(value.trim())\n return match ? match[1] : null\n }\n if (value instanceof Date && !Number.isNaN(value.getTime())) return formatLocalDate(value)\n return null\n}\n\n/**\n * Accepts either a wall clock (`11:45`, what the entry form sends) or a full\n * timestamp (what the timer endpoints persist), and always answers in the\n * calendar the rest of the module reads dates in.\n */\nfunction toClock(value: unknown): string | null {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return formatClock(value.getHours() * 60 + value.getMinutes())\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed) return null\n const clock = parseClock(trimmed)\n if (clock !== null) return formatClock(clock)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return formatClock(parsed.getHours() * 60 + parsed.getMinutes())\n}\n\nfunction toRowSpan(row: EntryRow): OverlapSpan | null {\n const start = toClock(row.startedAt)\n if (!start) return null\n const date =\n (row.startedAt instanceof Date && !Number.isNaN(row.startedAt.getTime())\n ? formatLocalDate(row.startedAt)\n : null) ?? toDayString(row.date)\n if (!date) return null\n return {\n id: row.id,\n date,\n start,\n end: toClock(row.endedAt),\n durationMinutes: typeof row.durationMinutes === 'number' ? row.durationMinutes : null,\n }\n}\n\nfunction readQuery(params: URLSearchParams): z.infer<typeof querySchema> {\n const raw: Record<string, string> = {}\n for (const key of ['date', 'startedAt', 'endedAt', 'durationMinutes', 'excludeId', 'staffMemberId']) {\n const value = params.get(key)\n if (value !== null && value.trim().length > 0) raw[key] = value.trim()\n }\n return querySchema.parse(raw)\n}\n\nasync function resolveGrantedFeatures(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n userId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<string[]> {\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return []\n return (await rbac.getGrantedFeatures(userId, scope)) ?? []\n } catch {\n return []\n }\n}\n\nasync function resolveOverlapSettings(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n tenantId: string,\n): Promise<{ assignmentGraceDays: number | null; warningsEnabled: boolean }> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return {\n assignmentGraceDays: settings.access.assignmentGraceDays,\n warningsEnabled: settings.warnings.overlap,\n }\n } catch {\n // The same fail-safe the grace window uses: an unreadable settings row keeps\n // the advisory warning on, which is what the module default says.\n return { assignmentGraceDays: null, warningsEnabled: true }\n }\n}\n\nasync function loadLabels(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n taskIds: string[],\n projectIds: string[],\n): Promise<{ tasks: Map<string, string>; projects: Map<string, string> }> {\n const tasks = new Map<string, string>()\n const projects = new Map<string, string>()\n if (taskIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeTask,\n { id: { $in: taskIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) tasks.set(row.id, row.title)\n }\n if (projectIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeProject,\n { id: { $in: projectIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) projects.set(row.id, row.name)\n }\n return { tasks, projects }\n}\n\n/**\n * GET /api/staff/timesheets/time-entries/overlaps\n *\n * Advisory-only overlap probe for the time entry form (mockup screen 9). It is\n * read-only and never blocks a save: overlapping time is legitimate in\n * consulting work (a client call while taking notes), so the UI renders the\n * result as a warning with a suggested fix, never as a validation error. Any\n * failure here MUST degrade to \"no warning\" rather than break the form \u2014 the\n * route therefore answers `{ items: [], total: 0 }` for unusable input, a\n * caller without a linked staff member, or a lookup that yields nothing, and\n * the client is expected to ignore non-200 responses.\n *\n * Scoping: rows are always filtered by `tenant_id` + `organization_id` and\n * intersected with `resolveProjectAccess`, so the endpoint cannot be used to\n * probe entries on projects the caller cannot see. Asking about another staff\n * member requires `staff.timesheets.manage_all`; without it the request is\n * silently scoped back to the caller's own member instead of erroring.\n *\n * Query shape: the form calls this on blur, so the lookup reads a three-day\n * window (`date - 1 .. date + 1`) against the\n * `(organization_id, staff_member_id, date, started_at)` index. The adjacent\n * days cover midnight-crossing spans in both directions \u2014 a candidate that ends\n * after midnight, and a stored entry that started the previous evening \u2014 without\n * ever scanning history.\n */\nexport async function GET(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null\n const organizationId = orgScope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'GET',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n query: readSearchParamsRecord(req.url),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const query = readQuery(session.searchParams)\n const candidate: OverlapSpan = {\n date: query.date,\n start: toClock(query.startedAt),\n end: toClock(query.endedAt),\n durationMinutes: query.durationMinutes ?? null,\n }\n const derived = deriveInterval({\n start: candidate.start,\n end: candidate.end,\n durationMinutes: candidate.durationMinutes,\n })\n if (!derived.start || derived.durationMinutes === null || derived.durationMinutes <= 0) {\n return session.respond(200, EMPTY_OVERLAP_RESULT)\n }\n\n const scope = { tenantId, organizationId }\n const em = (container.resolve('em') as EntityManager).fork()\n const userId = typeof auth.sub === 'string' ? auth.sub : null\n // Both questions go to the service. Asking one and inferring the other from a\n // grant array is how the two could disagree when the read failed.\n const manageAll = await resolveFeatureAccess(container, userId, [MANAGE_ALL_FEATURE], scope)\n const manageProjects = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)\n const canManageAll = manageAll.allowed\n\n const overlapSettings = await resolveOverlapSettings(container, tenantId)\n const access = await resolveProjectAccess({\n em,\n userId,\n tenantId,\n organizationId,\n canManageAll: manageProjects.allowed,\n userFeatures: manageAll.grantedFeatures,\n assignmentGraceDays: overlapSettings.assignmentGraceDays,\n })\n\n const staffMemberId =\n canManageAll && query.staffMemberId ? query.staffMemberId : access.staffMemberId\n if (!staffMemberId) return session.respond(200, EMPTY_OVERLAP_RESULT)\n const isSelfScope = staffMemberId === access.staffMemberId\n\n const where = {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n date: { $gte: shiftDate(query.date, -1), $lte: shiftDate(query.date, 1) },\n } as unknown as FilterQuery<StaffTimeEntry>\n const rows = (await findWithDecryption(\n em,\n StaffTimeEntry,\n where,\n { orderBy: { startedAt: 'asc' }, limit: MAX_WINDOW_ROWS },\n scope,\n )) as unknown as EntryRow[]\n\n const visible = rows.filter(\n (row) =>\n assertProjectAccess(access, row.timeProjectId ?? null) ||\n (isSelfScope && !row.timeProjectId),\n )\n const byId = new Map(visible.map((row) => [row.id, row]))\n const spans = visible\n .map((row) => toRowSpan(row))\n .filter((span): span is OverlapSpan => span !== null)\n\n const candidateSpan: OverlapSpan = {\n date: query.date,\n start: derived.start,\n end: derived.end,\n durationMinutes: derived.durationMinutes,\n }\n const overlaps = findOverlaps(\n candidateSpan,\n spans,\n query.excludeId ? { excludeId: query.excludeId } : undefined,\n )\n const decision = evaluateOverlapPolicies(overlaps, {\n tenantId,\n organizationId,\n candidate: candidateSpan,\n warningsEnabled: overlapSettings.warningsEnabled,\n staffMemberId,\n excludeId: query.excludeId ?? null,\n })\n // The item list is unchanged by the policy: this endpoint answers \"what\n // intersects\", and `decision` answers \"and what should happen\". Collapsing\n // the list when a tenant turns the warning off would make the two\n // indistinguishable from \"nothing intersects\" for every other caller.\n if (!overlaps.length) return session.respond(200, { ...EMPTY_OVERLAP_RESULT, decision })\n\n const taskIds = new Set<string>()\n const projectIds = new Set<string>()\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (row?.taskId) taskIds.add(row.taskId)\n if (row?.timeProjectId) projectIds.add(row.timeProjectId)\n }\n const labels = await loadLabels(em, scope, [...taskIds], [...projectIds])\n\n const items: OverlapItem[] = []\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (!row) continue\n const interval = deriveInterval({\n start: overlap.entry.start,\n end: overlap.entry.end,\n durationMinutes: overlap.entry.durationMinutes,\n })\n items.push({\n id: row.id,\n staff_member_id: row.staffMemberId,\n date: overlap.entry.date,\n started_at: interval.start ?? '',\n ended_at: interval.end ?? '',\n duration_minutes: interval.durationMinutes ?? 0,\n overlap_minutes: overlap.overlapMinutes,\n crosses_midnight: interval.crossesMidnight,\n task_id: row.taskId ?? null,\n task_title: row.taskId ? labels.tasks.get(row.taskId) ?? null : null,\n time_project_id: row.timeProjectId ?? null,\n project_name: row.timeProjectId ? labels.projects.get(row.timeProjectId) ?? null : null,\n notes: row.notes ?? null,\n })\n }\n\n return session.respond(200, { items, total: items.length, decision })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.overlaps GET failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Advisory time entry overlap check',\n methods: {\n GET: {\n summary: 'List time entries overlapping a candidate span',\n description:\n 'Advisory, read-only lookup used by the time entry form to warn about conflicting entries. Overlapping time is allowed, so the result never blocks a save. Returns an empty list when the supplied time information is insufficient. Checking another staff member requires staff.timesheets.manage_all; without it the request is silently scoped to the caller.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Overlapping entries', schema: overlapsResponseSchema },\n { status: 400, description: 'Invalid query or missing scope', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport { deriveInterval, formatClock, parseClock } from '../../../../lib/time-tracking/interval'\nimport {\n evaluateOverlapPolicies,\n findOverlaps,\n type OverlapSpan,\n} from '../../../../lib/time-tracking/overlap'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport {\n MANAGE_PROJECTS_FEATURE,\n assertProjectAccess,\n resolveProjectAccess,\n} from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport {\n readSearchParamsRecord,\n runTimesheetInterceptors,\n} from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/overlaps' })\n\nconst VIEW_FEATURE = 'staff.timesheets.view'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\n/**\n * Upper bound for the day-window scan. One staff member can only hold a handful\n * of entries across three days, so this is a guard against pathological data\n * rather than a paging contract.\n */\nconst MAX_WINDOW_ROWS = 500\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n}\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst DATE_PREFIX_PATTERN = /^(\\d{4}-\\d{2}-\\d{2})/\n\nconst querySchema = z.object({\n date: z.string().regex(DATE_PATTERN),\n startedAt: z.string().optional(),\n endedAt: z.string().optional(),\n durationMinutes: z.coerce.number().int().min(0).max(1440).optional(),\n excludeId: z.string().uuid().optional(),\n staffMemberId: z.string().uuid().optional(),\n})\n\nconst overlapItemSchema = z.object({\n id: z.string().uuid(),\n staff_member_id: z.string().uuid(),\n date: z.string(),\n started_at: z.string(),\n ended_at: z.string(),\n duration_minutes: z.number(),\n overlap_minutes: z.number(),\n crosses_midnight: z.boolean(),\n task_id: z.string().uuid().nullable(),\n task_title: z.string().nullable(),\n time_project_id: z.string().uuid().nullable(),\n project_name: z.string().nullable(),\n notes: z.string().nullable(),\n})\n\nconst overlapsResponseSchema = z.object({\n items: z.array(overlapItemSchema),\n total: z.number(),\n /**\n * EP-38. `warn` is what this route has always meant; the field exists so a\n * contributed policy that escalates to `block` reaches the form, and so a\n * caller can tell \"the tenant turned the warning off\" from \"nothing overlaps\".\n */\n decision: z.enum(['allow', 'warn', 'block']),\n})\n\ntype OverlapItem = z.infer<typeof overlapItemSchema>\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\ntype EntryRow = Pick<\n StaffTimeEntry,\n | 'id'\n | 'staffMemberId'\n | 'durationMinutes'\n | 'startedAt'\n | 'endedAt'\n | 'notes'\n | 'taskId'\n | 'timeProjectId'\n> & { date: unknown }\n\nconst EMPTY_OVERLAP_RESULT = { items: [], total: 0, decision: 'allow' as const }\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDate(value: Date): string {\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n}\n\nfunction shiftDate(date: string, days: number): string {\n const [year, month, day] = date.split('-').map(Number)\n const shifted = new Date(Date.UTC(year, month - 1, day + days))\n return `${shifted.getUTCFullYear()}-${pad(shifted.getUTCMonth() + 1)}-${pad(shifted.getUTCDate())}`\n}\n\nfunction toDayString(value: unknown): string | null {\n if (typeof value === 'string') {\n const match = DATE_PREFIX_PATTERN.exec(value.trim())\n return match ? match[1] : null\n }\n if (value instanceof Date && !Number.isNaN(value.getTime())) return formatLocalDate(value)\n return null\n}\n\n/**\n * Accepts either a wall clock (`11:45`, what the entry form sends) or a full\n * timestamp (what the timer endpoints persist), and always answers in the\n * calendar the rest of the module reads dates in.\n */\nfunction toClock(value: unknown): string | null {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return formatClock(value.getHours() * 60 + value.getMinutes())\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed) return null\n const clock = parseClock(trimmed)\n if (clock !== null) return formatClock(clock)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return formatClock(parsed.getHours() * 60 + parsed.getMinutes())\n}\n\nfunction toRowSpan(row: EntryRow): OverlapSpan | null {\n const start = toClock(row.startedAt)\n if (!start) return null\n const date =\n (row.startedAt instanceof Date && !Number.isNaN(row.startedAt.getTime())\n ? formatLocalDate(row.startedAt)\n : null) ?? toDayString(row.date)\n if (!date) return null\n return {\n id: row.id,\n date,\n start,\n end: toClock(row.endedAt),\n durationMinutes: typeof row.durationMinutes === 'number' ? row.durationMinutes : null,\n }\n}\n\nfunction readQuery(params: URLSearchParams): z.infer<typeof querySchema> {\n const raw: Record<string, string> = {}\n for (const key of ['date', 'startedAt', 'endedAt', 'durationMinutes', 'excludeId', 'staffMemberId']) {\n const value = params.get(key)\n if (value !== null && value.trim().length > 0) raw[key] = value.trim()\n }\n return querySchema.parse(raw)\n}\n\nasync function resolveGrantedFeatures(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n userId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<string[]> {\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return []\n return (await rbac.getGrantedFeatures(userId, scope)) ?? []\n } catch {\n return []\n }\n}\n\nasync function resolveOverlapSettings(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n tenantId: string,\n): Promise<{ assignmentGraceDays: number | null; warningsEnabled: boolean }> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return {\n assignmentGraceDays: settings.access.assignmentGraceDays,\n warningsEnabled: settings.warnings.overlap,\n }\n } catch {\n // The same fail-safe the grace window uses: an unreadable settings row keeps\n // the advisory warning on, which is what the module default says.\n return { assignmentGraceDays: null, warningsEnabled: true }\n }\n}\n\nasync function loadLabels(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n taskIds: string[],\n projectIds: string[],\n): Promise<{ tasks: Map<string, string>; projects: Map<string, string> }> {\n const tasks = new Map<string, string>()\n const projects = new Map<string, string>()\n if (taskIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeTask,\n { id: { $in: taskIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) tasks.set(row.id, row.title)\n }\n if (projectIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeProject,\n { id: { $in: projectIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) projects.set(row.id, row.name)\n }\n return { tasks, projects }\n}\n\n/**\n * GET /api/staff/timesheets/time-entries/overlaps\n *\n * Advisory-only overlap probe for the time entry form (mockup screen 9). It is\n * read-only and never blocks a save: overlapping time is legitimate in\n * consulting work (a client call while taking notes), so the UI renders the\n * result as a warning with a suggested fix, never as a validation error. Any\n * failure here MUST degrade to \"no warning\" rather than break the form \u2014 the\n * route therefore answers `{ items: [], total: 0 }` for unusable input, a\n * caller without a linked staff member, or a lookup that yields nothing, and\n * the client is expected to ignore non-200 responses.\n *\n * Scoping: rows are always filtered by `tenant_id` + `organization_id` and\n * intersected with `resolveProjectAccess`, so the endpoint cannot be used to\n * probe entries on projects the caller cannot see. Asking about another staff\n * member requires `staff.timesheets.manage_all`; without it the request is\n * silently scoped back to the caller's own member instead of erroring.\n *\n * Query shape: the form calls this on blur, so the lookup reads a three-day\n * window (`date - 1 .. date + 1`) against the\n * `(organization_id, staff_member_id, date, started_at)` index. The adjacent\n * days cover midnight-crossing spans in both directions \u2014 a candidate that ends\n * after midnight, and a stored entry that started the previous evening \u2014 without\n * ever scanning history.\n */\nexport async function GET(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(orgScope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'GET',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n query: readSearchParamsRecord(req.url),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const query = readQuery(session.searchParams)\n const candidate: OverlapSpan = {\n date: query.date,\n start: toClock(query.startedAt),\n end: toClock(query.endedAt),\n durationMinutes: query.durationMinutes ?? null,\n }\n const derived = deriveInterval({\n start: candidate.start,\n end: candidate.end,\n durationMinutes: candidate.durationMinutes,\n })\n if (!derived.start || derived.durationMinutes === null || derived.durationMinutes <= 0) {\n return session.respond(200, EMPTY_OVERLAP_RESULT)\n }\n\n const scope = { tenantId, organizationId }\n const em = (container.resolve('em') as EntityManager).fork()\n const userId = typeof auth.sub === 'string' ? auth.sub : null\n // Both questions go to the service. Asking one and inferring the other from a\n // grant array is how the two could disagree when the read failed.\n const manageAll = await resolveFeatureAccess(container, userId, [MANAGE_ALL_FEATURE], scope)\n const manageProjects = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)\n const canManageAll = manageAll.allowed\n\n const overlapSettings = await resolveOverlapSettings(container, tenantId)\n const access = await resolveProjectAccess({\n em,\n userId,\n tenantId,\n organizationId,\n canManageAll: manageProjects.allowed,\n userFeatures: manageAll.grantedFeatures,\n assignmentGraceDays: overlapSettings.assignmentGraceDays,\n })\n\n const staffMemberId =\n canManageAll && query.staffMemberId ? query.staffMemberId : access.staffMemberId\n if (!staffMemberId) return session.respond(200, EMPTY_OVERLAP_RESULT)\n const isSelfScope = staffMemberId === access.staffMemberId\n\n const where = {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n date: { $gte: shiftDate(query.date, -1), $lte: shiftDate(query.date, 1) },\n } as unknown as FilterQuery<StaffTimeEntry>\n const rows = (await findWithDecryption(\n em,\n StaffTimeEntry,\n where,\n { orderBy: { startedAt: 'asc' }, limit: MAX_WINDOW_ROWS },\n scope,\n )) as unknown as EntryRow[]\n\n const visible = rows.filter(\n (row) =>\n assertProjectAccess(access, row.timeProjectId ?? null) ||\n (isSelfScope && !row.timeProjectId),\n )\n const byId = new Map(visible.map((row) => [row.id, row]))\n const spans = visible\n .map((row) => toRowSpan(row))\n .filter((span): span is OverlapSpan => span !== null)\n\n const candidateSpan: OverlapSpan = {\n date: query.date,\n start: derived.start,\n end: derived.end,\n durationMinutes: derived.durationMinutes,\n }\n const overlaps = findOverlaps(\n candidateSpan,\n spans,\n query.excludeId ? { excludeId: query.excludeId } : undefined,\n )\n const decision = evaluateOverlapPolicies(overlaps, {\n tenantId,\n organizationId,\n candidate: candidateSpan,\n warningsEnabled: overlapSettings.warningsEnabled,\n staffMemberId,\n excludeId: query.excludeId ?? null,\n })\n // The item list is unchanged by the policy: this endpoint answers \"what\n // intersects\", and `decision` answers \"and what should happen\". Collapsing\n // the list when a tenant turns the warning off would make the two\n // indistinguishable from \"nothing intersects\" for every other caller.\n if (!overlaps.length) return session.respond(200, { ...EMPTY_OVERLAP_RESULT, decision })\n\n const taskIds = new Set<string>()\n const projectIds = new Set<string>()\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (row?.taskId) taskIds.add(row.taskId)\n if (row?.timeProjectId) projectIds.add(row.timeProjectId)\n }\n const labels = await loadLabels(em, scope, [...taskIds], [...projectIds])\n\n const items: OverlapItem[] = []\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (!row) continue\n const interval = deriveInterval({\n start: overlap.entry.start,\n end: overlap.entry.end,\n durationMinutes: overlap.entry.durationMinutes,\n })\n items.push({\n id: row.id,\n staff_member_id: row.staffMemberId,\n date: overlap.entry.date,\n started_at: interval.start ?? '',\n ended_at: interval.end ?? '',\n duration_minutes: interval.durationMinutes ?? 0,\n overlap_minutes: overlap.overlapMinutes,\n crosses_midnight: interval.crossesMidnight,\n task_id: row.taskId ?? null,\n task_title: row.taskId ? labels.tasks.get(row.taskId) ?? null : null,\n time_project_id: row.timeProjectId ?? null,\n project_name: row.timeProjectId ? labels.projects.get(row.timeProjectId) ?? null : null,\n notes: row.notes ?? null,\n })\n }\n\n return session.respond(200, { items, total: items.length, decision })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.overlaps GET failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Advisory time entry overlap check',\n methods: {\n GET: {\n summary: 'List time entries overlapping a candidate span',\n description:\n 'Advisory, read-only lookup used by the time entry form to warn about conflicting entries. Overlapping time is allowed, so the result never blocks a save. Returns an empty list when the supplied time information is insufficient. Checking another staff member requires staff.timesheets.manage_all; without it the request is silently scoped to the caller.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Overlapping entries', schema: overlapsResponseSchema },\n { status: 400, description: 'Invalid query or missing scope', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
6
7
  import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
7
8
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
8
9
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
@@ -27,7 +28,7 @@ async function buildContext(req) {
27
28
  container,
28
29
  auth,
29
30
  organizationScope: scope,
30
- selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,
31
+ selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,
31
32
  organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
32
33
  request: req
33
34
  };
@@ -35,7 +36,7 @@ async function buildContext(req) {
35
36
  ctx,
36
37
  translate,
37
38
  tenantId: scope?.tenantId ?? auth.tenantId ?? null,
38
- organizationId: scope?.selectedId ?? auth.orgId ?? null
39
+ organizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null
39
40
  };
40
41
  }
41
42
  async function POST(req) {