@open-mercato/core 0.6.8-develop.6925.1.cefa200fa4 → 0.6.8-develop.6931.1.4a1e1c786b

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 (405) 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.ids.generated.js +9 -0
  15. package/dist/generated/entities.ids.generated.js.map +2 -2
  16. package/dist/generated/entity-fields-registry.js +120 -0
  17. package/dist/generated/entity-fields-registry.js.map +2 -2
  18. package/dist/helpers/integration/ui.js +3 -0
  19. package/dist/helpers/integration/ui.js.map +2 -2
  20. package/dist/modules/attachments/fields/attachment.js +5 -1
  21. package/dist/modules/attachments/fields/attachment.js.map +2 -2
  22. package/dist/modules/attachments/lib/crud.js +3 -1
  23. package/dist/modules/attachments/lib/crud.js.map +2 -2
  24. package/dist/modules/eudr/acl.js +43 -0
  25. package/dist/modules/eudr/acl.js.map +7 -0
  26. package/dist/modules/eudr/ai-agents.js +205 -0
  27. package/dist/modules/eudr/ai-agents.js.map +7 -0
  28. package/dist/modules/eudr/ai-tools/compliance-pack.js +495 -0
  29. package/dist/modules/eudr/ai-tools/compliance-pack.js.map +7 -0
  30. package/dist/modules/eudr/ai-tools/types.js +10 -0
  31. package/dist/modules/eudr/ai-tools/types.js.map +7 -0
  32. package/dist/modules/eudr/ai-tools.js +10 -0
  33. package/dist/modules/eudr/ai-tools.js.map +7 -0
  34. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +386 -0
  35. package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +7 -0
  36. package/dist/modules/eudr/api/evidence-submissions/route.js +318 -0
  37. package/dist/modules/eudr/api/evidence-submissions/route.js.map +7 -0
  38. package/dist/modules/eudr/api/mitigation-actions/route.js +249 -0
  39. package/dist/modules/eudr/api/mitigation-actions/route.js.map +7 -0
  40. package/dist/modules/eudr/api/openapi.js +27 -0
  41. package/dist/modules/eudr/api/openapi.js.map +7 -0
  42. package/dist/modules/eudr/api/plots/import/route.js +294 -0
  43. package/dist/modules/eudr/api/plots/import/route.js.map +7 -0
  44. package/dist/modules/eudr/api/plots/route.js +279 -0
  45. package/dist/modules/eudr/api/plots/route.js.map +7 -0
  46. package/dist/modules/eudr/api/product-mappings/route.js +257 -0
  47. package/dist/modules/eudr/api/product-mappings/route.js.map +7 -0
  48. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +298 -0
  49. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +7 -0
  50. package/dist/modules/eudr/api/product-mappings/suggestions/route.js +132 -0
  51. package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +7 -0
  52. package/dist/modules/eudr/api/reports/annual/route.js +266 -0
  53. package/dist/modules/eudr/api/reports/annual/route.js.map +7 -0
  54. package/dist/modules/eudr/api/risk-assessments/route.js +300 -0
  55. package/dist/modules/eudr/api/risk-assessments/route.js.map +7 -0
  56. package/dist/modules/eudr/api/statements/[id]/export/route.js +631 -0
  57. package/dist/modules/eudr/api/statements/[id]/export/route.js.map +7 -0
  58. package/dist/modules/eudr/api/statements/[id]/readiness/route.js +111 -0
  59. package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +7 -0
  60. package/dist/modules/eudr/api/statements/route.js +314 -0
  61. package/dist/modules/eudr/api/statements/route.js.map +7 -0
  62. package/dist/modules/eudr/api/suppliers/compliance/route.js +173 -0
  63. package/dist/modules/eudr/api/suppliers/compliance/route.js.map +7 -0
  64. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js +468 -0
  65. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js.map +7 -0
  66. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js +16 -0
  67. package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js.map +7 -0
  68. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js +351 -0
  69. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js.map +7 -0
  70. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js +17 -0
  71. package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js.map +7 -0
  72. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +313 -0
  73. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +7 -0
  74. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js +16 -0
  75. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js.map +7 -0
  76. package/dist/modules/eudr/backend/eudr/page.js +360 -0
  77. package/dist/modules/eudr/backend/eudr/page.js.map +7 -0
  78. package/dist/modules/eudr/backend/eudr/page.meta.js +16 -0
  79. package/dist/modules/eudr/backend/eudr/page.meta.js.map +7 -0
  80. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js +346 -0
  81. package/dist/modules/eudr/backend/eudr/plots/[id]/page.js.map +7 -0
  82. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js +16 -0
  83. package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js.map +7 -0
  84. package/dist/modules/eudr/backend/eudr/plots/create/page.js +253 -0
  85. package/dist/modules/eudr/backend/eudr/plots/create/page.js.map +7 -0
  86. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js +17 -0
  87. package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js.map +7 -0
  88. package/dist/modules/eudr/backend/eudr/plots/page.js +386 -0
  89. package/dist/modules/eudr/backend/eudr/plots/page.js.map +7 -0
  90. package/dist/modules/eudr/backend/eudr/plots/page.meta.js +16 -0
  91. package/dist/modules/eudr/backend/eudr/plots/page.meta.js.map +7 -0
  92. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js +253 -0
  93. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js.map +7 -0
  94. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js +16 -0
  95. package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js.map +7 -0
  96. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js +174 -0
  97. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js.map +7 -0
  98. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js +17 -0
  99. package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js.map +7 -0
  100. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +306 -0
  101. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +7 -0
  102. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js +16 -0
  103. package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js.map +7 -0
  104. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js +335 -0
  105. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js.map +7 -0
  106. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js +16 -0
  107. package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js.map +7 -0
  108. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js +220 -0
  109. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js.map +7 -0
  110. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js +17 -0
  111. package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js.map +7 -0
  112. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +324 -0
  113. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +7 -0
  114. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js +16 -0
  115. package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js.map +7 -0
  116. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js +648 -0
  117. package/dist/modules/eudr/backend/eudr/statements/[id]/page.js.map +7 -0
  118. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js +16 -0
  119. package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js.map +7 -0
  120. package/dist/modules/eudr/backend/eudr/statements/create/page.js +432 -0
  121. package/dist/modules/eudr/backend/eudr/statements/create/page.js.map +7 -0
  122. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js +17 -0
  123. package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js.map +7 -0
  124. package/dist/modules/eudr/backend/eudr/statements/page.js +309 -0
  125. package/dist/modules/eudr/backend/eudr/statements/page.js.map +7 -0
  126. package/dist/modules/eudr/backend/eudr/statements/page.meta.js +16 -0
  127. package/dist/modules/eudr/backend/eudr/statements/page.meta.js.map +7 -0
  128. package/dist/modules/eudr/ce.js +56 -0
  129. package/dist/modules/eudr/ce.js.map +7 -0
  130. package/dist/modules/eudr/commands/evidence-submissions.js +571 -0
  131. package/dist/modules/eudr/commands/evidence-submissions.js.map +7 -0
  132. package/dist/modules/eudr/commands/index.js +7 -0
  133. package/dist/modules/eudr/commands/index.js.map +7 -0
  134. package/dist/modules/eudr/commands/lifecycle-events.js +19 -0
  135. package/dist/modules/eudr/commands/lifecycle-events.js.map +7 -0
  136. package/dist/modules/eudr/commands/mitigation-actions.js +488 -0
  137. package/dist/modules/eudr/commands/mitigation-actions.js.map +7 -0
  138. package/dist/modules/eudr/commands/plots.js +517 -0
  139. package/dist/modules/eudr/commands/plots.js.map +7 -0
  140. package/dist/modules/eudr/commands/product-mappings.js +468 -0
  141. package/dist/modules/eudr/commands/product-mappings.js.map +7 -0
  142. package/dist/modules/eudr/commands/risk-assessments.js +599 -0
  143. package/dist/modules/eudr/commands/risk-assessments.js.map +7 -0
  144. package/dist/modules/eudr/commands/statements.js +747 -0
  145. package/dist/modules/eudr/commands/statements.js.map +7 -0
  146. package/dist/modules/eudr/components/CountrySelectField.js +64 -0
  147. package/dist/modules/eudr/components/CountrySelectField.js.map +7 -0
  148. package/dist/modules/eudr/components/GeometryInput.js +133 -0
  149. package/dist/modules/eudr/components/GeometryInput.js.map +7 -0
  150. package/dist/modules/eudr/components/MappingSuggestionsDialog.js +307 -0
  151. package/dist/modules/eudr/components/MappingSuggestionsDialog.js.map +7 -0
  152. package/dist/modules/eudr/components/MitigationActionsSection.js +345 -0
  153. package/dist/modules/eudr/components/MitigationActionsSection.js.map +7 -0
  154. package/dist/modules/eudr/components/OrderSelectField.js +88 -0
  155. package/dist/modules/eudr/components/OrderSelectField.js.map +7 -0
  156. package/dist/modules/eudr/components/PlotImportDialog.js +229 -0
  157. package/dist/modules/eudr/components/PlotImportDialog.js.map +7 -0
  158. package/dist/modules/eudr/components/PlotMapPreview.js +35 -0
  159. package/dist/modules/eudr/components/PlotMapPreview.js.map +7 -0
  160. package/dist/modules/eudr/components/PlotMapPreviewImpl.js +97 -0
  161. package/dist/modules/eudr/components/PlotMapPreviewImpl.js.map +7 -0
  162. package/dist/modules/eudr/components/PlotMultiSelectField.js +198 -0
  163. package/dist/modules/eudr/components/PlotMultiSelectField.js.map +7 -0
  164. package/dist/modules/eudr/components/RiskCriteriaField.js +128 -0
  165. package/dist/modules/eudr/components/RiskCriteriaField.js.map +7 -0
  166. package/dist/modules/eudr/components/StatementLifecycleBar.js +274 -0
  167. package/dist/modules/eudr/components/StatementLifecycleBar.js.map +7 -0
  168. package/dist/modules/eudr/components/StatementReadinessChecklist.js +56 -0
  169. package/dist/modules/eudr/components/StatementReadinessChecklist.js.map +7 -0
  170. package/dist/modules/eudr/components/StatementRiskSection.js +179 -0
  171. package/dist/modules/eudr/components/StatementRiskSection.js.map +7 -0
  172. package/dist/modules/eudr/components/crudErrorI18n.js +31 -0
  173. package/dist/modules/eudr/components/crudErrorI18n.js.map +7 -0
  174. package/dist/modules/eudr/components/formConfig.js +593 -0
  175. package/dist/modules/eudr/components/formConfig.js.map +7 -0
  176. package/dist/modules/eudr/components/plotForm.js +68 -0
  177. package/dist/modules/eudr/components/plotForm.js.map +7 -0
  178. package/dist/modules/eudr/data/enrichers.js +62 -0
  179. package/dist/modules/eudr/data/enrichers.js.map +7 -0
  180. package/dist/modules/eudr/data/entities.js +437 -0
  181. package/dist/modules/eudr/data/entities.js.map +7 -0
  182. package/dist/modules/eudr/data/validators.js +271 -0
  183. package/dist/modules/eudr/data/validators.js.map +7 -0
  184. package/dist/modules/eudr/encryption.js +33 -0
  185. package/dist/modules/eudr/encryption.js.map +7 -0
  186. package/dist/modules/eudr/events.js +38 -0
  187. package/dist/modules/eudr/events.js.map +7 -0
  188. package/dist/modules/eudr/index.js +15 -0
  189. package/dist/modules/eudr/index.js.map +7 -0
  190. package/dist/modules/eudr/lib/annual-report.js +104 -0
  191. package/dist/modules/eudr/lib/annual-report.js.map +7 -0
  192. package/dist/modules/eudr/lib/completeness.js +64 -0
  193. package/dist/modules/eudr/lib/completeness.js.map +7 -0
  194. package/dist/modules/eudr/lib/detail-read-scope.js +16 -0
  195. package/dist/modules/eudr/lib/detail-read-scope.js.map +7 -0
  196. package/dist/modules/eudr/lib/geometry.js +142 -0
  197. package/dist/modules/eudr/lib/geometry.js.map +7 -0
  198. package/dist/modules/eudr/lib/notifications.js +56 -0
  199. package/dist/modules/eudr/lib/notifications.js.map +7 -0
  200. package/dist/modules/eudr/lib/plural.js +26 -0
  201. package/dist/modules/eudr/lib/plural.js.map +7 -0
  202. package/dist/modules/eudr/lib/reference-data.js +240 -0
  203. package/dist/modules/eudr/lib/reference-data.js.map +7 -0
  204. package/dist/modules/eudr/lib/species.js +10 -0
  205. package/dist/modules/eudr/lib/species.js.map +7 -0
  206. package/dist/modules/eudr/lib/statement-lifecycle.js +84 -0
  207. package/dist/modules/eudr/lib/statement-lifecycle.js.map +7 -0
  208. package/dist/modules/eudr/lib/statement-seeding.js +49 -0
  209. package/dist/modules/eudr/lib/statement-seeding.js.map +7 -0
  210. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js +15 -0
  211. package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js.map +7 -0
  212. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js +24 -0
  213. package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js.map +7 -0
  214. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js +15 -0
  215. package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js.map +7 -0
  216. package/dist/modules/eudr/notifications.js +58 -0
  217. package/dist/modules/eudr/notifications.js.map +7 -0
  218. package/dist/modules/eudr/search.js +186 -0
  219. package/dist/modules/eudr/search.js.map +7 -0
  220. package/dist/modules/eudr/setup.js +12 -0
  221. package/dist/modules/eudr/setup.js.map +7 -0
  222. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js +34 -0
  223. package/dist/modules/eudr/subscribers/notify-mitigation-completed.js.map +7 -0
  224. package/dist/modules/eudr/subscribers/notify-risk-concluded.js +35 -0
  225. package/dist/modules/eudr/subscribers/notify-risk-concluded.js.map +7 -0
  226. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js +37 -0
  227. package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js.map +7 -0
  228. package/dist/modules/eudr/subscribers/notify-statement-submitted.js +34 -0
  229. package/dist/modules/eudr/subscribers/notify-statement-submitted.js.map +7 -0
  230. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js +34 -0
  231. package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js.map +7 -0
  232. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js +124 -0
  233. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js.map +7 -0
  234. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js +22 -0
  235. package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js.map +7 -0
  236. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js +203 -0
  237. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js.map +7 -0
  238. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js +23 -0
  239. package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js.map +7 -0
  240. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js +19 -0
  241. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js.map +7 -0
  242. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js +31 -0
  243. package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js.map +7 -0
  244. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js +100 -0
  245. package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js.map +7 -0
  246. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js +17 -0
  247. package/dist/modules/eudr/widgets/injection/order-compliance/widget.js.map +7 -0
  248. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js +116 -0
  249. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js.map +7 -0
  250. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js +17 -0
  251. package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js.map +7 -0
  252. package/dist/modules/eudr/widgets/injection-table.js +32 -0
  253. package/dist/modules/eudr/widgets/injection-table.js.map +7 -0
  254. package/dist/modules/notifications/lib/notificationService.js +67 -3
  255. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  256. package/dist/modules/query_index/lib/engine.js +121 -48
  257. package/dist/modules/query_index/lib/engine.js.map +2 -2
  258. package/dist/modules/sales/api/documents/factory.js +17 -0
  259. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  260. package/dist/modules/sales/commands/documents.js +32 -18
  261. package/dist/modules/sales/commands/documents.js.map +2 -2
  262. package/dist/modules/sales/data/entities.js +1 -0
  263. package/dist/modules/sales/data/entities.js.map +2 -2
  264. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js +17 -0
  265. package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js.map +7 -0
  266. package/generated/entities/eudr_due_diligence_statement/index.ts +22 -0
  267. package/generated/entities/eudr_evidence_submission/index.ts +24 -0
  268. package/generated/entities/eudr_mitigation_action/index.ts +14 -0
  269. package/generated/entities/eudr_plot/index.ts +18 -0
  270. package/generated/entities/eudr_product_mapping/index.ts +14 -0
  271. package/generated/entities/eudr_risk_assessment/index.ts +16 -0
  272. package/generated/entities.ids.generated.ts +9 -0
  273. package/generated/entity-fields-registry.ts +120 -0
  274. package/package.json +7 -7
  275. package/src/helpers/integration/ui.ts +12 -0
  276. package/src/modules/attachments/fields/attachment.tsx +5 -0
  277. package/src/modules/attachments/lib/crud.ts +2 -0
  278. package/src/modules/eudr/acl.ts +39 -0
  279. package/src/modules/eudr/ai-agents.ts +258 -0
  280. package/src/modules/eudr/ai-tools/compliance-pack.ts +618 -0
  281. package/src/modules/eudr/ai-tools/types.ts +44 -0
  282. package/src/modules/eudr/ai-tools.ts +15 -0
  283. package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +481 -0
  284. package/src/modules/eudr/api/evidence-submissions/route.ts +335 -0
  285. package/src/modules/eudr/api/mitigation-actions/route.ts +263 -0
  286. package/src/modules/eudr/api/openapi.ts +28 -0
  287. package/src/modules/eudr/api/plots/import/route.ts +369 -0
  288. package/src/modules/eudr/api/plots/route.ts +296 -0
  289. package/src/modules/eudr/api/product-mappings/route.ts +269 -0
  290. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +382 -0
  291. package/src/modules/eudr/api/product-mappings/suggestions/route.ts +169 -0
  292. package/src/modules/eudr/api/reports/annual/route.ts +457 -0
  293. package/src/modules/eudr/api/risk-assessments/route.ts +323 -0
  294. package/src/modules/eudr/api/statements/[id]/export/route.ts +713 -0
  295. package/src/modules/eudr/api/statements/[id]/readiness/route.ts +129 -0
  296. package/src/modules/eudr/api/statements/route.ts +329 -0
  297. package/src/modules/eudr/api/suppliers/compliance/route.ts +241 -0
  298. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.ts +12 -0
  299. package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.tsx +545 -0
  300. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.ts +13 -0
  301. package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.tsx +383 -0
  302. package/src/modules/eudr/backend/eudr/evidence-submissions/page.meta.ts +12 -0
  303. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +379 -0
  304. package/src/modules/eudr/backend/eudr/page.meta.ts +12 -0
  305. package/src/modules/eudr/backend/eudr/page.tsx +477 -0
  306. package/src/modules/eudr/backend/eudr/plots/[id]/page.meta.ts +12 -0
  307. package/src/modules/eudr/backend/eudr/plots/[id]/page.tsx +404 -0
  308. package/src/modules/eudr/backend/eudr/plots/create/page.meta.ts +13 -0
  309. package/src/modules/eudr/backend/eudr/plots/create/page.tsx +267 -0
  310. package/src/modules/eudr/backend/eudr/plots/page.meta.ts +12 -0
  311. package/src/modules/eudr/backend/eudr/plots/page.tsx +442 -0
  312. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.ts +12 -0
  313. package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.tsx +311 -0
  314. package/src/modules/eudr/backend/eudr/product-mappings/create/page.meta.ts +13 -0
  315. package/src/modules/eudr/backend/eudr/product-mappings/create/page.tsx +190 -0
  316. package/src/modules/eudr/backend/eudr/product-mappings/page.meta.ts +12 -0
  317. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +344 -0
  318. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.ts +12 -0
  319. package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.tsx +421 -0
  320. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.meta.ts +13 -0
  321. package/src/modules/eudr/backend/eudr/risk-assessments/create/page.tsx +247 -0
  322. package/src/modules/eudr/backend/eudr/risk-assessments/page.meta.ts +12 -0
  323. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +383 -0
  324. package/src/modules/eudr/backend/eudr/statements/[id]/page.meta.ts +12 -0
  325. package/src/modules/eudr/backend/eudr/statements/[id]/page.tsx +777 -0
  326. package/src/modules/eudr/backend/eudr/statements/create/page.meta.ts +13 -0
  327. package/src/modules/eudr/backend/eudr/statements/create/page.tsx +511 -0
  328. package/src/modules/eudr/backend/eudr/statements/page.meta.ts +12 -0
  329. package/src/modules/eudr/backend/eudr/statements/page.tsx +372 -0
  330. package/src/modules/eudr/ce.ts +52 -0
  331. package/src/modules/eudr/commands/evidence-submissions.ts +699 -0
  332. package/src/modules/eudr/commands/index.ts +6 -0
  333. package/src/modules/eudr/commands/lifecycle-events.ts +28 -0
  334. package/src/modules/eudr/commands/mitigation-actions.ts +574 -0
  335. package/src/modules/eudr/commands/plots.ts +627 -0
  336. package/src/modules/eudr/commands/product-mappings.ts +556 -0
  337. package/src/modules/eudr/commands/risk-assessments.ts +728 -0
  338. package/src/modules/eudr/commands/statements.ts +933 -0
  339. package/src/modules/eudr/components/CountrySelectField.tsx +77 -0
  340. package/src/modules/eudr/components/GeometryInput.tsx +170 -0
  341. package/src/modules/eudr/components/MappingSuggestionsDialog.tsx +385 -0
  342. package/src/modules/eudr/components/MitigationActionsSection.tsx +417 -0
  343. package/src/modules/eudr/components/OrderSelectField.tsx +116 -0
  344. package/src/modules/eudr/components/PlotImportDialog.tsx +277 -0
  345. package/src/modules/eudr/components/PlotMapPreview.tsx +46 -0
  346. package/src/modules/eudr/components/PlotMapPreviewImpl.tsx +111 -0
  347. package/src/modules/eudr/components/PlotMultiSelectField.tsx +238 -0
  348. package/src/modules/eudr/components/RiskCriteriaField.tsx +173 -0
  349. package/src/modules/eudr/components/StatementLifecycleBar.tsx +363 -0
  350. package/src/modules/eudr/components/StatementReadinessChecklist.tsx +82 -0
  351. package/src/modules/eudr/components/StatementRiskSection.tsx +242 -0
  352. package/src/modules/eudr/components/crudErrorI18n.ts +36 -0
  353. package/src/modules/eudr/components/formConfig.tsx +717 -0
  354. package/src/modules/eudr/components/plotForm.ts +75 -0
  355. package/src/modules/eudr/data/enrichers.ts +82 -0
  356. package/src/modules/eudr/data/entities.ts +369 -0
  357. package/src/modules/eudr/data/validators.ts +357 -0
  358. package/src/modules/eudr/encryption.ts +31 -0
  359. package/src/modules/eudr/events.ts +38 -0
  360. package/src/modules/eudr/i18n/de.json +747 -0
  361. package/src/modules/eudr/i18n/en.json +747 -0
  362. package/src/modules/eudr/i18n/es.json +747 -0
  363. package/src/modules/eudr/i18n/ko.json +747 -0
  364. package/src/modules/eudr/i18n/pl.json +747 -0
  365. package/src/modules/eudr/index.ts +13 -0
  366. package/src/modules/eudr/lib/annual-report.ts +120 -0
  367. package/src/modules/eudr/lib/completeness.ts +90 -0
  368. package/src/modules/eudr/lib/detail-read-scope.ts +44 -0
  369. package/src/modules/eudr/lib/geometry.ts +191 -0
  370. package/src/modules/eudr/lib/notifications.ts +85 -0
  371. package/src/modules/eudr/lib/plural.ts +36 -0
  372. package/src/modules/eudr/lib/reference-data.ts +128 -0
  373. package/src/modules/eudr/lib/species.ts +12 -0
  374. package/src/modules/eudr/lib/statement-lifecycle.ts +109 -0
  375. package/src/modules/eudr/lib/statement-seeding.ts +86 -0
  376. package/src/modules/eudr/migrations/.snapshot-open-mercato.json +2046 -0
  377. package/src/modules/eudr/migrations/Migration20260706102002_eudr.ts +16 -0
  378. package/src/modules/eudr/migrations/Migration20260706152612_eudr.ts +30 -0
  379. package/src/modules/eudr/migrations/Migration20260722100000_eudr.ts +17 -0
  380. package/src/modules/eudr/notifications.ts +56 -0
  381. package/src/modules/eudr/search.ts +212 -0
  382. package/src/modules/eudr/setup.ts +10 -0
  383. package/src/modules/eudr/subscribers/notify-mitigation-completed.ts +56 -0
  384. package/src/modules/eudr/subscribers/notify-risk-concluded.ts +58 -0
  385. package/src/modules/eudr/subscribers/notify-statement-reference-issued.ts +59 -0
  386. package/src/modules/eudr/subscribers/notify-statement-submitted.ts +49 -0
  387. package/src/modules/eudr/subscribers/notify-statement-withdrawn.ts +49 -0
  388. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-created.ts +189 -0
  389. package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.ts +31 -0
  390. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.client.tsx +271 -0
  391. package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.ts +24 -0
  392. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.client.tsx +37 -0
  393. package/src/modules/eudr/widgets/injection/eudr-product-column/widget.ts +30 -0
  394. package/src/modules/eudr/widgets/injection/order-compliance/widget.client.tsx +131 -0
  395. package/src/modules/eudr/widgets/injection/order-compliance/widget.ts +16 -0
  396. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.client.tsx +168 -0
  397. package/src/modules/eudr/widgets/injection/supplier-compliance/widget.ts +16 -0
  398. package/src/modules/eudr/widgets/injection-table.ts +31 -0
  399. package/src/modules/notifications/lib/notificationService.ts +83 -4
  400. package/src/modules/query_index/lib/engine.ts +139 -51
  401. package/src/modules/sales/api/documents/factory.ts +29 -0
  402. package/src/modules/sales/commands/documents.ts +65 -18
  403. package/src/modules/sales/data/entities.ts +1 -0
  404. package/src/modules/sales/migrations/.snapshot-open-mercato.json +10 -0
  405. package/src/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.ts +59 -0
