@topconsultnpm/sdkui-react 6.22.0-t1 → 6.22.0-t3

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 (286) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +3 -3
  2. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -1
  3. package/lib/components/NewComponents/ContextMenu/styles.d.ts +45 -43
  4. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +6 -6
  5. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +80 -73
  6. package/lib/components/base/Styled.d.ts +92 -76
  7. package/lib/components/base/Styled.js +7 -5
  8. package/lib/components/base/TMAccordionNew.js +7 -7
  9. package/lib/components/base/TMAlert.d.ts +32 -32
  10. package/lib/components/base/TMAlert.js +6 -8
  11. package/lib/components/base/TMAreaManager.js +53 -55
  12. package/lib/components/base/TMButton.d.ts +13 -0
  13. package/lib/components/base/TMButton.js +139 -7
  14. package/lib/components/base/TMClosableList.js +3 -3
  15. package/lib/components/base/TMCustomButton.js +40 -48
  16. package/lib/components/base/TMDataGrid.d.ts +4 -0
  17. package/lib/components/base/TMDataGrid.js +292 -12
  18. package/lib/components/base/TMDataGridExportForm.js +39 -30
  19. package/lib/components/base/TMDropDownMenu.js +1 -1
  20. package/lib/components/base/TMEditorBase.d.ts +2 -0
  21. package/lib/components/base/TMFileManager.js +5 -3
  22. package/lib/components/base/TMFileManagerDataGridView.js +12 -3
  23. package/lib/components/base/TMFileManagerThumbnailItems.js +20 -10
  24. package/lib/components/base/TMFileManagerUtils.d.ts +6 -6
  25. package/lib/components/base/TMFileManagerUtils.js +37 -1
  26. package/lib/components/base/TMLayout.d.ts +2 -2
  27. package/lib/components/base/TMLayout.js +3 -3
  28. package/lib/components/base/TMList.js +1 -1
  29. package/lib/components/base/TMListView.js +3 -3
  30. package/lib/components/base/TMModal.d.ts +11 -0
  31. package/lib/components/base/TMModal.js +47 -13
  32. package/lib/components/base/TMPanel.d.ts +6 -0
  33. package/lib/components/base/TMPanel.js +141 -28
  34. package/lib/components/base/TMPopUp.js +5 -2
  35. package/lib/components/base/TMResizableMenu.js +2 -2
  36. package/lib/components/base/TMRightSidebar.js +1 -1
  37. package/lib/components/base/TMShowAllOrMaxItemsButton.js +1 -1
  38. package/lib/components/base/TMSpinner.d.ts +2 -1
  39. package/lib/components/base/TMSpinner.js +9 -7
  40. package/lib/components/base/TMTab.js +2 -2
  41. package/lib/components/base/TMTabControl.d.ts +106 -0
  42. package/lib/components/base/TMTabControl.js +664 -0
  43. package/lib/components/base/TMToggleButton.js +2 -2
  44. package/lib/components/base/TMToolbarCard.js +3 -3
  45. package/lib/components/base/TMTooltip.d.ts +2 -1
  46. package/lib/components/base/TMTooltip.js +24 -8
  47. package/lib/components/base/TMTreeView.d.ts +13 -7
  48. package/lib/components/base/TMTreeView.js +143 -70
  49. package/lib/components/base/TMWaitPanel.js +3 -3
  50. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  51. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  52. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  53. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  54. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  55. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  56. package/lib/components/base/waitPanel/index.d.ts +1 -0
  57. package/lib/components/base/waitPanel/index.js +1 -0
  58. package/lib/components/choosers/TMCultureIDPicker.js +2 -2
  59. package/lib/components/choosers/TMDataListItemChooser.js +1 -1
  60. package/lib/components/choosers/TMDataListItemPicker.js +2 -2
  61. package/lib/components/choosers/TMDcmtTypeChooser.js +5 -4
  62. package/lib/components/choosers/TMDiskChooser.js +1 -1
  63. package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
  64. package/lib/components/choosers/TMDistinctValues.js +157 -141
  65. package/lib/components/choosers/TMDynDataListItemChooser.js +1 -1
  66. package/lib/components/choosers/TMImageIDChooser.js +1 -1
  67. package/lib/components/choosers/TMMetadataChooser.js +3 -3
  68. package/lib/components/choosers/TMPathChooser.js +17 -34
  69. package/lib/components/choosers/TMQuickSearchInfo.d.ts +13 -0
  70. package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
  71. package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
  72. package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
  73. package/lib/components/choosers/TMRelationChooser.js +1 -1
  74. package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
  75. package/lib/components/choosers/TMSelectedValuesSummary.js +89 -0
  76. package/lib/components/choosers/TMUserChooser.js +1 -1
  77. package/lib/components/editors/TMCheckBox.js +1 -1
  78. package/lib/components/editors/TMDateBox.d.ts +16 -1
  79. package/lib/components/editors/TMDateBox.js +90 -22
  80. package/lib/components/editors/TMDropDown.d.ts +3 -0
  81. package/lib/components/editors/TMDropDown.js +197 -5
  82. package/lib/components/editors/TMEditorStyled.d.ts +21 -43
  83. package/lib/components/editors/TMFormulaEditor.js +1 -1
  84. package/lib/components/editors/TMHtmlContentDisplay.js +1 -1
  85. package/lib/components/editors/TMLocalizedTextBox.js +1 -1
  86. package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
  87. package/lib/components/editors/TMMetadataEditor.js +27 -23
  88. package/lib/components/editors/TMMetadataValues.js +6 -6
  89. package/lib/components/editors/TMRadioButton.js +2 -2
  90. package/lib/components/editors/TMSummary.js +3 -3
  91. package/lib/components/editors/TMTextArea.d.ts +1 -0
  92. package/lib/components/editors/TMTextArea.js +25 -17
  93. package/lib/components/editors/TMTextBox.d.ts +2 -0
  94. package/lib/components/editors/TMTextBox.js +32 -21
  95. package/lib/components/editors/TMTextExpression.js +1 -1
  96. package/lib/components/features/archive/TMArchive.js +24 -5
  97. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +29 -0
  98. package/lib/components/features/assistant/TMHelpOnLineChat.js +1384 -0
  99. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +4 -4
  100. package/lib/components/features/assistant/TMToppySpeechBubble.js +1 -1
  101. package/lib/components/features/assistant/dataset/Designer-Suggested-Questions.json +102 -0
  102. package/lib/components/features/assistant/dataset/Login-Suggested-Questions.json +32 -0
  103. package/lib/components/features/assistant/dataset/Surfer-Suggested-Questions.json +102 -0
  104. package/lib/components/features/documents/TMBatchUpdateForm.js +22 -9
  105. package/lib/components/features/documents/TMCopyToFolderForm.js +50 -42
  106. package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
  107. package/lib/components/features/documents/TMDcmtBlog.js +4 -4
  108. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  109. package/lib/components/features/documents/TMDcmtForm.js +281 -113
  110. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
  111. package/lib/components/features/documents/TMDcmtFormActionButtons.js +79 -9
  112. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
  113. package/lib/components/features/documents/TMDcmtIcon.js +8 -8
  114. package/lib/components/features/documents/TMDcmtPreview.d.ts +7 -5
  115. package/lib/components/features/documents/TMDcmtPreview.js +16 -10
  116. package/lib/components/features/documents/TMDcmtTasks.js +2 -9
  117. package/lib/components/features/documents/TMDownloadRelationViewerSection.js +1 -1
  118. package/lib/components/features/documents/TMFileUploader.js +7 -7
  119. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  120. package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -108
  121. package/lib/components/features/documents/TMMasterInfoFields.d.ts +20 -0
  122. package/lib/components/features/documents/TMMasterInfoFields.js +185 -0
  123. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
  124. package/lib/components/features/documents/TMMergeToPdfForm.js +83 -56
  125. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +46 -0
  126. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +320 -0
  127. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +370 -0
  128. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +283 -0
  129. package/lib/components/features/documents/TMRelationViewer.d.ts +29 -9
  130. package/lib/components/features/documents/TMRelationViewer.js +310 -208
  131. package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +1 -1
  132. package/lib/components/features/documents/relationTreeState.d.ts +32 -0
  133. package/lib/components/features/documents/relationTreeState.js +89 -0
  134. package/lib/components/features/search/TMMetadataOutputForm.js +1 -1
  135. package/lib/components/features/search/TMMetadataSorterForm.js +1 -1
  136. package/lib/components/features/search/TMSavedQuerySelector.js +2 -2
  137. package/lib/components/features/search/TMSearch.d.ts +1 -1
  138. package/lib/components/features/search/TMSearch.js +172 -34
  139. package/lib/components/features/search/TMSearchQueryEditor.js +7 -5
  140. package/lib/components/features/search/TMSearchQueryPanel.d.ts +14 -4
  141. package/lib/components/features/search/TMSearchQueryPanel.js +153 -62
  142. package/lib/components/features/search/TMSearchResult.d.ts +21 -0
  143. package/lib/components/features/search/TMSearchResult.js +461 -152
  144. package/lib/components/features/search/TMSignatureInfoContent.js +1 -1
  145. package/lib/components/features/search/TMTreeSelector.js +130 -83
  146. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  147. package/lib/components/features/tasks/TMTaskForm.js +1 -1
  148. package/lib/components/features/tasks/TMTaskFormUtils.js +3 -3
  149. package/lib/components/features/tasks/TMTasksAgenda.js +1 -1
  150. package/lib/components/features/tasks/TMTasksCalendar.js +1 -1
  151. package/lib/components/features/tasks/TMTasksHeader.js +1 -1
  152. package/lib/components/features/tasks/TMTasksUtilsView.js +1 -1
  153. package/lib/components/features/tasks/TMTasksView.js +97 -102
  154. package/lib/components/features/wg/TMWGsCopyMoveForm.js +92 -92
  155. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  156. package/lib/components/features/workflow/TMWorkflowPopup.js +9 -5
  157. package/lib/components/features/workflow/diagram/ConnectionComponent.js +3 -3
  158. package/lib/components/features/workflow/diagram/DiagramItemComponent.js +3 -3
  159. package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
  160. package/lib/components/features/workflow/diagram/DiagramItemSvgContent.js +1 -1
  161. package/lib/components/features/workflow/diagram/RecipientList.js +1 -1
  162. package/lib/components/features/workflow/diagram/WFDiagram.js +4 -4
  163. package/lib/components/features/workflow/diagram/WorkitemRecipientsEditor.d.ts +3 -1
  164. package/lib/components/forms/Login/ChangePasswordInputs.d.ts +3 -1
  165. package/lib/components/forms/Login/Menu.js +1 -1
  166. package/lib/components/forms/Login/OTPReader.js +1 -1
  167. package/lib/components/forms/Login/PasswordStrengthChecker.js +1 -1
  168. package/lib/components/forms/Login/RapidAccessLogin.js +2 -2
  169. package/lib/components/forms/Login/RecoverPasswordFlow.js +2 -2
  170. package/lib/components/forms/Login/SelectBox.js +1 -1
  171. package/lib/components/forms/Login/StepIndicator.js +1 -1
  172. package/lib/components/forms/Login/TMLoginForm.js +7 -7
  173. package/lib/components/forms/Login/TextBox.js +1 -1
  174. package/lib/components/forms/TMApplyForm.js +3 -3
  175. package/lib/components/forms/TMChooserForm.js +1 -1
  176. package/lib/components/forms/TMResultDialog.js +1 -1
  177. package/lib/components/forms/TMSaveForm.js +3 -3
  178. package/lib/components/grids/TMBlogAttachments.js +14 -3
  179. package/lib/components/grids/TMBlogHeader.js +5 -1
  180. package/lib/components/grids/TMBlogsPost.d.ts +2 -0
  181. package/lib/components/grids/TMBlogsPost.js +122 -99
  182. package/lib/components/grids/TMBlogsPostUtils.d.ts +1 -0
  183. package/lib/components/grids/TMBlogsPostUtils.js +15 -4
  184. package/lib/components/grids/TMRecentsManager.js +4 -4
  185. package/lib/components/grids/TMValidationItemsList.js +1 -1
  186. package/lib/components/index.d.ts +6 -0
  187. package/lib/components/index.js +5 -0
  188. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  189. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +25 -15
  190. package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
  191. package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +4 -5
  192. package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +1 -1
  193. package/lib/components/layout/panelManager/TMPanelWrapper.js +3 -3
  194. package/lib/components/layout/panelManager/types.d.ts +3 -0
  195. package/lib/components/layout/panelManager/utils.js +13 -11
  196. package/lib/components/pages/TMPage.js +30 -32
  197. package/lib/components/query/TMQueryCountButton.js +1 -1
  198. package/lib/components/query/TMQueryEditor.d.ts +17 -10
  199. package/lib/components/query/TMQueryEditor.js +9 -8
  200. package/lib/components/sidebar/TMCommandsPanel.d.ts +4 -4
  201. package/lib/components/sidebar/TMCommandsPanel.js +1 -1
  202. package/lib/components/sidebar/TMHeader.d.ts +1 -0
  203. package/lib/components/sidebar/TMHeader.js +37 -27
  204. package/lib/components/sidebar/TMSidebar.js +1 -1
  205. package/lib/components/sidebar/TMSidebarItem.js +1 -1
  206. package/lib/components/viewers/TMMidViewer.js +6 -3
  207. package/lib/components/viewers/TMTidViewer.js +89 -28
  208. package/lib/components/wizard/TMStepIndicator.js +3 -3
  209. package/lib/css/tm-sdkui.css +1 -1
  210. package/lib/devextreme-license.d.ts +1 -0
  211. package/lib/devextreme-license.js +3 -0
  212. package/lib/helper/Enum_Localizator.d.ts +4 -4
  213. package/lib/helper/Enum_Localizator.js +1 -0
  214. package/lib/helper/Globalization.d.ts +1 -1
  215. package/lib/helper/SDKUI_Globals.d.ts +110 -0
  216. package/lib/helper/SDKUI_Globals.js +151 -0
  217. package/lib/helper/SDKUI_Localizator.d.ts +756 -677
  218. package/lib/helper/SDKUI_Localizator.js +790 -0
  219. package/lib/helper/TMAppUpdatedModal.d.ts +2 -0
  220. package/lib/helper/TMAppUpdatedModal.js +134 -0
  221. package/lib/helper/TMIcons.d.ts +1 -0
  222. package/lib/helper/TMIcons.js +4 -3
  223. package/lib/helper/TMPdfViewer.js +1 -1
  224. package/lib/helper/TMToppyMessage.js +1 -1
  225. package/lib/helper/TMUtils.d.ts +51 -4
  226. package/lib/helper/TMUtils.js +273 -12
  227. package/lib/helper/ZipManager.d.ts +1 -1
  228. package/lib/helper/ZipManager.js +2 -1
  229. package/lib/helper/checkinCheckoutManager.d.ts +1 -1
  230. package/lib/helper/checkinCheckoutManager.js +1 -1
  231. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  232. package/lib/helper/dataGridSearchHelper.js +51 -0
  233. package/lib/helper/dcmtsHelper.d.ts +22 -0
  234. package/lib/helper/dcmtsHelper.js +60 -1
  235. package/lib/helper/helpers.d.ts +24 -2
  236. package/lib/helper/helpers.js +59 -3
  237. package/lib/helper/index.d.ts +2 -0
  238. package/lib/helper/index.js +2 -0
  239. package/lib/helper/queryHelper.d.ts +6 -1
  240. package/lib/helper/queryHelper.js +8 -3
  241. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  242. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  243. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  244. package/lib/hooks/useArchiveListForm.js +46 -0
  245. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  246. package/lib/hooks/useCaseFlowApprove.js +157 -0
  247. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  248. package/lib/hooks/useCheckInOutOperations.js +14 -34
  249. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  250. package/lib/hooks/useDataUserIdItem.js +1 -0
  251. package/lib/hooks/useDcmtOperations.d.ts +3 -12
  252. package/lib/hooks/useDcmtOperations.js +187 -179
  253. package/lib/hooks/useDocumentOperations.d.ts +5 -0
  254. package/lib/hooks/useDocumentOperations.js +64 -33
  255. package/lib/hooks/useInputDialog.js +1 -1
  256. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +98 -0
  257. package/lib/hooks/useMultiMasterDetailDcmts.js +758 -0
  258. package/lib/hooks/usePreventFileDrop.js +9 -0
  259. package/lib/hooks/useQueryParametersDialog.js +1 -1
  260. package/lib/hooks/useRelatedDocuments.d.ts +1 -0
  261. package/lib/hooks/useRelatedDocuments.js +22 -27
  262. package/lib/hooks/useShowDossiersV5.d.ts +12 -0
  263. package/lib/hooks/useShowDossiersV5.js +37 -0
  264. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  265. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  266. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  267. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  268. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  269. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  270. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  271. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  272. package/lib/hooks/useWorkflowApprove.js +33 -17
  273. package/lib/index.d.ts +2 -0
  274. package/lib/index.js +5 -2
  275. package/lib/services/caseflow/CaseFlowService.d.ts +277 -0
  276. package/lib/services/caseflow/CaseFlowService.js +761 -0
  277. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  278. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  279. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  280. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  281. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  282. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  283. package/lib/services/index.d.ts +4 -0
  284. package/lib/services/index.js +4 -0
  285. package/lib/services/platform_services.d.ts +4 -4
  286. package/package.json +16 -13
