@open-mercato/core 0.6.8-develop.6924.1.a8d208fcdc → 0.6.8-develop.6930.1.1e5976efc3

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 (453) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/eudr_due_diligence_statement/index.js +47 -0
  3. package/dist/generated/entities/eudr_due_diligence_statement/index.js.map +7 -0
  4. package/dist/generated/entities/eudr_evidence_submission/index.js +51 -0
  5. package/dist/generated/entities/eudr_evidence_submission/index.js.map +7 -0
  6. package/dist/generated/entities/eudr_mitigation_action/index.js +31 -0
  7. package/dist/generated/entities/eudr_mitigation_action/index.js.map +7 -0
  8. package/dist/generated/entities/eudr_plot/index.js +39 -0
  9. package/dist/generated/entities/eudr_plot/index.js.map +7 -0
  10. package/dist/generated/entities/eudr_product_mapping/index.js +31 -0
  11. package/dist/generated/entities/eudr_product_mapping/index.js.map +7 -0
  12. package/dist/generated/entities/eudr_risk_assessment/index.js +35 -0
  13. package/dist/generated/entities/eudr_risk_assessment/index.js.map +7 -0
  14. package/dist/generated/entities/role/index.js +2 -0
  15. package/dist/generated/entities/role/index.js.map +2 -2
  16. package/dist/generated/entities.ids.generated.js +9 -0
  17. package/dist/generated/entities.ids.generated.js.map +2 -2
  18. package/dist/generated/entity-fields-registry.js +121 -0
  19. package/dist/generated/entity-fields-registry.js.map +2 -2
  20. package/dist/helpers/integration/api.js +4 -0
  21. package/dist/helpers/integration/api.js.map +2 -2
  22. package/dist/modules/attachments/fields/attachment.js +5 -1
  23. package/dist/modules/attachments/fields/attachment.js.map +2 -2
  24. package/dist/modules/attachments/lib/crud.js +3 -1
  25. package/dist/modules/attachments/lib/crud.js.map +2 -2
  26. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
  27. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  28. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +5 -0
  29. package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
  30. package/dist/modules/audit_logs/backend/audit-logs/page.js +4 -4
  31. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  32. package/dist/modules/audit_logs/components/AuditLogsActions.js +9 -2
  33. package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
  34. package/dist/modules/auth/api/login.js +5 -2
  35. package/dist/modules/auth/api/login.js.map +2 -2
  36. package/dist/modules/auth/api/users/route.js +8 -5
  37. package/dist/modules/auth/api/users/route.js.map +2 -2
  38. package/dist/modules/auth/backend/users/[id]/edit/page.js +15 -1
  39. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  40. package/dist/modules/auth/backend/users/page.js +7 -1
  41. package/dist/modules/auth/backend/users/page.js.map +2 -2
  42. package/dist/modules/auth/commands/users.js +175 -76
  43. package/dist/modules/auth/commands/users.js.map +2 -2
  44. package/dist/modules/auth/data/entities.js +4 -0
  45. package/dist/modules/auth/data/entities.js.map +2 -2
  46. package/dist/modules/auth/lib/sessionIntegrity.js +1 -1
  47. package/dist/modules/auth/lib/sessionIntegrity.js.map +2 -2
  48. package/dist/modules/auth/lib/setup-app.js +1 -1
  49. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  50. package/dist/modules/auth/migrations/Migration20260728134212_auth.js +14 -0
  51. package/dist/modules/auth/migrations/Migration20260728134212_auth.js.map +7 -0
  52. package/dist/modules/eudr/acl.js +43 -0
  53. package/dist/modules/eudr/acl.js.map +7 -0
  54. package/dist/modules/eudr/ai-agents.js +205 -0
  55. package/dist/modules/eudr/ai-agents.js.map +7 -0
  56. package/dist/modules/eudr/ai-tools/compliance-pack.js +495 -0
  57. package/dist/modules/eudr/ai-tools/compliance-pack.js.map +7 -0
  58. package/dist/modules/eudr/ai-tools/types.js +10 -0
  59. package/dist/modules/eudr/ai-tools/types.js.map +7 -0
  60. package/dist/modules/eudr/ai-tools.js +10 -0
  61. package/dist/modules/eudr/ai-tools.js.map +7 -0
  62. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +386 -0
  63. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +7 -0
  64. package/dist/modules/eudr/api/evidence-submissions/route.js +318 -0
  65. package/dist/modules/eudr/api/evidence-submissions/route.js.map +7 -0
  66. package/dist/modules/eudr/api/mitigation-actions/route.js +249 -0
  67. package/dist/modules/eudr/api/mitigation-actions/route.js.map +7 -0
  68. package/dist/modules/eudr/api/openapi.js +27 -0
  69. package/dist/modules/eudr/api/openapi.js.map +7 -0
  70. package/dist/modules/eudr/api/plots/import/route.js +294 -0
  71. package/dist/modules/eudr/api/plots/import/route.js.map +7 -0
  72. package/dist/modules/eudr/api/plots/route.js +279 -0
  73. package/dist/modules/eudr/api/plots/route.js.map +7 -0
  74. package/dist/modules/eudr/api/product-mappings/route.js +257 -0
  75. package/dist/modules/eudr/api/product-mappings/route.js.map +7 -0
  76. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +298 -0
  77. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +7 -0
  78. package/dist/modules/eudr/api/product-mappings/suggestions/route.js +132 -0
  79. package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +7 -0
  80. package/dist/modules/eudr/api/reports/annual/route.js +266 -0
  81. package/dist/modules/eudr/api/reports/annual/route.js.map +7 -0
  82. package/dist/modules/eudr/api/risk-assessments/route.js +300 -0
  83. package/dist/modules/eudr/api/risk-assessments/route.js.map +7 -0
  84. package/dist/modules/eudr/api/statements/[id]/export/route.js +631 -0
  85. package/dist/modules/eudr/api/statements/[id]/export/route.js.map +7 -0
  86. package/dist/modules/eudr/api/statements/[id]/readiness/route.js +111 -0
  87. package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +7 -0
  88. package/dist/modules/eudr/api/statements/route.js +314 -0
  89. package/dist/modules/eudr/api/statements/route.js.map +7 -0
  90. package/dist/modules/eudr/api/suppliers/compliance/route.js +173 -0
  91. package/dist/modules/eudr/api/suppliers/compliance/route.js.map +7 -0
  92. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js +468 -0
  93. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js.map +7 -0
  94. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js +16 -0
  95. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js.map +7 -0
  96. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js +351 -0
  97. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js.map +7 -0
  98. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js +17 -0
  99. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js.map +7 -0
  100. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +313 -0
  101. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +7 -0
  102. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js +16 -0
  103. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js.map +7 -0
  104. package/dist/modules/eudr/backend/eudr/page.js +360 -0
  105. package/dist/modules/eudr/backend/eudr/page.js.map +7 -0
  106. package/dist/modules/eudr/backend/eudr/page.meta.js +16 -0
  107. package/dist/modules/eudr/backend/eudr/page.meta.js.map +7 -0
  108. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js +346 -0
  109. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js.map +7 -0
  110. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js +16 -0
  111. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js.map +7 -0
  112. package/dist/modules/eudr/backend/eudr/plots/create/page.js +253 -0
  113. package/dist/modules/eudr/backend/eudr/plots/create/page.js.map +7 -0
  114. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js +17 -0
  115. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js.map +7 -0
  116. package/dist/modules/eudr/backend/eudr/plots/page.js +386 -0
  117. package/dist/modules/eudr/backend/eudr/plots/page.js.map +7 -0
  118. package/dist/modules/eudr/backend/eudr/plots/page.meta.js +16 -0
  119. package/dist/modules/eudr/backend/eudr/plots/page.meta.js.map +7 -0
  120. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js +253 -0
  121. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js.map +7 -0
  122. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js +16 -0
  123. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js.map +7 -0
  124. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js +174 -0
  125. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js.map +7 -0
  126. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js +17 -0
  127. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js.map +7 -0
  128. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +306 -0
  129. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +7 -0
  130. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js +16 -0
  131. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js.map +7 -0
  132. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js +335 -0
  133. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js.map +7 -0
  134. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js +16 -0
  135. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js.map +7 -0
  136. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js +220 -0
  137. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js.map +7 -0
  138. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js +17 -0
  139. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js.map +7 -0
  140. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +324 -0
  141. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +7 -0
  142. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js +16 -0
  143. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js.map +7 -0
  144. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js +648 -0
  145. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js.map +7 -0
  146. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js +16 -0
  147. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js.map +7 -0
  148. package/dist/modules/eudr/backend/eudr/statements/create/page.js +432 -0
  149. package/dist/modules/eudr/backend/eudr/statements/create/page.js.map +7 -0
  150. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js +17 -0
  151. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js.map +7 -0
  152. package/dist/modules/eudr/backend/eudr/statements/page.js +309 -0
  153. package/dist/modules/eudr/backend/eudr/statements/page.js.map +7 -0
  154. package/dist/modules/eudr/backend/eudr/statements/page.meta.js +16 -0
  155. package/dist/modules/eudr/backend/eudr/statements/page.meta.js.map +7 -0
  156. package/dist/modules/eudr/ce.js +56 -0
  157. package/dist/modules/eudr/ce.js.map +7 -0
  158. package/dist/modules/eudr/commands/evidence-submissions.js +571 -0
  159. package/dist/modules/eudr/commands/evidence-submissions.js.map +7 -0
  160. package/dist/modules/eudr/commands/index.js +7 -0
  161. package/dist/modules/eudr/commands/index.js.map +7 -0
  162. package/dist/modules/eudr/commands/lifecycle-events.js +19 -0
  163. package/dist/modules/eudr/commands/lifecycle-events.js.map +7 -0
  164. package/dist/modules/eudr/commands/mitigation-actions.js +488 -0
  165. package/dist/modules/eudr/commands/mitigation-actions.js.map +7 -0
  166. package/dist/modules/eudr/commands/plots.js +517 -0
  167. package/dist/modules/eudr/commands/plots.js.map +7 -0
  168. package/dist/modules/eudr/commands/product-mappings.js +468 -0
  169. package/dist/modules/eudr/commands/product-mappings.js.map +7 -0
  170. package/dist/modules/eudr/commands/risk-assessments.js +599 -0
  171. package/dist/modules/eudr/commands/risk-assessments.js.map +7 -0
  172. package/dist/modules/eudr/commands/statements.js +747 -0
  173. package/dist/modules/eudr/commands/statements.js.map +7 -0
  174. package/dist/modules/eudr/components/CountrySelectField.js +64 -0
  175. package/dist/modules/eudr/components/CountrySelectField.js.map +7 -0
  176. package/dist/modules/eudr/components/GeometryInput.js +133 -0
  177. package/dist/modules/eudr/components/GeometryInput.js.map +7 -0
  178. package/dist/modules/eudr/components/MappingSuggestionsDialog.js +307 -0
  179. package/dist/modules/eudr/components/MappingSuggestionsDialog.js.map +7 -0
  180. package/dist/modules/eudr/components/MitigationActionsSection.js +345 -0
  181. package/dist/modules/eudr/components/MitigationActionsSection.js.map +7 -0
  182. package/dist/modules/eudr/components/OrderSelectField.js +88 -0
  183. package/dist/modules/eudr/components/OrderSelectField.js.map +7 -0
  184. package/dist/modules/eudr/components/PlotImportDialog.js +229 -0
  185. package/dist/modules/eudr/components/PlotImportDialog.js.map +7 -0
  186. package/dist/modules/eudr/components/PlotMapPreview.js +35 -0
  187. package/dist/modules/eudr/components/PlotMapPreview.js.map +7 -0
  188. package/dist/modules/eudr/components/PlotMapPreviewImpl.js +97 -0
  189. package/dist/modules/eudr/components/PlotMapPreviewImpl.js.map +7 -0
  190. package/dist/modules/eudr/components/PlotMultiSelectField.js +198 -0
  191. package/dist/modules/eudr/components/PlotMultiSelectField.js.map +7 -0
  192. package/dist/modules/eudr/components/RiskCriteriaField.js +128 -0
  193. package/dist/modules/eudr/components/RiskCriteriaField.js.map +7 -0
  194. package/dist/modules/eudr/components/StatementLifecycleBar.js +274 -0
  195. package/dist/modules/eudr/components/StatementLifecycleBar.js.map +7 -0
  196. package/dist/modules/eudr/components/StatementReadinessChecklist.js +56 -0
  197. package/dist/modules/eudr/components/StatementReadinessChecklist.js.map +7 -0
  198. package/dist/modules/eudr/components/StatementRiskSection.js +179 -0
  199. package/dist/modules/eudr/components/StatementRiskSection.js.map +7 -0
  200. package/dist/modules/eudr/components/crudErrorI18n.js +31 -0
  201. package/dist/modules/eudr/components/crudErrorI18n.js.map +7 -0
  202. package/dist/modules/eudr/components/formConfig.js +593 -0
  203. package/dist/modules/eudr/components/formConfig.js.map +7 -0
  204. package/dist/modules/eudr/components/plotForm.js +68 -0
  205. package/dist/modules/eudr/components/plotForm.js.map +7 -0
  206. package/dist/modules/eudr/data/enrichers.js +62 -0
  207. package/dist/modules/eudr/data/enrichers.js.map +7 -0
  208. package/dist/modules/eudr/data/entities.js +437 -0
  209. package/dist/modules/eudr/data/entities.js.map +7 -0
  210. package/dist/modules/eudr/data/validators.js +271 -0
  211. package/dist/modules/eudr/data/validators.js.map +7 -0
  212. package/dist/modules/eudr/encryption.js +33 -0
  213. package/dist/modules/eudr/encryption.js.map +7 -0
  214. package/dist/modules/eudr/events.js +38 -0
  215. package/dist/modules/eudr/events.js.map +7 -0
  216. package/dist/modules/eudr/index.js +15 -0
  217. package/dist/modules/eudr/index.js.map +7 -0
  218. package/dist/modules/eudr/lib/annual-report.js +104 -0
  219. package/dist/modules/eudr/lib/annual-report.js.map +7 -0
  220. package/dist/modules/eudr/lib/completeness.js +64 -0
  221. package/dist/modules/eudr/lib/completeness.js.map +7 -0
  222. package/dist/modules/eudr/lib/detail-read-scope.js +16 -0
  223. package/dist/modules/eudr/lib/detail-read-scope.js.map +7 -0
  224. package/dist/modules/eudr/lib/geometry.js +142 -0
  225. package/dist/modules/eudr/lib/geometry.js.map +7 -0
  226. package/dist/modules/eudr/lib/notifications.js +56 -0
  227. package/dist/modules/eudr/lib/notifications.js.map +7 -0
  228. package/dist/modules/eudr/lib/plural.js +26 -0
  229. package/dist/modules/eudr/lib/plural.js.map +7 -0
  230. package/dist/modules/eudr/lib/reference-data.js +240 -0
  231. package/dist/modules/eudr/lib/reference-data.js.map +7 -0
  232. package/dist/modules/eudr/lib/species.js +10 -0
  233. package/dist/modules/eudr/lib/species.js.map +7 -0
  234. package/dist/modules/eudr/lib/statement-lifecycle.js +84 -0
  235. package/dist/modules/eudr/lib/statement-lifecycle.js.map +7 -0
  236. package/dist/modules/eudr/lib/statement-seeding.js +49 -0
  237. package/dist/modules/eudr/lib/statement-seeding.js.map +7 -0
  238. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js +15 -0
  239. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js.map +7 -0
  240. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js +24 -0
  241. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js.map +7 -0
  242. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js +15 -0
  243. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js.map +7 -0
  244. package/dist/modules/eudr/notifications.js +58 -0
  245. package/dist/modules/eudr/notifications.js.map +7 -0
  246. package/dist/modules/eudr/search.js +186 -0
  247. package/dist/modules/eudr/search.js.map +7 -0
  248. package/dist/modules/eudr/setup.js +12 -0
  249. package/dist/modules/eudr/setup.js.map +7 -0
  250. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js +34 -0
  251. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js.map +7 -0
  252. package/dist/modules/eudr/subscribers/notify-risk-concluded.js +35 -0
  253. package/dist/modules/eudr/subscribers/notify-risk-concluded.js.map +7 -0
  254. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js +37 -0
  255. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js.map +7 -0
  256. package/dist/modules/eudr/subscribers/notify-statement-submitted.js +34 -0
  257. package/dist/modules/eudr/subscribers/notify-statement-submitted.js.map +7 -0
  258. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js +34 -0
  259. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js.map +7 -0
  260. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js +124 -0
  261. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js.map +7 -0
  262. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js +22 -0
  263. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js.map +7 -0
  264. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js +203 -0
  265. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js.map +7 -0
  266. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js +23 -0
  267. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js.map +7 -0
  268. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js +19 -0
  269. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js.map +7 -0
  270. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js +31 -0
  271. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js.map +7 -0
  272. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js +100 -0
  273. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js.map +7 -0
  274. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js +17 -0
  275. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js.map +7 -0
  276. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js +116 -0
  277. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js.map +7 -0
  278. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js +17 -0
  279. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js.map +7 -0
  280. package/dist/modules/eudr/widgets/injection-table.js +32 -0
  281. package/dist/modules/eudr/widgets/injection-table.js.map +7 -0
  282. package/dist/modules/notifications/lib/notificationService.js +67 -3
  283. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  284. package/dist/modules/query_index/lib/engine.js +121 -48
  285. package/dist/modules/query_index/lib/engine.js.map +2 -2
  286. package/dist/modules/sales/api/documents/factory.js +17 -0
  287. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  288. package/dist/modules/sales/commands/documents.js +32 -18
  289. package/dist/modules/sales/commands/documents.js.map +2 -2
  290. package/dist/modules/sales/data/entities.js +1 -0
  291. package/dist/modules/sales/data/entities.js.map +2 -2
  292. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js +17 -0
  293. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js.map +7 -0
  294. package/generated/entities/eudr_due_diligence_statement/index.ts +22 -0
  295. package/generated/entities/eudr_evidence_submission/index.ts +24 -0
  296. package/generated/entities/eudr_mitigation_action/index.ts +14 -0
  297. package/generated/entities/eudr_plot/index.ts +18 -0
  298. package/generated/entities/eudr_product_mapping/index.ts +14 -0
  299. package/generated/entities/eudr_risk_assessment/index.ts +16 -0
  300. package/generated/entities/role/index.ts +1 -0
  301. package/generated/entities.ids.generated.ts +9 -0
  302. package/generated/entity-fields-registry.ts +121 -0
  303. package/package.json +7 -7
  304. package/src/helpers/integration/api.ts +13 -0
  305. package/src/modules/attachments/fields/attachment.tsx +5 -0
  306. package/src/modules/attachments/lib/crud.ts +2 -0
  307. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +7 -0
  308. package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +9 -0
  309. package/src/modules/audit_logs/backend/audit-logs/page.tsx +4 -4
  310. package/src/modules/audit_logs/components/AuditLogsActions.tsx +20 -4
  311. package/src/modules/auth/api/login.ts +9 -2
  312. package/src/modules/auth/api/users/route.ts +8 -4
  313. package/src/modules/auth/backend/users/[id]/edit/page.tsx +17 -1
  314. package/src/modules/auth/backend/users/page.tsx +11 -0
  315. package/src/modules/auth/commands/users.ts +251 -85
  316. package/src/modules/auth/data/entities.ts +3 -0
  317. package/src/modules/auth/i18n/de.json +6 -0
  318. package/src/modules/auth/i18n/en.json +6 -0
  319. package/src/modules/auth/i18n/es.json +6 -0
  320. package/src/modules/auth/i18n/ko.json +6 -0
  321. package/src/modules/auth/i18n/pl.json +6 -0
  322. package/src/modules/auth/lib/sessionIntegrity.ts +1 -1
  323. package/src/modules/auth/lib/setup-app.ts +1 -1
  324. package/src/modules/auth/migrations/.snapshot-open-mercato.json +16 -0
  325. package/src/modules/auth/migrations/Migration20260728134212_auth.ts +14 -0
  326. package/src/modules/eudr/acl.ts +39 -0
  327. package/src/modules/eudr/ai-agents.ts +258 -0
  328. package/src/modules/eudr/ai-tools/compliance-pack.ts +618 -0
  329. package/src/modules/eudr/ai-tools/types.ts +44 -0
  330. package/src/modules/eudr/ai-tools.ts +15 -0
  331. package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +481 -0
  332. package/src/modules/eudr/api/evidence-submissions/route.ts +335 -0
  333. package/src/modules/eudr/api/mitigation-actions/route.ts +263 -0
  334. package/src/modules/eudr/api/openapi.ts +28 -0
  335. package/src/modules/eudr/api/plots/import/route.ts +369 -0
  336. package/src/modules/eudr/api/plots/route.ts +296 -0
  337. package/src/modules/eudr/api/product-mappings/route.ts +269 -0
  338. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +382 -0
  339. package/src/modules/eudr/api/product-mappings/suggestions/route.ts +169 -0
  340. package/src/modules/eudr/api/reports/annual/route.ts +457 -0
  341. package/src/modules/eudr/api/risk-assessments/route.ts +323 -0
  342. package/src/modules/eudr/api/statements/[id]/export/route.ts +713 -0
  343. package/src/modules/eudr/api/statements/[id]/readiness/route.ts +129 -0
  344. package/src/modules/eudr/api/statements/route.ts +329 -0
  345. package/src/modules/eudr/api/suppliers/compliance/route.ts +241 -0
  346. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.ts +12 -0
  347. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.tsx +545 -0
  348. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.ts +13 -0
  349. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.tsx +383 -0
  350. package/src/modules/eudr/backend/eudr/evidence-submissions/page.meta.ts +12 -0
  351. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +379 -0
  352. package/src/modules/eudr/backend/eudr/page.meta.ts +12 -0
  353. package/src/modules/eudr/backend/eudr/page.tsx +477 -0
  354. package/src/modules/eudr/backend/eudr/plots/[id]/page.meta.ts +12 -0
  355. package/src/modules/eudr/backend/eudr/plots/[id]/page.tsx +404 -0
  356. package/src/modules/eudr/backend/eudr/plots/create/page.meta.ts +13 -0
  357. package/src/modules/eudr/backend/eudr/plots/create/page.tsx +267 -0
  358. package/src/modules/eudr/backend/eudr/plots/page.meta.ts +12 -0
  359. package/src/modules/eudr/backend/eudr/plots/page.tsx +442 -0
  360. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.ts +12 -0
  361. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.tsx +311 -0
  362. package/src/modules/eudr/backend/eudr/product-mappings/create/page.meta.ts +13 -0
  363. package/src/modules/eudr/backend/eudr/product-mappings/create/page.tsx +190 -0
  364. package/src/modules/eudr/backend/eudr/product-mappings/page.meta.ts +12 -0
  365. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +344 -0
  366. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.ts +12 -0
  367. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.tsx +421 -0
  368. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.meta.ts +13 -0
  369. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.tsx +247 -0
  370. package/src/modules/eudr/backend/eudr/risk-assessments/page.meta.ts +12 -0
  371. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +383 -0
  372. package/src/modules/eudr/backend/eudr/statements/[id]/page.meta.ts +12 -0
  373. package/src/modules/eudr/backend/eudr/statements/[id]/page.tsx +777 -0
  374. package/src/modules/eudr/backend/eudr/statements/create/page.meta.ts +13 -0
  375. package/src/modules/eudr/backend/eudr/statements/create/page.tsx +511 -0
  376. package/src/modules/eudr/backend/eudr/statements/page.meta.ts +12 -0
  377. package/src/modules/eudr/backend/eudr/statements/page.tsx +372 -0
  378. package/src/modules/eudr/ce.ts +52 -0
  379. package/src/modules/eudr/commands/evidence-submissions.ts +699 -0
  380. package/src/modules/eudr/commands/index.ts +6 -0
  381. package/src/modules/eudr/commands/lifecycle-events.ts +28 -0
  382. package/src/modules/eudr/commands/mitigation-actions.ts +574 -0
  383. package/src/modules/eudr/commands/plots.ts +627 -0
  384. package/src/modules/eudr/commands/product-mappings.ts +556 -0
  385. package/src/modules/eudr/commands/risk-assessments.ts +728 -0
  386. package/src/modules/eudr/commands/statements.ts +933 -0
  387. package/src/modules/eudr/components/CountrySelectField.tsx +77 -0
  388. package/src/modules/eudr/components/GeometryInput.tsx +170 -0
  389. package/src/modules/eudr/components/MappingSuggestionsDialog.tsx +385 -0
  390. package/src/modules/eudr/components/MitigationActionsSection.tsx +417 -0
  391. package/src/modules/eudr/components/OrderSelectField.tsx +116 -0
  392. package/src/modules/eudr/components/PlotImportDialog.tsx +277 -0
  393. package/src/modules/eudr/components/PlotMapPreview.tsx +46 -0
  394. package/src/modules/eudr/components/PlotMapPreviewImpl.tsx +111 -0
  395. package/src/modules/eudr/components/PlotMultiSelectField.tsx +238 -0
  396. package/src/modules/eudr/components/RiskCriteriaField.tsx +173 -0
  397. package/src/modules/eudr/components/StatementLifecycleBar.tsx +363 -0
  398. package/src/modules/eudr/components/StatementReadinessChecklist.tsx +82 -0
  399. package/src/modules/eudr/components/StatementRiskSection.tsx +242 -0
  400. package/src/modules/eudr/components/crudErrorI18n.ts +36 -0
  401. package/src/modules/eudr/components/formConfig.tsx +717 -0
  402. package/src/modules/eudr/components/plotForm.ts +75 -0
  403. package/src/modules/eudr/data/enrichers.ts +82 -0
  404. package/src/modules/eudr/data/entities.ts +369 -0
  405. package/src/modules/eudr/data/validators.ts +357 -0
  406. package/src/modules/eudr/encryption.ts +31 -0
  407. package/src/modules/eudr/events.ts +38 -0
  408. package/src/modules/eudr/i18n/de.json +747 -0
  409. package/src/modules/eudr/i18n/en.json +747 -0
  410. package/src/modules/eudr/i18n/es.json +747 -0
  411. package/src/modules/eudr/i18n/ko.json +747 -0
  412. package/src/modules/eudr/i18n/pl.json +747 -0
  413. package/src/modules/eudr/index.ts +13 -0
  414. package/src/modules/eudr/lib/annual-report.ts +120 -0
  415. package/src/modules/eudr/lib/completeness.ts +90 -0
  416. package/src/modules/eudr/lib/detail-read-scope.ts +44 -0
  417. package/src/modules/eudr/lib/geometry.ts +191 -0
  418. package/src/modules/eudr/lib/notifications.ts +85 -0
  419. package/src/modules/eudr/lib/plural.ts +36 -0
  420. package/src/modules/eudr/lib/reference-data.ts +128 -0
  421. package/src/modules/eudr/lib/species.ts +12 -0
  422. package/src/modules/eudr/lib/statement-lifecycle.ts +109 -0
  423. package/src/modules/eudr/lib/statement-seeding.ts +86 -0
  424. package/src/modules/eudr/migrations/.snapshot-open-mercato.json +2046 -0
  425. package/src/modules/eudr/migrations/Migration20260706102002_eudr.ts +16 -0
  426. package/src/modules/eudr/migrations/Migration20260706152612_eudr.ts +30 -0
  427. package/src/modules/eudr/migrations/Migration20260722100000_eudr.ts +17 -0
  428. package/src/modules/eudr/notifications.ts +56 -0
  429. package/src/modules/eudr/search.ts +212 -0
  430. package/src/modules/eudr/setup.ts +10 -0
  431. package/src/modules/eudr/subscribers/notify-mitigation-completed.ts +56 -0
  432. package/src/modules/eudr/subscribers/notify-risk-concluded.ts +58 -0
  433. package/src/modules/eudr/subscribers/notify-statement-reference-issued.ts +59 -0
  434. package/src/modules/eudr/subscribers/notify-statement-submitted.ts +49 -0
  435. package/src/modules/eudr/subscribers/notify-statement-withdrawn.ts +49 -0
  436. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-created.ts +189 -0
  437. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.ts +31 -0
  438. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.client.tsx +271 -0
  439. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.ts +24 -0
  440. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.client.tsx +37 -0
  441. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.ts +30 -0
  442. package/src/modules/eudr/widgets/injection/order-compliance/widget.client.tsx +131 -0
  443. package/src/modules/eudr/widgets/injection/order-compliance/widget.ts +16 -0
  444. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.client.tsx +168 -0
  445. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.ts +16 -0
  446. package/src/modules/eudr/widgets/injection-table.ts +31 -0
  447. package/src/modules/notifications/lib/notificationService.ts +83 -4
  448. package/src/modules/query_index/lib/engine.ts +139 -51
  449. package/src/modules/sales/api/documents/factory.ts +29 -0
  450. package/src/modules/sales/commands/documents.ts +65 -18
  451. package/src/modules/sales/data/entities.ts +1 -0
  452. package/src/modules/sales/migrations/.snapshot-open-mercato.json +10 -0
  453. package/src/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.ts +59 -0