@@ -0,0 +1,933 @@
1
+ import { registerCommand } from '@open-mercato/shared/lib/commands'
2
+ import type { CommandHandler } from '@open-mercato/shared/lib/commands'
3
+ import type { RequiredEntityData } from '@mikro-orm/core'
4
+ import {
5
+ parseWithCustomFields,
6
+ emitCrudSideEffects,
7
+ emitCrudUndoSideEffects,
8
+ requireId,
9
+ setCustomFieldsIfAny,
10
+ snapshotsEqual,
11
+ } from '@open-mercato/shared/lib/commands/helpers'
12
+ import { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
13
+ import { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'
14
+ import { runCrudCommandWrite } from '@open-mercato/shared/lib/commands/runCrudCommandWrite'
15
+ import { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'
16
+ import {
17
+ loadCustomFieldSnapshot,
18
+ buildCustomFieldResetMap,
19
+ } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
20
+ import type { EntityManager } from '@mikro-orm/postgresql'
21
+ import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
22
+ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
23
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
24
+ import type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
25
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
26
+ import { emitEudrLifecycleEvent } from './lifecycle-events'
27
+ import { E } from '#generated/entities.ids.generated'
28
+ import { sql } from 'kysely'
29
+ import { z } from 'zod'
30
+ import {
31
+ EudrDueDiligenceStatement,
32
+ EudrEvidenceSubmission,
33
+ EudrMitigationAction,
34
+ EudrRiskAssessment,
35
+ } from '../data/entities'
36
+ import {
37
+ EUDR_STATEMENT_STATUSES,
38
+ statementCreateSchema,
39
+ statementUpdateSchema,
40
+ type EudrStatementStatus,
41
+ type StatementCreateInput,
42
+ type StatementUpdateInput,
43
+ } from '../data/validators'
44
+ import {
45
+ EUDR_AMEND_GUARDED_FIELDS,
46
+ canTransition,
47
+ evaluateSubmissionGate,
48
+ isAmendWindowOpen,
49
+ type GateAssessmentView,
50
+ type GateSubmissionView,
51
+ } from '../lib/statement-lifecycle'
52
+
53
+ const STATEMENT_ENTITY_ID = 'eudr:eudr_due_diligence_statement'
54
+
55
+ type ScopedCommandInput = {
56
+ tenantId: string
57
+ organizationId: string
58
+ }
59
+
60
+ type StatementSnapshot = {
61
+ id: string
62
+ organizationId: string
63
+ tenantId: string
64
+ title: string
65
+ commodity: string
66
+ referenceNumber: string | null
67
+ verificationNumber: string | null
68
+ status: string
69
+ activityType: string | null
70
+ actorRole: string | null
71
+ referencedStatements: Array<{ referenceNumber: string; verificationNumber?: string | null }>
72
+ quantityKg: string | null
73
+ supplementaryUnit: string | null
74
+ supplementaryQuantity: string | null
75
+ orderId: string | null
76
+ submittedAt: string | null
77
+ referenceIssuedAt: string | null
78
+ orderSnapshot: { orderNumber?: string | null } | null
79
+ notes: string | null
80
+ createdAt: string
81
+ updatedAt: string
82
+ deletedAt: string | null
83
+ custom?: Record<string, unknown> | null
84
+ }
85
+
86
+ type StatementUndoPayload = {
87
+ before?: StatementSnapshot | null
88
+ after?: StatementSnapshot | null
89
+ }
90
+
91
+ type ScopedStatementCreateInput = StatementCreateInput & ScopedCommandInput
92
+ type ScopedStatementUpdateInput = StatementUpdateInput & Partial<ScopedCommandInput>
93
+
94
+ type StatementCommandResult = {
95
+ entityId: string
96
+ updatedAt?: Date
97
+ }
98
+
99
+ const scopedCommandInputSchema = z.object({
100
+ tenantId: z.string().uuid(),
101
+ organizationId: z.string().uuid(),
102
+ })
103
+
104
+ const statementCrudIndexer: CrudIndexerConfig<EudrDueDiligenceStatement> = {
105
+ entityType: E.eudr.eudr_due_diligence_statement,
106
+ }
107
+
108
+ const statementCrudEvents: CrudEventsConfig<EudrDueDiligenceStatement> = {
109
+ module: 'eudr',
110
+ entity: 'due_diligence_statement',
111
+ persistent: true,
112
+ buildPayload: (emitContext) => ({
113
+ id: emitContext.identifiers.id,
114
+ entityId: emitContext.entity?.id ?? emitContext.identifiers.id,
115
+ organizationId: emitContext.identifiers.organizationId,
116
+ tenantId: emitContext.identifiers.tenantId,
117
+ }),
118
+ }
119
+
120
+ function parseScopedCommandInput(input: unknown): ScopedCommandInput {
121
+ return scopedCommandInputSchema.parse(input)
122
+ }
123
+
124
+ function toNumericString(value: number | null | undefined): string | null {
125
+ return value == null ? null : String(value)
126
+ }
127
+
128
+ function toDate(value: string | null): Date | null {
129
+ return value ? new Date(value) : null
130
+ }
131
+
132
+ function statementSeedFromSnapshot(snapshot: StatementSnapshot): RequiredEntityData<EudrDueDiligenceStatement> {
133
+ return {
134
+ id: snapshot.id,
135
+ organizationId: snapshot.organizationId,
136
+ tenantId: snapshot.tenantId,
137
+ title: snapshot.title,
138
+ commodity: snapshot.commodity,
139
+ referenceNumber: snapshot.referenceNumber,
140
+ verificationNumber: snapshot.verificationNumber,
141
+ status: snapshot.status,
142
+ activityType: snapshot.activityType,
143
+ actorRole: snapshot.actorRole,
144
+ referencedStatements: snapshot.referencedStatements.map((entry) => ({ ...entry })),
145
+ quantityKg: snapshot.quantityKg,
146
+ supplementaryUnit: snapshot.supplementaryUnit,
147
+ supplementaryQuantity: snapshot.supplementaryQuantity,
148
+ orderId: snapshot.orderId,
149
+ submittedAt: toDate(snapshot.submittedAt),
150
+ referenceIssuedAt: toDate(snapshot.referenceIssuedAt),
151
+ orderSnapshot: snapshot.orderSnapshot,
152
+ notes: snapshot.notes,
153
+ createdAt: new Date(snapshot.createdAt),
154
+ updatedAt: new Date(snapshot.updatedAt),
155
+ deletedAt: toDate(snapshot.deletedAt),
156
+ }
157
+ }
158
+
159
+ async function loadStatementSnapshot(em: EntityManager, entityId: string): Promise<StatementSnapshot | null> {
160
+ const record = await em.findOne(EudrDueDiligenceStatement, { id: entityId })
161
+ if (!record) return null
162
+ const custom = await loadCustomFieldSnapshot(em, {
163
+ entityId: STATEMENT_ENTITY_ID,
164
+ recordId: record.id,
165
+ tenantId: record.tenantId,
166
+ organizationId: record.organizationId,
167
+ })
168
+ return {
169
+ id: record.id,
170
+ organizationId: record.organizationId,
171
+ tenantId: record.tenantId,
172
+ title: record.title,
173
+ commodity: record.commodity,
174
+ referenceNumber: record.referenceNumber ?? null,
175
+ verificationNumber: record.verificationNumber ?? null,
176
+ status: record.status,
177
+ activityType: record.activityType ?? null,
178
+ actorRole: record.actorRole ?? null,
179
+ referencedStatements: Array.isArray(record.referencedStatements)
180
+ ? record.referencedStatements.map((entry) => ({ ...entry }))
181
+ : [],
182
+ quantityKg: record.quantityKg ?? null,
183
+ supplementaryUnit: record.supplementaryUnit ?? null,
184
+ supplementaryQuantity: record.supplementaryQuantity ?? null,
185
+ orderId: record.orderId ?? null,
186
+ submittedAt: record.submittedAt ? record.submittedAt.toISOString() : null,
187
+ referenceIssuedAt: record.referenceIssuedAt ? record.referenceIssuedAt.toISOString() : null,
188
+ orderSnapshot: record.orderSnapshot ?? null,
189
+ notes: record.notes ?? null,
190
+ createdAt: record.createdAt.toISOString(),
191
+ updatedAt: record.updatedAt.toISOString(),
192
+ deletedAt: record.deletedAt ? record.deletedAt.toISOString() : null,
193
+ custom: Object.keys(custom).length ? custom : null,
194
+ }
195
+ }
196
+
197
+ function applyStatementUpdate(record: EudrDueDiligenceStatement, parsed: StatementUpdateInput): void {
198
+ if (parsed.title !== undefined) record.title = parsed.title
199
+ if (parsed.commodity !== undefined) record.commodity = parsed.commodity
200
+ if (parsed.referenceNumber !== undefined) record.referenceNumber = parsed.referenceNumber ?? null
201
+ if (parsed.verificationNumber !== undefined) record.verificationNumber = parsed.verificationNumber ?? null
202
+ if (parsed.status !== undefined) record.status = parsed.status
203
+ if (parsed.activityType !== undefined) record.activityType = parsed.activityType ?? null
204
+ if (parsed.actorRole !== undefined) record.actorRole = parsed.actorRole ?? null
205
+ if (parsed.referencedStatements !== undefined) {
206
+ record.referencedStatements = parsed.referencedStatements.map((entry) => ({ ...entry }))
207
+ }
208
+ if (parsed.quantityKg !== undefined) record.quantityKg = toNumericString(parsed.quantityKg)
209
+ if (parsed.supplementaryUnit !== undefined) record.supplementaryUnit = parsed.supplementaryUnit ?? null
210
+ if (parsed.supplementaryQuantity !== undefined) record.supplementaryQuantity = toNumericString(parsed.supplementaryQuantity)
211
+ if (parsed.orderId !== undefined) record.orderId = parsed.orderId ?? null
212
+ if (parsed.referenceIssuedAt !== undefined) record.referenceIssuedAt = parsed.referenceIssuedAt
213
+ if (parsed.orderSnapshot !== undefined) record.orderSnapshot = parsed.orderSnapshot ?? null
214
+ if (parsed.notes !== undefined) record.notes = parsed.notes ?? null
215
+ }
216
+
217
+ function restoreStatement(record: EudrDueDiligenceStatement, snapshot: StatementSnapshot): void {
218
+ record.organizationId = snapshot.organizationId
219
+ record.tenantId = snapshot.tenantId
220
+ record.title = snapshot.title
221
+ record.commodity = snapshot.commodity
222
+ record.referenceNumber = snapshot.referenceNumber
223
+ record.verificationNumber = snapshot.verificationNumber
224
+ record.status = snapshot.status
225
+ record.activityType = snapshot.activityType
226
+ record.actorRole = snapshot.actorRole
227
+ record.referencedStatements = snapshot.referencedStatements.map((entry) => ({ ...entry }))
228
+ record.quantityKg = snapshot.quantityKg
229
+ record.supplementaryUnit = snapshot.supplementaryUnit
230
+ record.supplementaryQuantity = snapshot.supplementaryQuantity
231
+ record.orderId = snapshot.orderId
232
+ record.submittedAt = toDate(snapshot.submittedAt)
233
+ record.referenceIssuedAt = toDate(snapshot.referenceIssuedAt)
234
+ record.orderSnapshot = snapshot.orderSnapshot
235
+ record.notes = snapshot.notes
236
+ record.createdAt = new Date(snapshot.createdAt)
237
+ record.updatedAt = new Date(snapshot.updatedAt)
238
+ record.deletedAt = toDate(snapshot.deletedAt)
239
+ }
240
+
241
+ async function setStatementCustomFields(
242
+ dataEngine: DataEngine,
243
+ entityId: string,
244
+ organizationId: string,
245
+ tenantId: string,
246
+ values: Record<string, unknown>,
247
+ ): Promise<void> {
248
+ await setCustomFieldsIfAny({
249
+ dataEngine,
250
+ entityId: STATEMENT_ENTITY_ID,
251
+ recordId: entityId,
252
+ organizationId,
253
+ tenantId,
254
+ values,
255
+ notify: false,
256
+ })
257
+ }
258
+
259
+ type PatchedStatementView = {
260
+ referenceNumber: string | null
261
+ verificationNumber: string | null
262
+ actorRole: string | null
263
+ referencedStatements: Array<{ referenceNumber: string; verificationNumber?: string | null }>
264
+ }
265
+
266
+ type StatementServerFields = {
267
+ submittedAt?: Date
268
+ referenceIssuedAt?: Date | null
269
+ }
270
+
271
+ const STATEMENT_STATUS_SET = new Set<string>(EUDR_STATEMENT_STATUSES)
272
+
273
+ const STATEMENT_UPDATE_FIELDS = [
274
+ 'title',
275
+ 'commodity',
276
+ 'referenceNumber',
277
+ 'verificationNumber',
278
+ 'status',
279
+ 'activityType',
280
+ 'actorRole',
281
+ 'referencedStatements',
282
+ 'quantityKg',
283
+ 'supplementaryUnit',
284
+ 'supplementaryQuantity',
285
+ 'orderId',
286
+ 'referenceIssuedAt',
287
+ 'orderSnapshot',
288
+ 'notes',
289
+ ] as const
290
+
291
+ type StatementUpdateField = (typeof STATEMENT_UPDATE_FIELDS)[number]
292
+
293
+ function isStatementStatus(value: string): value is EudrStatementStatus {
294
+ return STATEMENT_STATUS_SET.has(value)
295
+ }
296
+
297
+ function isNonEmpty(value: string | null | undefined): boolean {
298
+ return typeof value === 'string' && value.trim().length > 0
299
+ }
300
+
301
+ function dateValuesEqual(left: Date | null | undefined, right: Date | null | undefined): boolean {
302
+ const leftTime = left ? left.getTime() : null
303
+ const rightTime = right ? right.getTime() : null
304
+ return leftTime === rightTime
305
+ }
306
+
307
+ function jsonValuesEqual(left: unknown, right: unknown): boolean {
308
+ return JSON.stringify(left ?? null) === JSON.stringify(right ?? null)
309
+ }
310
+
311
+ // PG numeric columns read back scale-padded ('100.000'); compare numerically so
312
+ // an unchanged echo from a whole-document save is not treated as an amendment.
313
+ function numericValuesEqual(left: string | null | undefined, right: string | null | undefined): boolean {
314
+ const leftNumber = left === null || left === undefined || left === '' ? null : Number(left)
315
+ const rightNumber = right === null || right === undefined || right === '' ? null : Number(right)
316
+ if (leftNumber === null || rightNumber === null) return leftNumber === rightNumber
317
+ return Number.isFinite(leftNumber) && Number.isFinite(rightNumber) && leftNumber === rightNumber
318
+ }
319
+
320
+ function referencedStatementsEqual(
321
+ left: Array<{ referenceNumber: string; verificationNumber?: string | null }> | null | undefined,
322
+ right: Array<{ referenceNumber: string; verificationNumber?: string | null }> | null | undefined,
323
+ ): boolean {
324
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? [])
325
+ }
326
+
327
+ function statementFieldChanged(
328
+ record: EudrDueDiligenceStatement,
329
+ parsed: StatementUpdateInput,
330
+ field: StatementUpdateField,
331
+ ): boolean {
332
+ switch (field) {
333
+ case 'title':
334
+ return parsed.title !== undefined && parsed.title !== record.title
335
+ case 'commodity':
336
+ return parsed.commodity !== undefined && parsed.commodity !== record.commodity
337
+ case 'referenceNumber':
338
+ return parsed.referenceNumber !== undefined && (parsed.referenceNumber ?? null) !== (record.referenceNumber ?? null)
339
+ case 'verificationNumber':
340
+ return parsed.verificationNumber !== undefined && (parsed.verificationNumber ?? null) !== (record.verificationNumber ?? null)
341
+ case 'status':
342
+ return parsed.status !== undefined && parsed.status !== record.status
343
+ case 'activityType':
344
+ return parsed.activityType !== undefined && (parsed.activityType ?? null) !== (record.activityType ?? null)
345
+ case 'actorRole':
346
+ return parsed.actorRole !== undefined && (parsed.actorRole ?? null) !== (record.actorRole ?? null)
347
+ case 'referencedStatements':
348
+ return parsed.referencedStatements !== undefined && !referencedStatementsEqual(parsed.referencedStatements, record.referencedStatements)
349
+ case 'quantityKg':
350
+ return parsed.quantityKg !== undefined && !numericValuesEqual(toNumericString(parsed.quantityKg), record.quantityKg ?? null)
351
+ case 'supplementaryUnit':
352
+ return parsed.supplementaryUnit !== undefined && (parsed.supplementaryUnit ?? null) !== (record.supplementaryUnit ?? null)
353
+ case 'supplementaryQuantity':
354
+ return parsed.supplementaryQuantity !== undefined && !numericValuesEqual(toNumericString(parsed.supplementaryQuantity), record.supplementaryQuantity ?? null)
355
+ case 'orderId':
356
+ return parsed.orderId !== undefined && (parsed.orderId ?? null) !== (record.orderId ?? null)
357
+ case 'referenceIssuedAt':
358
+ return parsed.referenceIssuedAt !== undefined && !dateValuesEqual(parsed.referenceIssuedAt, record.referenceIssuedAt ?? null)
359
+ case 'orderSnapshot':
360
+ return parsed.orderSnapshot !== undefined && !jsonValuesEqual(parsed.orderSnapshot ?? null, record.orderSnapshot ?? null)
361
+ case 'notes':
362
+ return parsed.notes !== undefined && (parsed.notes ?? null) !== (record.notes ?? null)
363
+ }
364
+ }
365
+
366
+ function hasStatementFieldChanges(record: EudrDueDiligenceStatement, parsed: StatementUpdateInput): boolean {
367
+ return STATEMENT_UPDATE_FIELDS.some((field) => statementFieldChanged(record, parsed, field))
368
+ }
369
+
370
+ function buildPatchedStatementView(record: EudrDueDiligenceStatement, parsed: StatementUpdateInput): PatchedStatementView {
371
+ return {
372
+ referenceNumber: parsed.referenceNumber !== undefined ? parsed.referenceNumber ?? null : record.referenceNumber ?? null,
373
+ verificationNumber: parsed.verificationNumber !== undefined ? parsed.verificationNumber ?? null : record.verificationNumber ?? null,
374
+ actorRole: parsed.actorRole !== undefined ? parsed.actorRole ?? null : record.actorRole ?? null,
375
+ referencedStatements: parsed.referencedStatements !== undefined
376
+ ? parsed.referencedStatements.map((entry) => ({ ...entry }))
377
+ : Array.isArray(record.referencedStatements)
378
+ ? record.referencedStatements.map((entry) => ({ ...entry }))
379
+ : [],
380
+ }
381
+ }
382
+
383
+ function hasChangedAmendGuardedField(record: EudrDueDiligenceStatement, parsed: StatementUpdateInput): boolean {
384
+ return EUDR_AMEND_GUARDED_FIELDS.some((field) => statementFieldChanged(record, parsed, field))
385
+ }
386
+
387
+ function hasConcernAnswers(criteria: Record<string, { answer: string; note?: string | null }>): boolean {
388
+ return Object.values(criteria).some((entry) => entry.answer === 'concern')
389
+ }
390
+
391
+ export async function loadStatementSubmissionsForGate(
392
+ em: EntityManager,
393
+ record: EudrDueDiligenceStatement,
394
+ ): Promise<GateSubmissionView[]> {
395
+ const submissions = await findWithDecryption(
396
+ em,
397
+ EudrEvidenceSubmission,
398
+ {
399
+ statementId: record.id,
400
+ tenantId: record.tenantId,
401
+ organizationId: record.organizationId,
402
+ deletedAt: null,
403
+ },
404
+ undefined,
405
+ { tenantId: record.tenantId, organizationId: record.organizationId },
406
+ )
407
+ return submissions.map((submission) => ({
408
+ status: submission.status,
409
+ completenessScore: submission.completenessScore,
410
+ originCountry: submission.originCountry ?? null,
411
+ }))
412
+ }
413
+
414
+ export async function loadLatestAssessmentForGate(
415
+ em: EntityManager,
416
+ record: EudrDueDiligenceStatement,
417
+ ): Promise<GateAssessmentView> {
418
+ const assessments = await findWithDecryption(
419
+ em,
420
+ EudrRiskAssessment,
421
+ {
422
+ statementId: record.id,
423
+ tenantId: record.tenantId,
424
+ organizationId: record.organizationId,
425
+ deletedAt: null,
426
+ },
427
+ {
428
+ orderBy: { assessedAt: 'desc', createdAt: 'desc' },
429
+ limit: 1,
430
+ },
431
+ { tenantId: record.tenantId, organizationId: record.organizationId },
432
+ )
433
+ const assessment = assessments[0]
434
+ if (!assessment) return null
435
+ const completedMitigationCount = await em.count(EudrMitigationAction, {
436
+ riskAssessmentId: assessment.id,
437
+ tenantId: record.tenantId,
438
+ organizationId: record.organizationId,
439
+ status: 'completed',
440
+ deletedAt: null,
441
+ })
442
+ return {
443
+ conclusion: assessment.conclusion,
444
+ countryRisks: assessment.countryRisks.map((risk) => ({ country: risk.country, tier: risk.tier })),
445
+ reviewDueAt: assessment.reviewDueAt ?? null,
446
+ hasConcernAnswers: hasConcernAnswers(assessment.criteria),
447
+ hasCompletedMitigation: completedMitigationCount > 0,
448
+ }
449
+ }
450
+
451
+ async function assertNoDownstreamReferences(
452
+ em: EntityManager,
453
+ record: EudrDueDiligenceStatement,
454
+ ): Promise<void> {
455
+ const referenceNumber = record.referenceNumber?.trim().toUpperCase() ?? ''
456
+ if (!referenceNumber) return
457
+ const db = em.getKysely<{
458
+ eudr_due_diligence_statements: {
459
+ id: string
460
+ tenant_id: string
461
+ organization_id: string
462
+ deleted_at: Date | null
463
+ referenced_statements: unknown
464
+ }
465
+ }>()
466
+ const containment = JSON.stringify([{ referenceNumber }])
467
+ const downstreamRow = await db
468
+ .selectFrom('eudr_due_diligence_statements')
469
+ .select(sql<number>`count(*)`.as('total'))
470
+ .where('tenant_id', '=', record.tenantId)
471
+ .where('organization_id', '=', record.organizationId)
472
+ .where('deleted_at', 'is', null)
473
+ .where('id', '!=', record.id)
474
+ .where(sql<boolean>`referenced_statements @> ${containment}::jsonb`)
475
+ .executeTakeFirst()
476
+ if (Number(downstreamRow?.total ?? 0) > 0) {
477
+ throw new CrudHttpError(400, { error: 'eudr.errors.referencedDownstream' })
478
+ }
479
+ }
480
+
481
+ async function validateStatementUpdate(
482
+ em: EntityManager,
483
+ record: EudrDueDiligenceStatement,
484
+ parsed: StatementUpdateInput,
485
+ custom: Record<string, unknown>,
486
+ ): Promise<StatementServerFields> {
487
+ if (!isStatementStatus(record.status)) {
488
+ throw new CrudHttpError(400, { error: 'eudr.errors.invalidTransition' })
489
+ }
490
+
491
+ const requestedStatus = parsed.status
492
+ const statusChanged = requestedStatus !== undefined && requestedStatus !== record.status
493
+ if (statusChanged && (!isStatementStatus(requestedStatus) || !canTransition(record.status, requestedStatus))) {
494
+ throw new CrudHttpError(400, { error: 'eudr.errors.invalidTransition' })
495
+ }
496
+
497
+ const hasBaseChanges = hasStatementFieldChanges(record, parsed)
498
+ if (record.status === 'archived' && (hasBaseChanges || Object.keys(custom).length > 0)) {
499
+ throw new CrudHttpError(400, { error: 'eudr.errors.archivedReadOnly' })
500
+ }
501
+
502
+ const patched = buildPatchedStatementView(record, parsed)
503
+ const serverFields: StatementServerFields = {}
504
+ const isSubmittedToAvailable = record.status === 'submitted' && requestedStatus === 'available'
505
+
506
+ if (statusChanged) {
507
+ if (record.status === 'draft' && requestedStatus === 'submitted') {
508
+ const submissions = await loadStatementSubmissionsForGate(em, record)
509
+ const latestAssessment = await loadLatestAssessmentForGate(em, record)
510
+ const gate = evaluateSubmissionGate({
511
+ actorRole: patched.actorRole,
512
+ referencedStatementsCount: patched.referencedStatements.length,
513
+ submissions,
514
+ latestAssessment,
515
+ })
516
+ if (!gate.allowed) {
517
+ throw new CrudHttpError(400, {
518
+ error: 'eudr.errors.gateFailed',
519
+ details: {
520
+ reasons: gate.reasons.map((reason) => `eudr.gate.${reason}`),
521
+ },
522
+ })
523
+ }
524
+ serverFields.submittedAt = new Date()
525
+ }
526
+
527
+ if (isSubmittedToAvailable) {
528
+ if (!isNonEmpty(patched.referenceNumber) || !isNonEmpty(patched.verificationNumber)) {
529
+ throw new CrudHttpError(400, {
530
+ error: 'eudr.errors.referenceNumbersRequired',
531
+ details: {
532
+ reasons: ['eudr.gate.referenceNumbersRequired'],
533
+ },
534
+ })
535
+ }
536
+ const issuedAt = parsed.referenceIssuedAt ?? new Date()
537
+ if (issuedAt.getTime() > Date.now()) {
538
+ throw new CrudHttpError(400, { error: 'eudr.errors.referenceIssuedAtInvalid' })
539
+ }
540
+ serverFields.referenceIssuedAt = issuedAt
541
+ }
542
+
543
+ if (record.status === 'available' && requestedStatus === 'withdrawn') {
544
+ if (!isAmendWindowOpen(record.referenceIssuedAt ?? null)) {
545
+ throw new CrudHttpError(400, { error: 'eudr.errors.amendWindowElapsed' })
546
+ }
547
+ await assertNoDownstreamReferences(em, record)
548
+ }
549
+ }
550
+
551
+ if (
552
+ parsed.referenceIssuedAt !== undefined
553
+ && !isSubmittedToAvailable
554
+ && !dateValuesEqual(parsed.referenceIssuedAt, record.referenceIssuedAt ?? null)
555
+ ) {
556
+ throw new CrudHttpError(400, { error: 'eudr.errors.referenceIssuedAtImmutable' })
557
+ }
558
+
559
+ // Guarded fields freeze once the amend window elapses, regardless of any
560
+ // status transition requested in the same call (available→archived must not
561
+ // smuggle in a quantity/commodity change).
562
+ if (
563
+ record.status === 'available'
564
+ && !isAmendWindowOpen(record.referenceIssuedAt ?? null)
565
+ && hasChangedAmendGuardedField(record, parsed)
566
+ ) {
567
+ throw new CrudHttpError(400, { error: 'eudr.errors.amendWindowElapsed' })
568
+ }
569
+
570
+ return serverFields
571
+ }
572
+
573
+ const createStatementCommand: CommandHandler<ScopedStatementCreateInput, StatementCommandResult> = {
574
+ id: 'eudr.statements.create',
575
+ async execute(rawInput, ctx) {
576
+ const { parsed, custom } = parseWithCustomFields(statementCreateSchema, rawInput)
577
+ const scope = parseScopedCommandInput(rawInput)
578
+ ensureTenantScope(ctx, scope.tenantId)
579
+ ensureOrganizationScope(ctx, scope.organizationId)
580
+ if (parsed.status !== undefined && parsed.status !== 'draft') {
581
+ throw new CrudHttpError(400, { error: 'eudr.errors.invalidTransition' })
582
+ }
583
+ if (parsed.referenceIssuedAt !== undefined) {
584
+ throw new CrudHttpError(400, { error: 'eudr.errors.referenceIssuedAtImmutable' })
585
+ }
586
+
587
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
588
+ let record!: EudrDueDiligenceStatement
589
+
590
+ await runCrudCommandWrite({
591
+ ctx,
592
+ em: entityManager,
593
+ entityId: STATEMENT_ENTITY_ID,
594
+ action: 'created',
595
+ scope,
596
+ customFields: custom,
597
+ events: statementCrudEvents,
598
+ indexer: statementCrudIndexer,
599
+ sideEffect: () => ({
600
+ entity: record,
601
+ identifiers: {
602
+ id: record.id,
603
+ organizationId: record.organizationId,
604
+ tenantId: record.tenantId,
605
+ },
606
+ }),
607
+ phases: [
608
+ () => {
609
+ record = entityManager.create(EudrDueDiligenceStatement, {
610
+ organizationId: scope.organizationId,
611
+ tenantId: scope.tenantId,
612
+ title: parsed.title,
613
+ commodity: parsed.commodity,
614
+ referenceNumber: parsed.referenceNumber ?? null,
615
+ verificationNumber: parsed.verificationNumber ?? null,
616
+ status: parsed.status ?? 'draft',
617
+ activityType: parsed.activityType ?? null,
618
+ actorRole: parsed.actorRole ?? null,
619
+ referencedStatements: parsed.referencedStatements ? parsed.referencedStatements.map((entry) => ({ ...entry })) : [],
620
+ quantityKg: toNumericString(parsed.quantityKg),
621
+ supplementaryUnit: parsed.supplementaryUnit ?? null,
622
+ supplementaryQuantity: toNumericString(parsed.supplementaryQuantity),
623
+ orderId: parsed.orderId ?? null,
624
+ referenceIssuedAt: null,
625
+ orderSnapshot: parsed.orderSnapshot ?? null,
626
+ notes: parsed.notes ?? null,
627
+ })
628
+ entityManager.persist(record)
629
+ },
630
+ ],
631
+ })
632
+
633
+ return { entityId: record.id }
634
+ },
635
+ captureAfter: async (_rawInput, result, ctx) => {
636
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
637
+ return loadStatementSnapshot(entityManager, result.entityId)
638
+ },
639
+ buildLog: async ({ snapshots }) => {
640
+ const after = snapshots.after as StatementSnapshot | undefined
641
+ if (!after) return null
642
+ const { translate } = await resolveTranslations()
643
+ return {
644
+ actionLabel: translate('eudr.audit.statements.create', 'Create EUDR due diligence statement'),
645
+ resourceKind: 'eudr.due_diligence_statement',
646
+ resourceId: after.id,
647
+ tenantId: after.tenantId,
648
+ organizationId: after.organizationId,
649
+ snapshotAfter: after,
650
+ payload: {
651
+ undo: { after } satisfies StatementUndoPayload,
652
+ },
653
+ }
654
+ },
655
+ undo: async ({ logEntry, ctx }) => {
656
+ const payload = extractUndoPayload<StatementUndoPayload>(logEntry)
657
+ const after = payload?.after
658
+ if (!after) return
659
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
660
+ const record = await entityManager.findOne(EudrDueDiligenceStatement, { id: after.id })
661
+ if (!record) return
662
+ ensureTenantScope(ctx, record.tenantId)
663
+ ensureOrganizationScope(ctx, record.organizationId)
664
+ record.deletedAt = new Date()
665
+ await entityManager.flush()
666
+
667
+ const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
668
+ const resetValues = buildCustomFieldResetMap(undefined, after.custom ?? undefined)
669
+ await setStatementCustomFields(dataEngine, after.id, after.organizationId, after.tenantId, resetValues)
670
+ await emitCrudUndoSideEffects({
671
+ dataEngine,
672
+ action: 'deleted',
673
+ entity: record,
674
+ identifiers: {
675
+ id: record.id,
676
+ organizationId: record.organizationId,
677
+ tenantId: record.tenantId,
678
+ },
679
+ indexer: statementCrudIndexer,
680
+ events: statementCrudEvents,
681
+ })
682
+ },
683
+ redo: makeCreateRedo<EudrDueDiligenceStatement, StatementSnapshot, ScopedStatementCreateInput, StatementCommandResult>({
684
+ entityClass: EudrDueDiligenceStatement,
685
+ seedFromSnapshot: statementSeedFromSnapshot,
686
+ buildResult: (entity) => ({ entityId: entity.id }),
687
+ indexer: statementCrudIndexer,
688
+ events: statementCrudEvents,
689
+ afterRestore: async ({ ctx, entity, snapshot }) => {
690
+ if (!snapshot.custom || !Object.keys(snapshot.custom).length) return
691
+ await setStatementCustomFields(
692
+ ctx.container.resolve('dataEngine') as DataEngine,
693
+ entity.id,
694
+ entity.organizationId,
695
+ entity.tenantId,
696
+ snapshot.custom,
697
+ )
698
+ },
699
+ }),
700
+ }
701
+
702
+ const updateStatementCommand: CommandHandler<ScopedStatementUpdateInput, StatementCommandResult> = {
703
+ id: 'eudr.statements.update',
704
+ async prepare(rawInput, ctx) {
705
+ const { parsed } = parseWithCustomFields(statementUpdateSchema, rawInput)
706
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
707
+ const snapshot = await loadStatementSnapshot(entityManager, parsed.id)
708
+ if (snapshot) {
709
+ ensureTenantScope(ctx, snapshot.tenantId)
710
+ ensureOrganizationScope(ctx, snapshot.organizationId)
711
+ }
712
+ return snapshot ? { before: snapshot } : {}
713
+ },
714
+ async execute(rawInput, ctx) {
715
+ const { parsed, custom } = parseWithCustomFields(statementUpdateSchema, rawInput)
716
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
717
+ const record = await entityManager.findOne(EudrDueDiligenceStatement, { id: parsed.id, deletedAt: null })
718
+ if (!record) throw new CrudHttpError(404, { error: 'EUDR due diligence statement not found' })
719
+ ensureTenantScope(ctx, record.tenantId)
720
+ ensureOrganizationScope(ctx, record.organizationId)
721
+ const previousStatus = record.status
722
+
723
+ await runCrudCommandWrite({
724
+ ctx,
725
+ em: entityManager,
726
+ entityId: STATEMENT_ENTITY_ID,
727
+ action: 'updated',
728
+ scope: { tenantId: record.tenantId, organizationId: record.organizationId },
729
+ customFields: custom,
730
+ events: statementCrudEvents,
731
+ indexer: statementCrudIndexer,
732
+ sideEffect: () => ({
733
+ entity: record,
734
+ identifiers: {
735
+ id: record.id,
736
+ organizationId: record.organizationId,
737
+ tenantId: record.tenantId,
738
+ },
739
+ }),
740
+ phases: [
741
+ async () => {
742
+ const serverFields = await validateStatementUpdate(entityManager, record, parsed, custom)
743
+ applyStatementUpdate(record, parsed)
744
+ if (serverFields.submittedAt !== undefined) record.submittedAt = serverFields.submittedAt
745
+ if (serverFields.referenceIssuedAt !== undefined) record.referenceIssuedAt = serverFields.referenceIssuedAt
746
+ },
747
+ ],
748
+ })
749
+
750
+ const lifecycleEventId = record.status === previousStatus
751
+ ? null
752
+ : previousStatus === 'draft' && record.status === 'submitted'
753
+ ? 'eudr.due_diligence_statement.submitted'
754
+ : previousStatus === 'submitted' && record.status === 'available'
755
+ ? 'eudr.due_diligence_statement.reference_issued'
756
+ : previousStatus === 'available' && record.status === 'withdrawn'
757
+ ? 'eudr.due_diligence_statement.withdrawn'
758
+ : null
759
+ if (lifecycleEventId) {
760
+ await emitEudrLifecycleEvent(ctx.container, lifecycleEventId, {
761
+ id: record.id,
762
+ tenantId: record.tenantId,
763
+ organizationId: record.organizationId,
764
+ title: record.title,
765
+ referenceNumber: record.referenceNumber ?? null,
766
+ })
767
+ }
768
+
769
+ return { entityId: record.id, updatedAt: record.updatedAt }
770
+ },
771
+ captureAfter: async (_rawInput, result, ctx) => {
772
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
773
+ return loadStatementSnapshot(entityManager, result.entityId)
774
+ },
775
+ buildLog: async ({ snapshots }) => {
776
+ const before = snapshots.before as StatementSnapshot | undefined
777
+ const after = snapshots.after as StatementSnapshot | undefined
778
+ if (!before) return null
779
+ if (after && snapshotsEqual(before, after)) return { skipLog: true }
780
+ const { translate } = await resolveTranslations()
781
+ return {
782
+ actionLabel: translate('eudr.audit.statements.update', 'Update EUDR due diligence statement'),
783
+ resourceKind: 'eudr.due_diligence_statement',
784
+ resourceId: before.id,
785
+ tenantId: before.tenantId,
786
+ organizationId: before.organizationId,
787
+ snapshotBefore: before,
788
+ snapshotAfter: after ?? null,
789
+ payload: {
790
+ undo: { before, after: after ?? null } satisfies StatementUndoPayload,
791
+ },
792
+ }
793
+ },
794
+ undo: async ({ logEntry, ctx }) => {
795
+ const payload = extractUndoPayload<StatementUndoPayload>(logEntry)
796
+ const before = payload?.before
797
+ if (!before) return
798
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
799
+ let record = await entityManager.findOne(EudrDueDiligenceStatement, { id: before.id })
800
+ if (!record) {
801
+ record = entityManager.create(EudrDueDiligenceStatement, statementSeedFromSnapshot(before))
802
+ entityManager.persist(record)
803
+ } else {
804
+ ensureTenantScope(ctx, before.tenantId)
805
+ ensureOrganizationScope(ctx, before.organizationId)
806
+ restoreStatement(record, before)
807
+ }
808
+ await entityManager.flush()
809
+
810
+ const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
811
+ const resetValues = buildCustomFieldResetMap(before.custom ?? undefined, payload?.after?.custom ?? undefined)
812
+ await setStatementCustomFields(dataEngine, before.id, before.organizationId, before.tenantId, resetValues)
813
+ await emitCrudUndoSideEffects({
814
+ dataEngine,
815
+ action: 'updated',
816
+ entity: record,
817
+ identifiers: {
818
+ id: record.id,
819
+ organizationId: record.organizationId,
820
+ tenantId: record.tenantId,
821
+ },
822
+ indexer: statementCrudIndexer,
823
+ events: statementCrudEvents,
824
+ })
825
+ },
826
+ }
827
+
828
+ const deleteStatementCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, StatementCommandResult> = {
829
+ id: 'eudr.statements.delete',
830
+ async prepare(input, ctx) {
831
+ const entityId = requireId(input, 'EUDR due diligence statement id required')
832
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
833
+ const snapshot = await loadStatementSnapshot(entityManager, entityId)
834
+ if (snapshot) {
835
+ ensureTenantScope(ctx, snapshot.tenantId)
836
+ ensureOrganizationScope(ctx, snapshot.organizationId)
837
+ }
838
+ return snapshot ? { before: snapshot } : {}
839
+ },
840
+ async execute(input, ctx) {
841
+ const entityId = requireId(input, 'EUDR due diligence statement id required')
842
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
843
+ const record = await entityManager.findOne(EudrDueDiligenceStatement, { id: entityId, deletedAt: null })
844
+ if (!record) throw new CrudHttpError(404, { error: 'EUDR due diligence statement not found' })
845
+ ensureTenantScope(ctx, record.tenantId)
846
+ ensureOrganizationScope(ctx, record.organizationId)
847
+ if (record.status === 'archived') {
848
+ throw new CrudHttpError(400, { error: 'eudr.errors.archivedReadOnly' })
849
+ }
850
+ if (record.status === 'available') {
851
+ if (!isAmendWindowOpen(record.referenceIssuedAt ?? null)) {
852
+ throw new CrudHttpError(400, { error: 'eudr.errors.amendWindowElapsed' })
853
+ }
854
+ await assertNoDownstreamReferences(entityManager, record)
855
+ }
856
+
857
+ const snapshot = await loadStatementSnapshot(entityManager, entityId)
858
+ record.deletedAt = new Date()
859
+ await entityManager.flush()
860
+
861
+ const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
862
+ if (snapshot?.custom) {
863
+ const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined)
864
+ await setStatementCustomFields(dataEngine, snapshot.id, snapshot.organizationId, snapshot.tenantId, resetValues)
865
+ }
866
+ await emitCrudSideEffects({
867
+ dataEngine,
868
+ action: 'deleted',
869
+ entity: record,
870
+ identifiers: {
871
+ id: record.id,
872
+ organizationId: record.organizationId,
873
+ tenantId: record.tenantId,
874
+ },
875
+ indexer: statementCrudIndexer,
876
+ events: statementCrudEvents,
877
+ })
878
+ return { entityId: record.id, updatedAt: record.updatedAt }
879
+ },
880
+ buildLog: async ({ snapshots }) => {
881
+ const before = snapshots.before as StatementSnapshot | undefined
882
+ if (!before) return null
883
+ const { translate } = await resolveTranslations()
884
+ return {
885
+ actionLabel: translate('eudr.audit.statements.delete', 'Delete EUDR due diligence statement'),
886
+ resourceKind: 'eudr.due_diligence_statement',
887
+ resourceId: before.id,
888
+ tenantId: before.tenantId,
889
+ organizationId: before.organizationId,
890
+ snapshotBefore: before,
891
+ payload: {
892
+ undo: { before } satisfies StatementUndoPayload,
893
+ },
894
+ }
895
+ },
896
+ undo: async ({ logEntry, ctx }) => {
897
+ const payload = extractUndoPayload<StatementUndoPayload>(logEntry)
898
+ const before = payload?.before
899
+ if (!before) return
900
+ const entityManager = (ctx.container.resolve('em') as EntityManager).fork()
901
+ let record = await entityManager.findOne(EudrDueDiligenceStatement, { id: before.id })
902
+ if (!record) {
903
+ record = entityManager.create(EudrDueDiligenceStatement, statementSeedFromSnapshot(before))
904
+ entityManager.persist(record)
905
+ } else {
906
+ ensureTenantScope(ctx, before.tenantId)
907
+ ensureOrganizationScope(ctx, before.organizationId)
908
+ restoreStatement(record, before)
909
+ }
910
+ await entityManager.flush()
911
+
912
+ const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
913
+ if (before.custom) {
914
+ await setStatementCustomFields(dataEngine, before.id, before.organizationId, before.tenantId, before.custom)
915
+ }
916
+ await emitCrudUndoSideEffects({
917
+ dataEngine,
918
+ action: 'created',
919
+ entity: record,
920
+ identifiers: {
921
+ id: record.id,
922
+ organizationId: record.organizationId,
923
+ tenantId: record.tenantId,
924
+ },
925
+ indexer: statementCrudIndexer,
926
+ events: statementCrudEvents,
927
+ })
928
+ },
929
+ }
930
+
931
+ registerCommand(createStatementCommand)
932
+ registerCommand(updateStatementCommand)
933
+ registerCommand(deleteStatementCommand)