@@ -0,0 +1,761 @@
1
+ import { SDK_Globals, BlogPost, CaseFlowDcmt, CaseFlowInstanceDescriptor, CaseFlowParticipant, CaseFlowWIOperationDescriptor, MetadataValueDescriptor, CF_ParticipantRoles, CF_States, CF_WIOperations, CF_WorkItemCategories, CF_WorkItemStates, UserListCacheService, DcmtTypeListCacheService, } from '@topconsultnpm/sdk-ts';
2
+ import { CaseFlowTemplateCacheService } from './CaseFlowTemplateCacheService';
3
+ import { getTemplateNameSync, primeTemplateName, primeTemplateNames, primeActionNames } from './caseFlowNameCache';
4
+ export class CaseFlowService {
5
+ // ----- Template / tipi documento (invariati: cache condivise) ------------
6
+ /** Elenco dei template reali (via CaseFlowTemplateCacheService). */
7
+ static async getTemplates() {
8
+ const templates = await CaseFlowTemplateCacheService.GetAllAsync();
9
+ // Popola la cache dei nomi template, così i componenti possono risolvere
10
+ // `templateName` dal `cftid` senza augmentare i descrittori del SDK.
11
+ primeTemplateNames(templates);
12
+ return templates;
13
+ }
14
+ /**
15
+ * Singolo template per id. Usa GetAsync (non un find su GetAllAsync): così i
16
+ * template di cui l'utente NON può creare istanze — ma di cui è destinatario
17
+ * di un workitem (es. Completamento dati) — vengono risolti on-demand via
18
+ * RetrieveAsync mirata invece di restituire undefined.
19
+ */
20
+ static async getTemplate(id) {
21
+ const template = await CaseFlowTemplateCacheService.GetAsync(id);
22
+ if (template)
23
+ primeTemplateNames([template]);
24
+ return template;
25
+ }
26
+ /** Invalida la cache dei template (dopo creazione/modifica/eliminazione). */
27
+ static invalidateTemplatesCache() {
28
+ CaseFlowTemplateCacheService.Invalidate();
29
+ }
30
+ /** DcmtTypeDescriptor (copertina) per il TID indicato. */
31
+ static async getDcmtTypeAsync(tid) {
32
+ try {
33
+ return await DcmtTypeListCacheService.GetAsync(tid, true);
34
+ }
35
+ catch (e) {
36
+ console.warn(`[CaseFlow] DcmtType ${tid} non disponibile:`, e);
37
+ return undefined;
38
+ }
39
+ }
40
+ // ----- Lettura istanze ---------------------------------------------------
41
+ /** Tutte le istanze visibili all'utente (unione di tutti i template). */
42
+ static async getCaseflows() {
43
+ return this.loadAllInstances();
44
+ }
45
+ /**
46
+ * Singola istanza COMPLETA per id. `RetrieveAllAsync` (usata dalla dashboard)
47
+ * porta i dati necessari alle card, ma per il dettaglio completo
48
+ * (documenti/bacheca/timeline) serve comunque una `RetrieveAsync(cftid, id)`
49
+ * che idrata l'istanza. Il `cftid` va passato quando noto (evita una
50
+ * scansione); in mancanza viene risolto dalla lista.
51
+ *
52
+ * ATTENZIONE sui `workItems` della RetrieveAll: contengono i soli workitem
53
+ * ANCORA APERTI, non lo storico. Una pratica conclusa arriva con
54
+ * `workItems: []`, perché non ha più nulla di pendente. Chi deve ricostruire
55
+ * la STORIA di una pratica (chi ha fatto cosa e quando) non può quindi basarsi
56
+ * sull'elenco: serve la Retrieve completa, e la fonte dei passaggi già
57
+ * avvenuti sono i messaggi di sistema della bacheca (`blogPosts` con
58
+ * allegato di riferimento all'azione).
59
+ */
60
+ static async getCaseflow(id, cftid) {
61
+ try {
62
+ return await this.retrieveCaseflow(id, cftid);
63
+ }
64
+ catch (e) {
65
+ console.warn(`[CaseFlow] RetrieveAsync istanza id=${id} (cftid=${cftid}) fallita:`, e);
66
+ return undefined;
67
+ }
68
+ }
69
+ /**
70
+ * Come {@link getCaseflow} ma PROPAGA l'errore invece di inghiottirlo.
71
+ * Da usare nei punti in cui l'utente apre esplicitamente l'istanza (apertura
72
+ * scheda, deep link): il fallimento della Retrieve deve essere notificato,
73
+ * non mascherato da una vista incompleta ("non sei un partecipante").
74
+ */
75
+ static async retrieveCaseflow(id, cftid) {
76
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
77
+ if (!engine)
78
+ return undefined;
79
+ let resolvedCftid = cftid;
80
+ if (resolvedCftid == null) {
81
+ const all = await this.loadAllInstances();
82
+ resolvedCftid = all.find(cf => cf.id === id)?.cftid;
83
+ }
84
+ if (resolvedCftid == null) {
85
+ console.warn(`[CaseFlow] impossibile risolvere il cftid per l'istanza id=${id}.`);
86
+ return undefined;
87
+ }
88
+ const full = await engine.RetrieveAsync(resolvedCftid, id);
89
+ if (!full)
90
+ return undefined;
91
+ if (full.cftid == null)
92
+ full.cftid = resolvedCftid;
93
+ // Risolvi il template (best-effort, on-demand se non creabile dall'utente)
94
+ // per l'etichetta del modello e i nomi azione.
95
+ const tpl = await CaseFlowTemplateCacheService.GetAsync(full.cftid);
96
+ if (tpl) {
97
+ primeTemplateName(tpl.id, tpl.name);
98
+ primeActionNames(tpl);
99
+ }
100
+ await this.resolveUserNames([full]);
101
+ return full;
102
+ }
103
+ /** Istanze di cui l'utente indicato è proprietario. */
104
+ static async getMyCaseflows(userId) {
105
+ const all = await this.loadAllInstances();
106
+ return all.filter(cf => cf.ownerID === userId);
107
+ }
108
+ /** Ricerca avanzata sulle istanze applicando i filtri indicati. */
109
+ static async searchCaseflows(criteria) {
110
+ const all = await this.loadAllInstances();
111
+ const lowerIncludes = (haystack, needle) => (haystack ?? '').toLowerCase().includes(needle.toLowerCase());
112
+ return all.filter(cf => {
113
+ if (criteria.templateId != null && cf.cftid !== criteria.templateId)
114
+ return false;
115
+ if (criteria.name && !lowerIncludes(cf.name, criteria.name))
116
+ return false;
117
+ if (criteria.description && !lowerIncludes(cf.description, criteria.description))
118
+ return false;
119
+ if (criteria.state && cf.state !== criteria.state)
120
+ return false;
121
+ if (criteria.dateFrom) {
122
+ const from = new Date(criteria.dateFrom);
123
+ if (cf.creationTime && cf.creationTime < from)
124
+ return false;
125
+ }
126
+ if (criteria.dateTo) {
127
+ const to = new Date(criteria.dateTo);
128
+ if (cf.creationTime && cf.creationTime > to)
129
+ return false;
130
+ }
131
+ if (criteria.metadataFilters && criteria.metadataFilters.length > 0) {
132
+ for (const mf of criteria.metadataFilters) {
133
+ if (!mf.value.trim())
134
+ continue;
135
+ const cfMeta = (cf.cover ?? []).find(m => m.mid === mf.mid);
136
+ if (!cfMeta || !lowerIncludes(cfMeta.value, mf.value))
137
+ return false;
138
+ }
139
+ }
140
+ if (criteria.freeTextMetadata && criteria.freeTextMetadata.trim()) {
141
+ const needle = criteria.freeTextMetadata.trim();
142
+ const matchesCommon = lowerIncludes(cf.name, needle)
143
+ || lowerIncludes(cf.description, needle)
144
+ || lowerIncludes(cf.ownerName, needle)
145
+ || lowerIncludes(getTemplateNameSync(cf.cftid), needle);
146
+ const matchesMeta = (cf.cover ?? []).some(m => lowerIncludes(m.value, needle) || lowerIncludes(m.metadataName, needle));
147
+ if (!matchesCommon && !matchesMeta)
148
+ return false;
149
+ }
150
+ return true;
151
+ });
152
+ }
153
+ /** WorkItem pendenti assegnati all'utente, raggruppati per istanza. */
154
+ static async getMyPendingWorkItems(userId) {
155
+ return this.collectWorkItemsByState(userId, CF_WorkItemStates.Pending);
156
+ }
157
+ /**
158
+ * Richieste di "maggiori informazioni" INVIATE dall'utente e ancora in
159
+ * attesa di risposta, raggruppate per istanza.
160
+ *
161
+ * Non basta filtrare `toUserID === userId && state === MoreInfo` (il solo
162
+ * workitem "padre" bloccato intestato a me): a seconda di come il backend
163
+ * materializza la richiesta, l'unico workitem visibile può essere quello
164
+ * INVIATO al destinatario (`fromUserID === userId`, categoria MoreInfo,
165
+ * ancora Pending). In quel caso il filtro su `toUserID` non lo intercetta e
166
+ * l'istanza sparisce da TUTTE le sezioni di "Per te" (non è più Pending per
167
+ * me, non è MoreInfo intestato a me, e non ne sono owner). Consideriamo
168
+ * quindi entrambe le rappresentazioni, preferendo il figlio inviato (che
169
+ * porta il destinatario) ed evitando di mostrare padre + figlio della
170
+ * stessa richiesta.
171
+ */
172
+ static async getMyMoreInfoRequests(userId) {
173
+ const all = await this.loadAllInstances();
174
+ const result = [];
175
+ for (const cf of all) {
176
+ const wis = cf.workItems ?? [];
177
+ // Richieste che HO inviato e attendono risposta da un altro utente.
178
+ const sent = wis.filter(wi => wi.fromUserID === userId
179
+ && wi.workItemCategory === CF_WorkItemCategories.MoreInfo
180
+ && wi.state === CF_WorkItemStates.Pending);
181
+ // Mia attività messa "in attesa" (workitem padre intestato a me).
182
+ const waitingParents = wis.filter(wi => wi.toUserID === userId && wi.state === CF_WorkItemStates.MoreInfo);
183
+ const parentsWithoutChild = waitingParents.filter(p => !sent.some(s => s.parentWorkItemID === p.id));
184
+ const workItems = [...sent, ...parentsWithoutChild];
185
+ if (workItems.length > 0)
186
+ result.push({ caseflow: cf, workItems });
187
+ }
188
+ return result;
189
+ }
190
+ static async collectWorkItemsByState(userId, state) {
191
+ const all = await this.loadAllInstances();
192
+ const result = [];
193
+ for (const cf of all) {
194
+ const workItems = (cf.workItems ?? []).filter(wi => wi.toUserID === userId && wi.state === state);
195
+ if (workItems.length > 0)
196
+ result.push({ caseflow: cf, workItems });
197
+ }
198
+ return result;
199
+ }
200
+ // ----- Creazione / eliminazione / aggiornamento istanza ------------------
201
+ /**
202
+ * Crea una nuova istanza a partire da un template tramite CreateAsync.
203
+ * I metadati di copertina (metadataValues: mid -> valore) vengono passati
204
+ * nel campo `cover`. Il backend materializza azioni/stati/partecipanti dal
205
+ * template identificato da `cftid`.
206
+ * @returns id della nuova istanza.
207
+ */
208
+ static async CreateCaseFlowInstance(template, instanceName, instanceDescription, metadataValues) {
209
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
210
+ if (!engine)
211
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
212
+ if (template.id == null)
213
+ throw new Error('Template privo di id (cftid) per la creazione dell\'istanza.');
214
+ const d = new CaseFlowInstanceDescriptor();
215
+ d.cftid = template.id;
216
+ d.name = instanceName;
217
+ d.description = instanceDescription ?? '';
218
+ d.state = CF_States.New;
219
+ d.progress = 0;
220
+ d.cover = Object.entries(metadataValues).map(([mid, value]) => MetadataValueDescriptor.fromJS({ mid: Number(mid), value }));
221
+ const id = await engine.CreateAsync(d);
222
+ return id ?? 0;
223
+ }
224
+ /**
225
+ * Avvia un'istanza tramite StartAsync(cftid, id): il backend porta la pratica
226
+ * da `New` a `InProgress` e materializza i workitem della prima azione.
227
+ *
228
+ * NON va chiamata dopo `CreateCaseFlowInstance`: la Create dell'API REST
229
+ * esegue già lo start internamente, quindi un'istanza appena creata è
230
+ * ancora `New` solo nel descrittore inviato, non lato server. Questa resta
231
+ * per le istanze che si trovano davvero in `New` (avvio differito); sulle
232
+ * altre il backend rifiuta.
233
+ */
234
+ static async startCaseflow(caseFlowId, cftid) {
235
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
236
+ if (!engine)
237
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
238
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
239
+ if (resolvedCftid == null)
240
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
241
+ await engine.StartAsync(resolvedCftid, caseFlowId);
242
+ }
243
+ /**
244
+ * Elimina un'istanza tramite DeleteAsync(cftid, id). Il cftid è preso dal
245
+ * template fornito (se disponibile) oppure risolto dall'istanza.
246
+ */
247
+ static async DeleteCaseFlowInstance(instanceId, template) {
248
+ if (!instanceId)
249
+ throw new Error('instanceId non valido per la cancellazione.');
250
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
251
+ if (!engine)
252
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
253
+ const cftid = template?.id ?? (await this.getCaseflow(instanceId))?.cftid;
254
+ if (cftid == null)
255
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${instanceId}.`);
256
+ await engine.DeleteAsync(cftid, instanceId);
257
+ return { instanceDID: instanceId, complete: true, orphaned: [] };
258
+ }
259
+ /** Aggiorna un'istanza esistente tramite UpdateAsync. */
260
+ static async saveCaseflow(caseflow) {
261
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
262
+ if (!engine)
263
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
264
+ await engine.UpdateAsync(caseflow);
265
+ }
266
+ // ----- Bacheca (blog dell'istanza) ---------------------------------------
267
+ /**
268
+ * Aggiunge un post in bacheca tramite BlogPostAddAsync(cftid, id, bp). Il
269
+ * cftid è passato quando noto (evita una Retrieve) oppure risolto dall'istanza.
270
+ * @returns il post creato (transitorio: la ricarica leggerà quelli reali).
271
+ */
272
+ static async addBlogPost(caseFlowId, text, authorId, authorName, attachments, cftid) {
273
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
274
+ if (!engine)
275
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
276
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
277
+ if (resolvedCftid == null)
278
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
279
+ const attachmentsJson = attachments?.map(a => a.toJSON()) ?? [];
280
+ const bp = BlogPost.fromJS({
281
+ ownerID: authorId,
282
+ ownerName: authorName,
283
+ description: text,
284
+ isDel: 0,
285
+ isSys: 0,
286
+ attachments: attachmentsJson,
287
+ });
288
+ const newId = await engine.BlogPostAddAsync(resolvedCftid, caseFlowId, bp);
289
+ // Post transitorio: rispecchia quanto inviato in attesa della ricarica.
290
+ return BlogPost.fromJS({
291
+ id: newId ?? 0,
292
+ ownerID: authorId,
293
+ ownerName: authorName,
294
+ description: text,
295
+ creationTime: new Date().toISOString(),
296
+ isDel: 0,
297
+ isSys: 0,
298
+ attachments: attachmentsJson,
299
+ });
300
+ }
301
+ /**
302
+ * Elimina (o ripristina) un post in bacheca tramite
303
+ * BlogPostDeleteOrUndeleteAsync(cftid, id, postID, del).
304
+ */
305
+ static async deleteOrUndeleteBlogPost(caseFlowId, postId, del, cftid) {
306
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
307
+ if (!engine)
308
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
309
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
310
+ if (resolvedCftid == null)
311
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
312
+ await engine.BlogPostDeleteOrUndeleteAsync(resolvedCftid, caseFlowId, postId, del);
313
+ }
314
+ // ----- Documenti della pratica --------------------------------------------
315
+ //
316
+ // Come per i partecipanti, l'engine espone UNA sola API,
317
+ // DcmtAddOrRemoveAsync(cftid, id, tid, did, remove): con remove=false aggancia
318
+ // il documento all'istanza, con remove=true lo sgancia.
319
+ /**
320
+ * Aggancia un documento (nuovo archiviato o già presente in archivio)
321
+ * all'istanza tramite DcmtAddOrRemoveAsync(remove=false).
322
+ * @returns descrittore transitorio del documento agganciato (la ricarica
323
+ * dell'istanza leggerà quello reale).
324
+ */
325
+ static async addDcmtToInstance(caseFlowId, tid, did, cftid) {
326
+ await this.dcmtAddOrRemove(caseFlowId, tid, did, false, cftid);
327
+ return CaseFlowDcmt.fromJS({ tid, did, creationTime: new Date().toISOString() });
328
+ }
329
+ /** Sgancia un documento dall'istanza tramite DcmtAddOrRemoveAsync(remove=true). */
330
+ static async removeDcmtFromInstance(caseFlowId, tid, did, cftid) {
331
+ await this.dcmtAddOrRemove(caseFlowId, tid, did, true, cftid);
332
+ }
333
+ // ----- Regole documenti attesi dell'istanza (dcmtRules) -------------------
334
+ //
335
+ // Finché l'istanza non ha regole proprie (dcmtRules assente o vuota) i
336
+ // documenti attesi — con min e max — sono i dcmts del template. Un
337
+ // supervisor può personalizzarle per la SOLA istanza tramite l'unica API
338
+ // DcmtRuleAddOrRemoveAsync(cftid, id, tid, min, max, remove): con
339
+ // remove=false funge da UPSERT della regola per il TID, con remove=true la
340
+ // rimuove. Rimuovendo tutte le regole l'istanza torna a seguire il template.
341
+ /** Aggiunge o aggiorna (upsert) la regola documenti del TID sulla sola istanza. */
342
+ static async saveDcmtRule(caseFlowId, tid, min, max, cftid) {
343
+ await this.dcmtRuleAddOrRemove(caseFlowId, tid, min, max, false, cftid);
344
+ }
345
+ /** Rimuove la regola documenti del TID dalla sola istanza. */
346
+ static async removeDcmtRule(caseFlowId, tid, cftid) {
347
+ await this.dcmtRuleAddOrRemove(caseFlowId, tid, 0, 0, true, cftid);
348
+ }
349
+ /**
350
+ * Chiama DcmtRuleAddOrRemoveAsync(cftid, id, tid, min, max, remove)
351
+ * risolvendo il cftid quando non passato (come per bacheca e partecipanti).
352
+ */
353
+ static async dcmtRuleAddOrRemove(caseFlowId, tid, min, max, remove, cftid) {
354
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
355
+ if (!engine)
356
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
357
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
358
+ if (resolvedCftid == null)
359
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
360
+ await engine.DcmtRuleAddOrRemoveAsync(resolvedCftid, caseFlowId, tid, min, max, remove);
361
+ }
362
+ /**
363
+ * Chiama DcmtAddOrRemoveAsync(cftid, id, tid, did, remove) risolvendo il
364
+ * cftid quando non passato (come per bacheca e partecipanti).
365
+ */
366
+ static async dcmtAddOrRemove(caseFlowId, tid, did, remove, cftid) {
367
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
368
+ if (!engine)
369
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
370
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
371
+ if (resolvedCftid == null)
372
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
373
+ await engine.DcmtAddOrRemoveAsync(resolvedCftid, caseFlowId, tid, did, remove);
374
+ }
375
+ /**
376
+ * Allega un file alla pratica tramite AttachAddAsync(cftid, id, ...).
377
+ * @returns il DID dell'allegato appena archiviato.
378
+ */
379
+ static async addAttachment(caseFlowId, file, name, description, cftid) {
380
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
381
+ if (!engine)
382
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
383
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
384
+ if (resolvedCftid == null)
385
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
386
+ return await engine.AttachAddAsync(resolvedCftid, caseFlowId, name, description, file);
387
+ }
388
+ /**
389
+ * Toglie l'allegato dalla pratica tramite AttachRemoveAsync(cftid, id, attachDID).
390
+ * A differenza dello sgancio di un documento d'archivio, qui il file viene
391
+ * anche cancellato: un allegato fuori dalla sua pratica non ha più senso.
392
+ */
393
+ static async removeAttachment(caseFlowId, attachDID, cftid) {
394
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
395
+ if (!engine)
396
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
397
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
398
+ if (resolvedCftid == null)
399
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
400
+ await engine.AttachRemoveAsync(resolvedCftid, caseFlowId, attachDID);
401
+ }
402
+ // ----- Partecipanti ------------------------------------------------------
403
+ //
404
+ // Per scelta del backend l'engine espone UNA sola API,
405
+ // ParticipantsAddOrRemoveAsync(cftid, id, d, remove): con remove=false funge
406
+ // da UPSERT (aggiunge il partecipante oppure ne aggiorna ruolo/permessi Guest
407
+ // se già presente), con remove=true lo rimuove. Non esiste — né esisterà — un
408
+ // metodo di Update dedicato: modifica ruolo e permessi passano dallo stesso
409
+ // upsert. Il partecipante identifica l'utente col campo `id` (ex `userId`).
410
+ /**
411
+ * Aggiunge un partecipante all'istanza (o ne aggiorna ruolo/permessi se già
412
+ * presente) tramite ParticipantsAddOrRemoveAsync(remove=false).
413
+ */
414
+ static async addParticipant(caseFlowId, userId, userName, role, guestPermissions, cftid) {
415
+ const participant = CaseFlowParticipant.fromJS({
416
+ id: userId,
417
+ name: userName,
418
+ role,
419
+ guestPermissions: guestPermissions?.toJSON(),
420
+ });
421
+ await this.participantsAddOrRemove(caseFlowId, participant, false, cftid);
422
+ return participant;
423
+ }
424
+ /** Rimuove un partecipante dall'istanza tramite ParticipantsAddOrRemoveAsync(remove=true). */
425
+ static async removeParticipant(caseFlowId, userId, cftid) {
426
+ const participant = CaseFlowParticipant.fromJS({ id: userId });
427
+ await this.participantsAddOrRemove(caseFlowId, participant, true, cftid);
428
+ }
429
+ /**
430
+ * Cambia il ruolo di un partecipante ri-applicando l'upsert col nuovo ruolo
431
+ * (non esiste un'Update dedicata). Passando a Guest si conservano/impostano i
432
+ * relativi permessi.
433
+ */
434
+ static async changeParticipantRole(caseFlowId, userId, role, guestPermissions, cftid) {
435
+ const participant = CaseFlowParticipant.fromJS({
436
+ id: userId,
437
+ role,
438
+ guestPermissions: role === CF_ParticipantRoles.Guest ? guestPermissions?.toJSON() : undefined,
439
+ });
440
+ await this.participantsAddOrRemove(caseFlowId, participant, false, cftid);
441
+ }
442
+ /** Aggiorna i permessi Guest di un partecipante (upsert col ruolo Guest). */
443
+ static async updateParticipantGuestPermissions(caseFlowId, userId, permissions, cftid) {
444
+ const participant = CaseFlowParticipant.fromJS({
445
+ id: userId,
446
+ role: CF_ParticipantRoles.Guest,
447
+ guestPermissions: permissions.toJSON(),
448
+ });
449
+ await this.participantsAddOrRemove(caseFlowId, participant, false, cftid);
450
+ }
451
+ /**
452
+ * Chiama ParticipantsAddOrRemoveAsync(cftid, id, d, remove) risolvendo il
453
+ * cftid quando non passato (come per la bacheca).
454
+ */
455
+ static async participantsAddOrRemove(caseFlowId, participant, remove, cftid) {
456
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
457
+ if (!engine)
458
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
459
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
460
+ if (resolvedCftid == null)
461
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
462
+ await engine.ParticipantsAddOrRemoveAsync(resolvedCftid, caseFlowId, participant, remove);
463
+ }
464
+ // ----- Azioni personali dell'utente (Preferiti / Segui) -------------------
465
+ //
466
+ // Riguardano la relazione fra UTENTE e istanza, non il ciclo di vita della
467
+ // pratica. Come per documenti e partecipanti, l'engine espone un'unica API
468
+ // add-or-remove per ciascuna delle due.
469
+ //
470
+ // ATTENZIONE all'asimmetria: dei Preferiti si può leggere l'elenco
471
+ // (FavoritesRetrieveAllAsync), del Segui NO. Nessuna API dice se l'utente
472
+ // corrente segue una pratica: le `FollowAddOrRemove` sono di sola scrittura,
473
+ // qui come per pratiche-dossier e gruppi di lavoro. Finché il backend non
474
+ // esporrà `IsFollow` sul descrittore d'istanza, lo stato del comando Segui
475
+ // resta a carico del chiamante.
476
+ /** Istanze marcate come preferite dall'utente corrente. */
477
+ static async getFavorites() {
478
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
479
+ if (!engine)
480
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
481
+ return await engine.FavoritesRetrieveAllAsync() ?? [];
482
+ }
483
+ /**
484
+ * Aggiunge o rimuove l'istanza dai preferiti dell'utente corrente tramite
485
+ * FavoritesAddOrRemoveAsync(cftid, id, remove).
486
+ */
487
+ static async setFavorite(caseFlowId, favorite, cftid) {
488
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
489
+ if (!engine)
490
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
491
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
492
+ if (resolvedCftid == null)
493
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
494
+ await engine.FavoritesAddOrRemoveAsync(resolvedCftid, caseFlowId, !favorite);
495
+ }
496
+ /**
497
+ * Inizia o smette di seguire l'istanza tramite
498
+ * FollowAddOrRemoveAsync(cftid, id, remove).
499
+ */
500
+ static async setFollow(caseFlowId, follow, cftid) {
501
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
502
+ if (!engine)
503
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
504
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
505
+ if (resolvedCftid == null)
506
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
507
+ await engine.FollowAddOrRemoveAsync(resolvedCftid, caseFlowId, !follow);
508
+ }
509
+ /**
510
+ * Stato "l'utente corrente segue questa pratica" letto dal descrittore.
511
+ *
512
+ * `isFollow` fa ormai parte del contratto (`CaseFlowInstanceDescriptor`):
513
+ * arriva valorizzato da `usp_Cfi_RetrieveAll`/`usp_Cfi_Retrieve`. Resta
514
+ * `undefined` solo quando il descrittore proviene da una sorgente che non
515
+ * popola il campo.
516
+ *
517
+ * Da NON dedurre dalla bacheca Home come fanno pratiche-dossier e gruppi di
518
+ * lavoro (filtro su `HomeBlogPost.classID`): quella query prende `TOP 30`
519
+ * righe e il solo ultimo post non di sistema per oggetto, quindi chi segue
520
+ * un oggetto senza post risulta "non seguace"; per le istanze di CaseFlow,
521
+ * poi, non esiste nemmeno un ramo nella UNION.
522
+ */
523
+ static readFollowFlag(cf) {
524
+ return cf.isFollow;
525
+ }
526
+ // ----- Workitem: movimentazione del flusso -------------------------------
527
+ //
528
+ // L'engine espone UNA sola API per la movimentazione dei workitem,
529
+ // ExecuteWorkItemOperationAsync(cftid, id, operation): il tipo di operazione
530
+ // (Complete, Reject, RequestMoreInfo, RespondMoreInfo, Assign, ForceComplete,
531
+ // ForceReject, ...) è indicato in `operation.operation` e i soli dati pertinenti
532
+ // (wiid, actionID, toUserID, comment, metadataValues) vengono valorizzati in
533
+ // base all'operazione. I messaggi in bacheca (nota di completamento, motivo del
534
+ // rifiuto, richiesta informazioni) sono inseriti DIRETTAMENTE dal backend a
535
+ // partire dal `comment`: la UI non deve più creare post di bacheca a mano.
536
+ /** Completa (approva) un workitem. La nota facoltativa viene pubblicata in bacheca dal backend. */
537
+ static async completeWorkItem(caseFlowId, workItemId, response, cftid) {
538
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
539
+ operation: CF_WIOperations.Complete,
540
+ wiid: workItemId,
541
+ comment: response,
542
+ }), cftid);
543
+ }
544
+ /**
545
+ * Completa un workitem di "Completamento dati": i metadati valorizzati
546
+ * (mid -> valore) viaggiano in `metadataValues`; il backend li applica al
547
+ * documento master dell'istanza e completa il workitem.
548
+ */
549
+ static async completeDataCompletionWorkItem(caseFlowId, workItemId, metadataValues, response, cftid) {
550
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
551
+ operation: CF_WIOperations.Complete,
552
+ wiid: workItemId,
553
+ comment: response,
554
+ metadataValues: Object.entries(metadataValues).map(([mid, value]) => ({ mid: Number(mid), value })),
555
+ }), cftid);
556
+ }
557
+ /** Rifiuta un workitem. Il motivo viene pubblicato in bacheca dal backend. */
558
+ static async rejectWorkItem(caseFlowId, workItemId, response, cftid) {
559
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
560
+ operation: CF_WIOperations.Reject,
561
+ wiid: workItemId,
562
+ comment: response,
563
+ }), cftid);
564
+ }
565
+ /**
566
+ * Prende in carico un'azione per se stessi (operazione Claim): il backend
567
+ * crea il workitem intestato al chiamante e porta l'azione in lavorazione.
568
+ *
569
+ * Non e' un caso particolare di `createWorkItemForAction`: assegnare e'
570
+ * riservato a Owner e Supervisore, prendere in carico e' di chiunque lavori
571
+ * alla pratica. Passando dall'operazione Assign, un Partecipante si vedeva
572
+ * rifiutare la presa in carico (TMC-2789).
573
+ */
574
+ static async claimAction(caseFlowId, actionID, cftid) {
575
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
576
+ operation: CF_WIOperations.Claim,
577
+ actionID,
578
+ }), cftid);
579
+ }
580
+ /**
581
+ * Rilascia un workitem preso in carico (operazione ReleaseClaim): il backend
582
+ * lo cancella e, se per quell'azione non restano altri destinatari, la riporta
583
+ * disponibile per la presa in carico.
584
+ *
585
+ * Stessa chiamata per due gesti diversi, e' la guardia della SP a distinguerli:
586
+ * il detentore rilascia il proprio workitem, un Owner/Supervisore annulla
587
+ * l'assegnazione di un altro. Chi chiama non deve dire quale dei due sta facendo.
588
+ */
589
+ static async releaseClaim(caseFlowId, workItemId, cftid) {
590
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
591
+ operation: CF_WIOperations.ReleaseClaim,
592
+ wiid: workItemId,
593
+ }), cftid);
594
+ }
595
+ /** Assegna un'azione a un ALTRO utente creando il relativo workitem (operazione Assign). */
596
+ static async createWorkItemForAction(caseFlowId, actionID, toUserID, description, cftid) {
597
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
598
+ operation: CF_WIOperations.Assign,
599
+ actionID,
600
+ toUserID,
601
+ comment: description,
602
+ }), cftid);
603
+ }
604
+ /** Forza il completamento di un'azione (supervisor). */
605
+ static async forceCompleteAction(caseFlowId, actionID, reason, cftid) {
606
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
607
+ operation: CF_WIOperations.ForceComplete,
608
+ actionID,
609
+ comment: reason,
610
+ }), cftid);
611
+ }
612
+ /** Forza il rifiuto di un'azione (supervisor). Il motivo è obbligatorio. */
613
+ static async forceRejectAction(caseFlowId, actionID, reason, cftid) {
614
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
615
+ operation: CF_WIOperations.ForceReject,
616
+ actionID,
617
+ comment: reason,
618
+ }), cftid);
619
+ }
620
+ /**
621
+ * Richiede maggiori informazioni a un altro utente: il backend crea il
622
+ * workitem MoreInfo figlio assegnato a `targetUserId` e registra la richiesta.
623
+ */
624
+ static async requestMoreInfo(caseFlowId, workItemId, targetUserId, message, cftid) {
625
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
626
+ operation: CF_WIOperations.RequestMoreInfo,
627
+ wiid: workItemId,
628
+ toUserID: targetUserId,
629
+ comment: message,
630
+ }), cftid);
631
+ }
632
+ /** Risponde a una richiesta di informazioni completando il workitem MoreInfo. */
633
+ static async respondToMoreInfo(caseFlowId, moreInfoWorkItemId, response, cftid) {
634
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
635
+ operation: CF_WIOperations.RespondMoreInfo,
636
+ wiid: moreInfoWorkItemId,
637
+ comment: response,
638
+ }), cftid);
639
+ }
640
+ /**
641
+ * Chiama ExecuteWorkItemOperationAsync(cftid, id, operation) risolvendo il
642
+ * cftid quando non passato (come per bacheca, documenti e partecipanti).
643
+ */
644
+ static async executeWorkItemOperation(caseFlowId, operation, cftid) {
645
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
646
+ if (!engine)
647
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
648
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
649
+ if (resolvedCftid == null)
650
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
651
+ await engine.ExecuteWorkItemOperationAsync(resolvedCftid, caseFlowId, operation);
652
+ }
653
+ static loadAllInstances() {
654
+ if (this._inFlightInstances)
655
+ return this._inFlightInstances;
656
+ const p = this.doLoadAllInstances().finally(() => {
657
+ if (this._inFlightInstances === p)
658
+ this._inFlightInstances = undefined;
659
+ });
660
+ this._inFlightInstances = p;
661
+ return p;
662
+ }
663
+ static async doLoadAllInstances() {
664
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
665
+ if (!engine)
666
+ return [];
667
+ // cftid=0 => TUTTE le istanze visibili all'utente in un'unica chiamata,
668
+ // cross-template (owner, partecipante di template o di singola istanza),
669
+ // incluse quelle di template NON visibili all'utente.
670
+ //
671
+ // TRANSIZIONE: finché il backend non supporta `cftid=0`, la chiamata può
672
+ // fallire o tornare vuota. In quel caso si ripiega sull'enumerazione
673
+ // per-template (copre gli utenti che vedono i template). Rimuovere il
674
+ // fallback quando `cftid=0` sarà stabile lato backend.
675
+ let list = [];
676
+ try {
677
+ list = (await engine.RetrieveAllAsync(0)) ?? [];
678
+ }
679
+ catch (e) {
680
+ console.warn('[CaseFlow] RetrieveAll(0) non disponibile, ripiego per-template:', e);
681
+ }
682
+ if (list.length === 0) {
683
+ list = await this.loadInstancesPerTemplate(engine);
684
+ }
685
+ const instances = [];
686
+ const seen = new Set();
687
+ for (const inst of list) {
688
+ // Salvaguardia anti-duplicati per id.
689
+ if (inst.id != null) {
690
+ if (seen.has(inst.id))
691
+ continue;
692
+ seen.add(inst.id);
693
+ }
694
+ instances.push(inst);
695
+ }
696
+ // Risolvi i template di TUTTI i cftid presenti per popolare le cache di
697
+ // nome template e nome azione (usate in rendering). GetAsync risolve
698
+ // on-demand anche i template non creabili dall'utente (di cui però
699
+ // partecipa a un'istanza), che altrimenti la cache non conoscerebbe.
700
+ // Best-effort: se un template non è risolvibile, l'etichetta degrada su
701
+ // `Modello #<cftid>` senza nascondere l'istanza.
702
+ const cftids = [...new Set(instances.map(i => i.cftid).filter((c) => c != null))];
703
+ await Promise.all(cftids.map(async (cftid) => {
704
+ const tpl = await CaseFlowTemplateCacheService.GetAsync(cftid);
705
+ if (tpl) {
706
+ primeTemplateName(tpl.id, tpl.name);
707
+ primeActionNames(tpl);
708
+ }
709
+ }));
710
+ await this.resolveUserNames(instances);
711
+ return instances;
712
+ }
713
+ /**
714
+ * Fallback di TRANSIZIONE: enumera le istanze template per template usando
715
+ * l'elenco dei template visibili all'utente. Non copre le istanze di template
716
+ * NON visibili (partecipante a livello di istanza): per quelle serve la
717
+ * RetrieveAll cross-template (cftid=0). Da rimuovere quando cftid=0 è stabile.
718
+ */
719
+ static async loadInstancesPerTemplate(engine) {
720
+ const templates = await this.getTemplates();
721
+ const collected = [];
722
+ await Promise.all(templates.map(async (t) => {
723
+ if (t.id == null)
724
+ return;
725
+ try {
726
+ const l = await engine.RetrieveAllAsync(t.id);
727
+ for (const inst of l ?? []) {
728
+ if (inst.cftid == null)
729
+ inst.cftid = t.id;
730
+ collected.push(inst);
731
+ }
732
+ }
733
+ catch (e) {
734
+ console.warn(`[CaseFlow] RetrieveAll istanze per template ${t.id} fallito:`, e);
735
+ }
736
+ }));
737
+ return collected;
738
+ }
739
+ /** Valorizza `ownerName` sulle istanze (best-effort, via cache utenti). */
740
+ static async resolveUserNames(instances) {
741
+ const ids = new Set();
742
+ for (const inst of instances) {
743
+ if (inst.ownerID != null)
744
+ ids.add(inst.ownerID);
745
+ }
746
+ const nameById = new Map();
747
+ await Promise.all([...ids].map(async (id) => {
748
+ try {
749
+ const u = await UserListCacheService.GetAsync(id);
750
+ nameById.set(id, u?.name ?? `Utente ${id}`);
751
+ }
752
+ catch {
753
+ nameById.set(id, `Utente ${id}`);
754
+ }
755
+ }));
756
+ for (const inst of instances) {
757
+ if (inst.ownerID != null && !inst.ownerName)
758
+ inst.ownerName = nameById.get(inst.ownerID);
759
+ }
760
+ }
761
+ }