@@ -0,0 +1,13 @@
1
+ import type { ModuleInfo } from '@open-mercato/shared/modules/registry'
2
+
3
+ export const metadata: ModuleInfo = {
4
+ name: 'eudr',
5
+ title: 'EUDR Compliance',
6
+ version: '0.1.0',
7
+ description: 'EU Deforestation Regulation compliance: product commodity mappings, supplier origin evidence, due diligence statements.',
8
+ author: 'Open Mercato Team',
9
+ license: 'MIT',
10
+ ejectable: true,
11
+ }
12
+
13
+ export { features } from './acl'
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod'
2
+ import {
3
+ serializeExport,
4
+ type CrudExportColumn,
5
+ type PreparedExport,
6
+ } from '@open-mercato/shared/lib/crud/exporters'
7
+
8
+ const nonNegativeIntegerSchema = z.number().int().nonnegative()
9
+
10
+ const supplementaryQuantitySchema = z.object({
11
+ unit: z.string(),
12
+ quantity: z.string(),
13
+ })
14
+
15
+ const commodityReportSchema = z.object({
16
+ commodity: z.string(),
17
+ count: nonNegativeIntegerSchema,
18
+ quantityKg: z.string(),
19
+ supplementaryQuantities: z.array(supplementaryQuantitySchema),
20
+ })
21
+
22
+ export const annualReportSchema = z.object({
23
+ year: z.number().int(),
24
+ generatedAt: z.string().datetime(),
25
+ statements: z.object({
26
+ total: nonNegativeIntegerSchema,
27
+ byStatus: z.record(z.string(), nonNegativeIntegerSchema),
28
+ byCommodity: z.array(commodityReportSchema),
29
+ }),
30
+ countries: z.array(z.object({
31
+ country: z.string(),
32
+ tier: z.string(),
33
+ submissionCount: nonNegativeIntegerSchema,
34
+ })).optional(),
35
+ risk: z.object({
36
+ assessments: nonNegativeIntegerSchema,
37
+ negligible: nonNegativeIntegerSchema,
38
+ nonNegligible: nonNegativeIntegerSchema,
39
+ simplified: nonNegativeIntegerSchema,
40
+ }).optional(),
41
+ mitigation: z.object({
42
+ total: nonNegativeIntegerSchema,
43
+ completed: nonNegativeIntegerSchema,
44
+ }).optional(),
45
+ })
46
+
47
+ export type AnnualReport = z.infer<typeof annualReportSchema>
48
+
49
+ function parseThousandths(value: string | number | null): bigint | null {
50
+ if (value === null) return 0n
51
+ const normalized = typeof value === 'number' ? value.toFixed(3) : value.trim()
52
+ const match = /^([+-]?)(\d+)(?:\.(\d{1,3}))?$/.exec(normalized)
53
+ if (match === null) return null
54
+
55
+ const sign = match[1] === '-' ? -1n : 1n
56
+ const whole = BigInt(match[2]) * 1000n
57
+ const fraction = BigInt((match[3] ?? '').padEnd(3, '0') || '0')
58
+ return sign * (whole + fraction)
59
+ }
60
+
61
+ function formatThousandths(value: bigint): string {
62
+ const sign = value < 0n ? '-' : ''
63
+ const absolute = value < 0n ? -value : value
64
+ const whole = absolute / 1000n
65
+ const fraction = (absolute % 1000n).toString().padStart(3, '0')
66
+ return `${sign}${whole}.${fraction}`
67
+ }
68
+
69
+ export function accumulateSupplementaryQuantities(
70
+ rows: Array<{ unit: string | null; quantity: string | number | null }>,
71
+ ): Array<{ unit: string; quantity: string }> {
72
+ const quantities = new Map<string, bigint>()
73
+
74
+ for (const row of rows) {
75
+ const unit = row.unit?.trim().toUpperCase() ?? ''
76
+ if (unit.length === 0) continue
77
+ const quantity = parseThousandths(row.quantity)
78
+ if (quantity === null) continue
79
+ quantities.set(unit, (quantities.get(unit) ?? 0n) + quantity)
80
+ }
81
+
82
+ return Array.from(quantities, ([unit, quantity]) => ({
83
+ unit,
84
+ quantity: formatThousandths(quantity),
85
+ }))
86
+ }
87
+
88
+ const annualReportCsvColumns: CrudExportColumn[] = [
89
+ { field: 'commodity', header: 'commodity' },
90
+ { field: 'count', header: 'count' },
91
+ { field: 'quantityKg', header: 'quantityKg' },
92
+ { field: 'supplementaryUnit', header: 'supplementaryUnit' },
93
+ { field: 'supplementaryQuantity', header: 'supplementaryQuantity' },
94
+ ]
95
+
96
+ export function buildAnnualReportCsv(report: AnnualReport): { content: string; contentType: string } {
97
+ const rows: PreparedExport['rows'] = report.statements.byCommodity.flatMap((commodity) => {
98
+ const supplementaryQuantities = commodity.supplementaryQuantities.length > 0
99
+ ? commodity.supplementaryQuantities
100
+ : [null]
101
+
102
+ return supplementaryQuantities.map((supplementaryQuantity) => ({
103
+ commodity: commodity.commodity,
104
+ count: commodity.count,
105
+ quantityKg: commodity.quantityKg,
106
+ supplementaryUnit: supplementaryQuantity?.unit ?? '',
107
+ supplementaryQuantity: supplementaryQuantity?.quantity ?? '',
108
+ }))
109
+ })
110
+ const prepared: PreparedExport = {
111
+ columns: annualReportCsvColumns,
112
+ rows,
113
+ }
114
+ const serialized = serializeExport(prepared, 'csv')
115
+
116
+ return {
117
+ content: serialized.body,
118
+ contentType: serialized.contentType,
119
+ }
120
+ }
@@ -0,0 +1,90 @@
1
+ import { GEOJSON_TYPES } from '../data/validators'
2
+
3
+ export const COMPLETENESS_DIMENSIONS = ['origin_country', 'geolocation', 'quantity', 'harvest_period', 'producer', 'documents'] as const
4
+ export const REQUIRED_DIMENSIONS = COMPLETENESS_DIMENSIONS
5
+ export type CompletenessDimension = (typeof COMPLETENESS_DIMENSIONS)[number]
6
+
7
+ export type CompletenessInput = {
8
+ originCountry?: string | null
9
+ geolocation?: unknown
10
+ quantityKg?: string | number | null
11
+ harvestFrom?: Date | string | null
12
+ harvestTo?: Date | string | null
13
+ producerName?: string | null
14
+ attachmentIds?: string[] | null
15
+ }
16
+
17
+ export type CompletenessContext = {
18
+ linkedAttachmentCount?: number
19
+ activePlotCount?: number
20
+ }
21
+
22
+ export type CompletenessResult = { score: number; missingFields: CompletenessDimension[] }
23
+
24
+ function isOriginCountryComplete(value: string | null | undefined): boolean {
25
+ return typeof value === 'string' && /^[A-Za-z]{2}$/.test(value.trim())
26
+ }
27
+
28
+ function isGeolocationComplete(value: unknown): boolean {
29
+ if (value === null || typeof value !== 'object') return false
30
+ const candidate = value as { type?: unknown }
31
+ return typeof candidate.type === 'string' && GEOJSON_TYPES.includes(candidate.type as (typeof GEOJSON_TYPES)[number])
32
+ }
33
+
34
+ function isQuantityComplete(value: string | number | null | undefined): boolean {
35
+ if (typeof value !== 'string' && typeof value !== 'number') return false
36
+ const quantity = Number(value)
37
+ return Number.isFinite(quantity) && quantity > 0
38
+ }
39
+
40
+ function parseDate(value: Date | string | null | undefined): Date | null {
41
+ if (value == null) return null
42
+ const date = value instanceof Date ? value : new Date(value)
43
+ return Number.isNaN(date.getTime()) ? null : date
44
+ }
45
+
46
+ function isHarvestPeriodComplete(harvestFrom: Date | string | null | undefined, harvestTo: Date | string | null | undefined): boolean {
47
+ const parsedHarvestFrom = parseDate(harvestFrom)
48
+ const parsedHarvestTo = parseDate(harvestTo)
49
+ return parsedHarvestFrom !== null && parsedHarvestTo !== null && parsedHarvestFrom <= parsedHarvestTo
50
+ }
51
+
52
+ function isProducerComplete(value: string | null | undefined): boolean {
53
+ return typeof value === 'string' && value.trim().length > 0
54
+ }
55
+
56
+ function isDocumentsComplete(value: string[] | null | undefined): boolean {
57
+ return Array.isArray(value) && value.length >= 1
58
+ }
59
+
60
+ export function computeCompleteness(input: CompletenessInput, context: CompletenessContext = {}): CompletenessResult {
61
+ const completionByDimension: Record<CompletenessDimension, boolean> = {
62
+ origin_country: isOriginCountryComplete(input.originCountry),
63
+ geolocation: isGeolocationComplete(input.geolocation) || (context.activePlotCount ?? 0) > 0,
64
+ quantity: isQuantityComplete(input.quantityKg),
65
+ harvest_period: isHarvestPeriodComplete(input.harvestFrom, input.harvestTo),
66
+ producer: isProducerComplete(input.producerName),
67
+ documents: isDocumentsComplete(input.attachmentIds) || (context.linkedAttachmentCount ?? 0) > 0,
68
+ }
69
+
70
+ const missingFields = COMPLETENESS_DIMENSIONS.filter((dimension) => !completionByDimension[dimension])
71
+ const metCount = COMPLETENESS_DIMENSIONS.length - missingFields.length
72
+ const score = Math.round((metCount / COMPLETENESS_DIMENSIONS.length) * 100)
73
+
74
+ return { score, missingFields }
75
+ }
76
+
77
+ export function computeSubmissionCompleteness(input: CompletenessInput, context?: CompletenessContext): CompletenessResult {
78
+ return computeCompleteness(input, context)
79
+ }
80
+
81
+ export const HARVEST_CUTOFF_DATE = new Date('2020-12-31T23:59:59.999Z')
82
+
83
+ export function computeHarvestCutoffWarning(
84
+ harvestFrom: Date | string | null | undefined,
85
+ harvestTo: Date | string | null | undefined,
86
+ ): 'harvest_before_cutoff' | null {
87
+ const windowEnd = parseDate(harvestTo) ?? parseDate(harvestFrom)
88
+ if (!windowEnd) return null
89
+ return windowEnd.getTime() <= HARVEST_CUTOFF_DATE.getTime() ? 'harvest_before_cutoff' : null
90
+ }
@@ -0,0 +1,44 @@
1
+ export type DetailReadScope = {
2
+ tenantId: string
3
+ /** Matches the organization scope the list query itself used. */
4
+ organizationFilter: string | { $in: string[] }
5
+ /**
6
+ * Fallback organization for decryption only. `decryptEntitiesWithFallbackScope`
7
+ * prefers each record's own tenant/organization and consults this only when the
8
+ * record carries none, so a single value stays correct for multi-org reads.
9
+ */
10
+ decryptionOrganizationId: string | null
11
+ }
12
+
13
+ type DetailReadScopeContext = {
14
+ auth?: { tenantId?: string | null; orgId?: string | null } | null
15
+ organizationIds?: string[] | null
16
+ selectedOrganizationId?: string | null
17
+ }
18
+
19
+ /**
20
+ * Resolves the scope for an `afterList` detail re-fetch (the decryption pass).
21
+ *
22
+ * The list query is scoped by the full `organizationIds` array, so re-fetching by
23
+ * a single organization silently drops decrypted fields for a multi-org user who
24
+ * has not selected one. Returns null when no scope can be established, so callers
25
+ * fail closed rather than reading across the tenant.
26
+ */
27
+ export function resolveDetailReadScope(ctx: DetailReadScopeContext): DetailReadScope | null {
28
+ const tenantId = ctx.auth?.tenantId ?? null
29
+ if (!tenantId) return null
30
+
31
+ const scopedOrganizationIds = Array.isArray(ctx.organizationIds) && ctx.organizationIds.length
32
+ ? Array.from(new Set(ctx.organizationIds))
33
+ : null
34
+ const singleOrganizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
35
+ if (!scopedOrganizationIds && !singleOrganizationId) return null
36
+
37
+ return {
38
+ tenantId,
39
+ organizationFilter: scopedOrganizationIds
40
+ ? { $in: scopedOrganizationIds }
41
+ : (singleOrganizationId as string),
42
+ decryptionOrganizationId: singleOrganizationId,
43
+ }
44
+ }
@@ -0,0 +1,191 @@
1
+ export type EudrPlotGeometryType = 'point' | 'polygon'
2
+
3
+ type GeoJsonPosition = [number, number]
4
+ type GeoJsonPoint = { type: 'Point'; coordinates: GeoJsonPosition }
5
+ type GeoJsonPolygon = { type: 'Polygon'; coordinates: GeoJsonPosition[][] }
6
+ type GeoJsonMultiPolygon = { type: 'MultiPolygon'; coordinates: GeoJsonPosition[][][] }
7
+ type GeoJsonSupportedGeometry = GeoJsonPoint | GeoJsonPolygon | GeoJsonMultiPolygon
8
+
9
+ export type GeoJsonFeature = {
10
+ type: 'Feature'
11
+ properties: Record<string, unknown>
12
+ geometry: GeoJsonSupportedGeometry
13
+ }
14
+
15
+ export type GeometryValidationResult =
16
+ | { ok: true; feature: GeoJsonFeature; plotType: EudrPlotGeometryType; computedAreaHa: number | null; warnings: string[] }
17
+ | { ok: false; errorKey: string }
18
+
19
+ type PositionValidationResult =
20
+ | { ok: true; position: GeoJsonPosition; lowPrecision: boolean }
21
+ | { ok: false; errorKey: 'geometryOutOfRange' }
22
+
23
+ const MAX_GEOMETRY_SIZE = 262_144
24
+ const MAX_IMPORT_SIZE = 1_048_576
25
+ const MAX_FEATURE_COLLECTION_FEATURES = 500
26
+ const EARTH_RADIUS_METERS = 6_371_008.8
27
+
28
+ export const POINT_MAX_AREA_HA = 4
29
+
30
+ function isRecord(value: unknown): value is Record<string, unknown> {
31
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
32
+ }
33
+
34
+ function serializedLength(value: unknown): number | null {
35
+ try {
36
+ const serialized = JSON.stringify(value)
37
+ return typeof serialized === 'string' ? serialized.length : null
38
+ } catch {
39
+ return null
40
+ }
41
+ }
42
+
43
+ function decimalPlaces(value: number): number {
44
+ const decimal = String(value).split(/[eE]/)[0]?.split('.')[1]
45
+ return decimal?.length ?? 0
46
+ }
47
+
48
+ function validatePosition(input: unknown): PositionValidationResult {
49
+ if (!Array.isArray(input) || input.length < 2) return { ok: false, errorKey: 'geometryOutOfRange' }
50
+ const [longitude, latitude] = input
51
+ if (typeof longitude !== 'number' || typeof latitude !== 'number') return { ok: false, errorKey: 'geometryOutOfRange' }
52
+ if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return { ok: false, errorKey: 'geometryOutOfRange' }
53
+ if (longitude < -180 || longitude > 180 || latitude < -90 || latitude > 90) return { ok: false, errorKey: 'geometryOutOfRange' }
54
+
55
+ return {
56
+ ok: true,
57
+ position: [longitude, latitude],
58
+ lowPrecision: decimalPlaces(longitude) < 6 || decimalPlaces(latitude) < 6,
59
+ }
60
+ }
61
+
62
+ function positionsEqual(left: GeoJsonPosition, right: GeoJsonPosition): boolean {
63
+ return left[0] === right[0] && left[1] === right[1]
64
+ }
65
+
66
+ function validateRing(input: unknown): { ok: true; ring: GeoJsonPosition[]; lowPrecision: boolean } | { ok: false; errorKey: string } {
67
+ if (!Array.isArray(input)) return { ok: false, errorKey: 'geometryInvalid' }
68
+ if (input.length < 4) return { ok: false, errorKey: 'geometryRingNotClosed' }
69
+
70
+ const ring: GeoJsonPosition[] = []
71
+ let lowPrecision = false
72
+ for (const candidate of input) {
73
+ const result = validatePosition(candidate)
74
+ if (!result.ok) return result
75
+ ring.push(result.position)
76
+ lowPrecision = lowPrecision || result.lowPrecision
77
+ }
78
+
79
+ if (!positionsEqual(ring[0], ring[ring.length - 1])) return { ok: false, errorKey: 'geometryRingNotClosed' }
80
+ return { ok: true, ring, lowPrecision }
81
+ }
82
+
83
+ function validatePolygonCoordinates(input: unknown): { ok: true; rings: GeoJsonPosition[][]; lowPrecision: boolean } | { ok: false; errorKey: string } {
84
+ if (!Array.isArray(input) || input.length === 0) return { ok: false, errorKey: 'geometryInvalid' }
85
+
86
+ const rings: GeoJsonPosition[][] = []
87
+ let lowPrecision = false
88
+ for (const candidate of input) {
89
+ const result = validateRing(candidate)
90
+ if (!result.ok) return result
91
+ rings.push(result.ring)
92
+ lowPrecision = lowPrecision || result.lowPrecision
93
+ }
94
+
95
+ return { ok: true, rings, lowPrecision }
96
+ }
97
+
98
+ function validateGeometry(input: unknown): { ok: true; geometry: GeoJsonSupportedGeometry; lowPrecision: boolean } | { ok: false; errorKey: string } {
99
+ if (!isRecord(input) || typeof input.type !== 'string') return { ok: false, errorKey: 'geometryInvalid' }
100
+
101
+ if (input.type === 'Point') {
102
+ const result = validatePosition(input.coordinates)
103
+ if (!result.ok) return result
104
+ return { ok: true, geometry: { type: 'Point', coordinates: result.position }, lowPrecision: result.lowPrecision }
105
+ }
106
+
107
+ if (input.type === 'Polygon') {
108
+ const result = validatePolygonCoordinates(input.coordinates)
109
+ if (!result.ok) return result
110
+ return { ok: true, geometry: { type: 'Polygon', coordinates: result.rings }, lowPrecision: result.lowPrecision }
111
+ }
112
+
113
+ if (input.type === 'MultiPolygon') {
114
+ if (!Array.isArray(input.coordinates) || input.coordinates.length === 0) return { ok: false, errorKey: 'geometryInvalid' }
115
+ const polygons: GeoJsonPosition[][][] = []
116
+ let lowPrecision = false
117
+ for (const candidate of input.coordinates) {
118
+ const result = validatePolygonCoordinates(candidate)
119
+ if (!result.ok) return result
120
+ polygons.push(result.rings)
121
+ lowPrecision = lowPrecision || result.lowPrecision
122
+ }
123
+ return { ok: true, geometry: { type: 'MultiPolygon', coordinates: polygons }, lowPrecision }
124
+ }
125
+
126
+ return { ok: false, errorKey: 'geometryInvalid' }
127
+ }
128
+
129
+ function toRadians(value: number): number {
130
+ return (value * Math.PI) / 180
131
+ }
132
+
133
+ function ringAreaSquareMeters(ring: GeoJsonPosition[]): number {
134
+ let total = 0
135
+ for (let index = 0; index < ring.length - 1; index += 1) {
136
+ const [longitude1, latitude1] = ring[index]
137
+ const [longitude2, latitude2] = ring[index + 1]
138
+ total += (toRadians(longitude2) - toRadians(longitude1)) * (2 + Math.sin(toRadians(latitude1)) + Math.sin(toRadians(latitude2)))
139
+ }
140
+ return Math.abs((total * EARTH_RADIUS_METERS * EARTH_RADIUS_METERS) / 2)
141
+ }
142
+
143
+ function polygonAreaSquareMeters(rings: GeoJsonPosition[][]): number {
144
+ const [exterior, ...holes] = rings
145
+ const holesArea = holes.reduce((sum, ring) => sum + ringAreaSquareMeters(ring), 0)
146
+ return Math.max(0, ringAreaSquareMeters(exterior) - holesArea)
147
+ }
148
+
149
+ function areaHa(geometry: GeoJsonPolygon | GeoJsonMultiPolygon): number {
150
+ // Spherical-excess area is approximate, but is within about 0.5% at normal
151
+ // plot scale and sufficient for the 4-ha EUDR threshold hinting flow.
152
+ const squareMeters = geometry.type === 'Polygon'
153
+ ? polygonAreaSquareMeters(geometry.coordinates)
154
+ : geometry.coordinates.reduce((sum, polygon) => sum + polygonAreaSquareMeters(polygon), 0)
155
+
156
+ return Math.round((squareMeters / 10_000) * 10_000) / 10_000
157
+ }
158
+
159
+ export function validatePlotGeometry(input: unknown): GeometryValidationResult {
160
+ const length = serializedLength(input)
161
+ if (length === null) return { ok: false, errorKey: 'geometryInvalid' }
162
+ if (length > MAX_GEOMETRY_SIZE) return { ok: false, errorKey: 'geometryTooLarge' }
163
+
164
+ const isFeature = isRecord(input) && input.type === 'Feature'
165
+ const geometryInput = isFeature ? input.geometry : input
166
+ const properties = isFeature && isRecord(input.properties) ? input.properties : {}
167
+ const result = validateGeometry(geometryInput)
168
+ if (!result.ok) return result
169
+
170
+ const plotType: EudrPlotGeometryType = result.geometry.type === 'Point' ? 'point' : 'polygon'
171
+ return {
172
+ ok: true,
173
+ feature: { type: 'Feature', properties, geometry: result.geometry },
174
+ plotType,
175
+ computedAreaHa: result.geometry.type === 'Point' ? null : areaHa(result.geometry),
176
+ warnings: result.lowPrecision ? ['low_precision'] : [],
177
+ }
178
+ }
179
+
180
+ export function collectFeatures(featureCollection: unknown): { ok: true; features: unknown[] } | { ok: false; errorKey: string } {
181
+ const length = serializedLength(featureCollection)
182
+ if (length === null) return { ok: false, errorKey: 'geometryInvalid' }
183
+ if (length > MAX_IMPORT_SIZE) return { ok: false, errorKey: 'importTooLarge' }
184
+
185
+ if (!isRecord(featureCollection) || featureCollection.type !== 'FeatureCollection' || !Array.isArray(featureCollection.features)) {
186
+ return { ok: false, errorKey: 'geometryInvalid' }
187
+ }
188
+
189
+ if (featureCollection.features.length > MAX_FEATURE_COLLECTION_FEATURES) return { ok: false, errorKey: 'importTooManyFeatures' }
190
+ return { ok: true, features: featureCollection.features }
191
+ }
@@ -0,0 +1,85 @@
1
+ import { createLogger } from '@open-mercato/shared/lib/logger'
2
+ import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
3
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
4
+ import { notificationTypes } from '../notifications'
5
+
6
+ const logger = createLogger('eudr').child({ component: 'notifications' })
7
+
8
+ export type EudrNotificationTypeId =
9
+ | 'eudr.statement.submitted'
10
+ | 'eudr.statement.reference_issued'
11
+ | 'eudr.statement.withdrawn'
12
+ | 'eudr.risk.non_negligible'
13
+ | 'eudr.mitigation.completed'
14
+
15
+ export type EudrNotificationContainer = {
16
+ resolve: (name: string) => unknown
17
+ }
18
+
19
+ type EudrNotificationPayload = {
20
+ tenantId: string
21
+ organizationId: string | null
22
+ entityId: string
23
+ occurredAt: string
24
+ bodyVariables: Record<string, string>
25
+ linkHref: string
26
+ sourceEntityType: string
27
+ }
28
+
29
+ type DeliverEudrNotificationInput = {
30
+ container: EudrNotificationContainer
31
+ typeId: EudrNotificationTypeId
32
+ payload: EudrNotificationPayload
33
+ }
34
+
35
+ const REQUIRED_FEATURE_BY_TYPE: Record<EudrNotificationTypeId, string> = {
36
+ 'eudr.statement.submitted': 'eudr.statements.manage',
37
+ 'eudr.statement.reference_issued': 'eudr.statements.manage',
38
+ 'eudr.statement.withdrawn': 'eudr.statements.manage',
39
+ 'eudr.risk.non_negligible': 'eudr.risk.manage',
40
+ 'eudr.mitigation.completed': 'eudr.risk.manage',
41
+ }
42
+
43
+ export async function deliverEudrNotification({
44
+ container,
45
+ typeId,
46
+ payload,
47
+ }: DeliverEudrNotificationInput): Promise<void> {
48
+ let notificationService: ReturnType<typeof resolveNotificationService>
49
+ try {
50
+ notificationService = resolveNotificationService(container)
51
+ } catch (err) {
52
+ logger.debug('Notification service unavailable; skipping EUDR notification', {
53
+ typeId,
54
+ entityId: payload.entityId,
55
+ err,
56
+ })
57
+ return
58
+ }
59
+
60
+ const typeDef = notificationTypes.find((candidate) => candidate.type === typeId)
61
+ if (!typeDef) {
62
+ throw new Error(`[internal] EUDR notification type is not registered: ${typeId}`)
63
+ }
64
+
65
+ const requiredFeature = REQUIRED_FEATURE_BY_TYPE[typeId]
66
+ const content = buildFeatureNotificationFromType(typeDef, {
67
+ requiredFeature,
68
+ bodyVariables: payload.bodyVariables,
69
+ sourceEntityType: payload.sourceEntityType,
70
+ sourceEntityId: payload.entityId,
71
+ linkHref: payload.linkHref,
72
+ groupKey: `${typeId}:${payload.entityId}:${payload.occurredAt}`,
73
+ })
74
+
75
+ await notificationService.createForFeature(
76
+ {
77
+ ...content,
78
+ restrictRecipientsToOrganization: true,
79
+ },
80
+ {
81
+ tenantId: payload.tenantId,
82
+ organizationId: payload.organizationId,
83
+ },
84
+ )
85
+ }
@@ -0,0 +1,36 @@
1
+ type Translator = (
2
+ key: string,
3
+ fallbackOrParams?: string | Record<string, string | number>,
4
+ params?: Record<string, string | number>,
5
+ ) => string
6
+
7
+ const pluralRulesCache = new Map<string, Intl.PluralRules>()
8
+
9
+ function pluralRulesFor(locale: string): Intl.PluralRules {
10
+ const cached = pluralRulesCache.get(locale)
11
+ if (cached) return cached
12
+ let rules: Intl.PluralRules
13
+ try {
14
+ rules = new Intl.PluralRules(locale)
15
+ } catch {
16
+ rules = new Intl.PluralRules('en')
17
+ }
18
+ pluralRulesCache.set(locale, rules)
19
+ return rules
20
+ }
21
+
22
+ export function translatePlural(
23
+ translate: Translator,
24
+ locale: string,
25
+ baseKey: string,
26
+ count: number,
27
+ params?: Record<string, string | number>,
28
+ ): string {
29
+ const category = pluralRulesFor(locale).select(count)
30
+ const mergedParams = { count, ...(params ?? {}) }
31
+ for (const key of [`${baseKey}.${category}`, `${baseKey}.other`, baseKey]) {
32
+ const result = translate(key, mergedParams)
33
+ if (result !== key) return result
34
+ }
35
+ return translate(baseKey, mergedParams)
36
+ }