@acorex/modules 20.7.4 → 20.7.6

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 (24) hide show
  1. package/document-management/index.d.ts +3 -2
  2. package/fesm2022/acorex-modules-document-management.mjs +106 -20
  3. package/fesm2022/acorex-modules-document-management.mjs.map +1 -1
  4. package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-CITYAroH.mjs → acorex-modules-human-capital-management-leave-request.entity-Dn2LLbuq.mjs} +20 -3
  5. package/fesm2022/acorex-modules-human-capital-management-leave-request.entity-Dn2LLbuq.mjs.map +1 -0
  6. package/fesm2022/acorex-modules-human-capital-management.mjs +558 -103
  7. package/fesm2022/acorex-modules-human-capital-management.mjs.map +1 -1
  8. package/fesm2022/{acorex-modules-product-catalog-product.entity-CBieRmkK.mjs → acorex-modules-product-catalog-product.entity-CuTXUcc1.mjs} +20 -17
  9. package/fesm2022/acorex-modules-product-catalog-product.entity-CuTXUcc1.mjs.map +1 -0
  10. package/fesm2022/acorex-modules-product-catalog.mjs +1 -1
  11. package/fesm2022/{acorex-modules-task-management-task-board.page-CIlxqtgJ.mjs → acorex-modules-task-management-task-board.page-BNwqZ5Eu.mjs} +3 -3
  12. package/fesm2022/{acorex-modules-task-management-task-board.page-CIlxqtgJ.mjs.map → acorex-modules-task-management-task-board.page-BNwqZ5Eu.mjs.map} +1 -1
  13. package/fesm2022/acorex-modules-task-management.mjs +9 -3
  14. package/fesm2022/acorex-modules-task-management.mjs.map +1 -1
  15. package/fesm2022/{acorex-modules-workflow-management-index-vstmaa5b.mjs → acorex-modules-workflow-management-index-BIl8No8o.mjs} +16 -10
  16. package/fesm2022/acorex-modules-workflow-management-index-BIl8No8o.mjs.map +1 -0
  17. package/fesm2022/acorex-modules-workflow-management.mjs +36 -4
  18. package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -1
  19. package/human-capital-management/index.d.ts +58 -1
  20. package/package.json +6 -6
  21. package/workflow-management/index.d.ts +2 -0
  22. package/fesm2022/acorex-modules-human-capital-management-leave-request.entity-CITYAroH.mjs.map +0 -1
  23. package/fesm2022/acorex-modules-product-catalog-product.entity-CBieRmkK.mjs.map +0 -1
  24. package/fesm2022/acorex-modules-workflow-management-index-vstmaa5b.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { AXTranslationService } from '@acorex/core/translation';
2
2
  import { AXPWorkflowTaskProvider, AXP_WORKFLOW_TASK_PROVIDER } from '@acorex/modules/task-management';
3
3
  import { AXPSystemStatuses, AXPStatusProvider, systemStatusToDefinition, AXPSystemStatusType, AXP_STATUS_PROVIDERS, AXP_MENU_PROVIDER } from '@acorex/platform/common';
4
- import { AXPEntityService, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
4
+ import { AXPEntityService, AXPEntityDefinitionRegistryService, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
5
  import { AXPCommandExecutor, AXPCommandEventsService, AXPRuntimeModule, provideCommandSetups } from '@acorex/platform/runtime';
6
6
  import * as i0 from '@angular/core';
7
7
  import { inject, Injectable, DestroyRef, signal, NgModule, Injector } from '@angular/core';
@@ -194,17 +194,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
194
194
  }]
195
195
  }] });
196
196
 
197
+ //#region ---- Imports ----
198
+ //#endregion
199
+ /**
200
+ * Leave Request Task Provider
201
+ *
202
+ * Integrates with workflow engine to show leave request tasks from work items.
203
+ * Uses work items created by workflow engine for human-task activities.
204
+ *
205
+ * Architecture:
206
+ * - Queries work items filtered by entityRefType = 'HumanCapitalManagement.LeaveRequest'
207
+ * - Loads related entity data from entityRefId
208
+ * - Maps work item status to task board status
209
+ * - Provides actions based on workflow activity definitions
210
+ */
197
211
  class AXMLeaveRequestTaskProvider extends AXPWorkflowTaskProvider {
198
212
  constructor() {
213
+ //#region ---- Services & Dependencies ----
199
214
  super(...arguments);
200
215
  this.entityService = inject(AXPEntityService);
216
+ this.entityRegistry = inject(AXPEntityDefinitionRegistryService);
217
+ this.workItemData = this.entityService
218
+ .withEntity('WorkflowManagement', 'WorkItem')
219
+ .data();
201
220
  this.leaveRequestData = this.entityService
202
221
  .withEntity(RootConfig.module.name, RootConfig.entities.leaveRequest.name)
203
222
  .data();
204
223
  this.commandExecutor = inject(AXPCommandExecutor);
205
224
  this.translationService = inject(AXTranslationService);
206
225
  this.dialogService = inject(AXPLeaveRequestDialogService);
226
+ //#endregion
207
227
  }
228
+ //#endregion
229
+ //#region ---- Provider Metadata ----
208
230
  get name() {
209
231
  return 'leave-request';
210
232
  }
@@ -214,184 +236,627 @@ class AXMLeaveRequestTaskProvider extends AXPWorkflowTaskProvider {
214
236
  get icon() {
215
237
  return 'fa-light fa-plane-departure';
216
238
  }
239
+ //#endregion
240
+ //#region ---- Task Retrieval ----
241
+ /**
242
+ * Get tasks from work items for leave requests.
243
+ *
244
+ * Queries work items filtered by:
245
+ * - entityRefType = 'HumanCapitalManagement.LeaveRequest'
246
+ * - status (Pending, Claimed, InProgress)
247
+ * - assignee/claimant (if filter provided)
248
+ * - date range (if provided)
249
+ *
250
+ * Then loads related entity data and maps to task board format.
251
+ */
217
252
  async getTasks(options) {
253
+ console.log(`[LeaveRequestTaskProvider] 🔍 getTasks called with options:`, {
254
+ skip: options?.skip,
255
+ take: options?.take,
256
+ assigneeIds: options?.assigneeIds,
257
+ range: options?.range,
258
+ types: options?.types,
259
+ });
260
+ // Build filter for work items
218
261
  const baseFilter = { logic: 'and', filters: [] };
219
- baseFilter.filters?.push({
220
- field: 'statusId',
221
- operator: { type: 'equal', negative: true },
222
- value: AXPSystemStatuses.Cancelled.name,
262
+ const entityTypeValue = `${RootConfig.module.name}.${RootConfig.entities.leaveRequest.name}`;
263
+ const workflowNameValue = 'create-leave-request';
264
+ console.log(`[LeaveRequestTaskProvider] 📋 Building filters:`, {
265
+ entityType: entityTypeValue,
266
+ workflowName: workflowNameValue,
223
267
  });
224
- // Filter for tasks that OVERLAP with the given date range
225
- // A task overlaps if: task.startDate <= range.end AND task.endDate >= range.from
226
- if (options?.range?.end) {
227
- baseFilter.filters?.push({ field: 'startDate', operator: { type: 'lte' }, value: options.range.end });
268
+ // Filter by entity type OR workflow instance definitionId
269
+ // Primary: entityRefType (set after entity creation)
270
+ // Fallback: instanceId from workflow instances with definitionId = 'create-leave-request'
271
+ // We'll use OR logic to catch both cases
272
+ const entityTypeFilter = {
273
+ field: 'entityRefType',
274
+ operator: { type: 'equal' },
275
+ value: entityTypeValue,
276
+ };
277
+ // Try to get workflow instance IDs for fallback
278
+ let workflowInstanceIds = [];
279
+ try {
280
+ const workflowInstanceData = this.entityService
281
+ .withEntity('WorkflowManagement', 'WorkflowInstance')
282
+ .data();
283
+ const instances = await workflowInstanceData.query({
284
+ skip: 0,
285
+ take: 100,
286
+ filter: {
287
+ logic: 'and',
288
+ filters: [
289
+ {
290
+ field: 'definitionId',
291
+ operator: { type: 'equal' },
292
+ value: workflowNameValue,
293
+ },
294
+ ],
295
+ },
296
+ });
297
+ workflowInstanceIds = instances.items.map((inst) => inst.id);
298
+ console.log(`[LeaveRequestTaskProvider] 🔍 Found ${workflowInstanceIds.length} workflow instances for '${workflowNameValue}'`);
299
+ }
300
+ catch (error) {
301
+ console.warn(`[LeaveRequestTaskProvider] ⚠️ Failed to query workflow instances:`, error);
302
+ }
303
+ // Build OR filter: entityRefType OR instanceId in workflowInstanceIds
304
+ if (workflowInstanceIds.length > 0) {
305
+ baseFilter.filters?.push({
306
+ logic: 'or',
307
+ filters: [
308
+ entityTypeFilter,
309
+ {
310
+ field: 'instanceId',
311
+ operator: { type: 'in' },
312
+ value: workflowInstanceIds,
313
+ },
314
+ ],
315
+ });
228
316
  }
229
- if (options?.range?.from) {
230
- baseFilter.filters?.push({ field: 'endDate', operator: { type: 'gte' }, value: options.range.from });
317
+ else {
318
+ // Only use entityRefType if no workflow instances found
319
+ baseFilter.filters?.push(entityTypeFilter);
231
320
  }
321
+ // Filter by status (exclude completed/cancelled)
322
+ baseFilter.filters?.push({
323
+ field: 'status',
324
+ operator: { type: 'in' },
325
+ value: ['Pending', 'Claimed', 'InProgress'],
326
+ });
327
+ // Filter by date range if provided
328
+ // Work items don't have startDate/endDate directly, but we can filter by createdAt or dueDate
329
+ // For now, we'll skip date range filtering to ensure work items are found
330
+ // TODO: Implement proper date range filtering based on entity data (startDate/endDate)
331
+ // if (options?.range?.from || options?.range?.end) {
332
+ // if (options.range.from) {
333
+ // baseFilter.filters?.push({
334
+ // field: 'createdAt',
335
+ // operator: { type: 'gte' as const },
336
+ // value: options.range.from,
337
+ // });
338
+ // }
339
+ //
340
+ // if (options.range.end) {
341
+ // // For end date, we want: dueDate <= end OR dueDate is null
342
+ // // This allows work items without dueDate to still appear
343
+ // baseFilter.filters?.push({
344
+ // logic: 'or',
345
+ // filters: [
346
+ // {
347
+ // field: 'dueDate',
348
+ // operator: { type: 'lte' as const },
349
+ // value: options.range.end,
350
+ // },
351
+ // {
352
+ // field: 'dueDate',
353
+ // operator: { type: 'isNull' as const },
354
+ // },
355
+ // ],
356
+ // });
357
+ // }
358
+ // }
359
+ // Filter by assignee/claimant
232
360
  if (options?.assigneeIds && options.assigneeIds.length > 0) {
233
- // Filter by manager (assignee) of the employee requesting leave
234
361
  baseFilter.filters?.push({
235
- field: 'employee.manager.userId',
236
- operator: { type: 'in' },
237
- value: options.assigneeIds,
362
+ logic: 'or',
363
+ filters: [
364
+ {
365
+ field: 'assignedUserId',
366
+ operator: { type: 'in' },
367
+ value: options.assigneeIds,
368
+ },
369
+ {
370
+ field: 'claimedByUserId',
371
+ operator: { type: 'in' },
372
+ value: options.assigneeIds,
373
+ },
374
+ ],
238
375
  });
239
376
  }
240
- const { items, total } = await this.leaveRequestData.query({
377
+ console.log(`[LeaveRequestTaskProvider] 🔎 Query filter:`, JSON.stringify(baseFilter, null, 2));
378
+ // Query work items
379
+ const { items: workItems, total } = await this.workItemData.query({
241
380
  skip: options?.skip ?? 0,
242
381
  take: options?.take ?? 20,
243
382
  filter: baseFilter,
244
383
  });
245
- const tasks = items.map((item) => {
246
- const statusId = item.statusId ?? item.status?.id ?? AXPSystemStatuses.Pending.name;
247
- const statusTitle = item.status?.title ??
248
- this.translationService.translateSync(`@human-capital-management:leave-requests.states.${statusId}`);
249
- return {
250
- id: item.id,
251
- title: `${item.employee?.person?.displayName} - ${this.translationService.translateSync(item.leaveType?.title ?? '')}`,
252
- description: item.reason ?? '',
253
- startDate: item.startDate ?? new Date(),
254
- endDate: item.endDate ?? new Date(),
255
- // Leave requests are all-day events by default (they span full days)
256
- allDay: true,
257
- assignee: {
258
- id: item.employee?.userId ?? '',
259
- type: 'user',
260
- fullName: item.employee?.manager?.person?.displayName ?? '',
261
- },
262
- index: 2,
263
- status: { id: statusId, title: statusTitle },
264
- priority: this.getPriorityFromStatus(statusId),
265
- reporter: {
266
- id: item.employee?.userId ?? item.employeeId,
267
- type: 'user',
268
- fullName: item.employee?.person?.displayName ?? '',
269
- },
270
- payload: item,
271
- };
384
+ console.log(`[LeaveRequestTaskProvider] 📦 Query result:`, {
385
+ total,
386
+ itemsCount: workItems.length,
387
+ workItems: workItems.map((wi) => ({
388
+ id: wi.id,
389
+ title: wi.title,
390
+ status: wi.status,
391
+ entityRefType: wi.entityRefType,
392
+ entityRefId: wi.entityRefId,
393
+ instanceId: wi.instanceId,
394
+ instanceDefinitionId: wi.instance?.definitionId,
395
+ instanceName: wi.instance?.name,
396
+ activityNodeId: wi.activityNodeId,
397
+ activityName: wi.activityName,
398
+ createdAt: wi.createdAt,
399
+ dueDate: wi.dueDate,
400
+ })),
272
401
  });
402
+ // Debug: Log if no work items found
403
+ if (workItems.length === 0) {
404
+ console.warn(`[LeaveRequestTaskProvider] ⚠️ No work items found!`, {
405
+ filter: baseFilter,
406
+ entityTypeValue: `${RootConfig.module.name}.${RootConfig.entities.leaveRequest.name}`,
407
+ workflowNameValue: 'create-leave-request',
408
+ suggestion: 'Check if: 1) Workflow was started, 2) Human-task activity exists, 3) Work item was created, 4) entityRefType was set after entity creation',
409
+ });
410
+ // Fallback: Try to query all work items to see what we have
411
+ const allWorkItems = await this.workItemData.query({
412
+ skip: 0,
413
+ take: 10,
414
+ filter: {
415
+ logic: 'and',
416
+ filters: [
417
+ {
418
+ field: 'status',
419
+ operator: { type: 'in' },
420
+ value: ['Pending', 'Claimed', 'InProgress'],
421
+ },
422
+ ],
423
+ },
424
+ });
425
+ console.log(`[LeaveRequestTaskProvider] 🔍 Fallback: Found ${allWorkItems.items.length} work items total (any entity):`, {
426
+ workItems: allWorkItems.items.map((wi) => ({
427
+ id: wi.id,
428
+ title: wi.title,
429
+ entityRefType: wi.entityRefType,
430
+ entityRefId: wi.entityRefId,
431
+ instanceId: wi.instanceId,
432
+ status: wi.status,
433
+ })),
434
+ });
435
+ }
436
+ // Load entity data for each work item and map to tasks
437
+ const tasks = [];
438
+ console.log(`[LeaveRequestTaskProvider] 🔄 Processing ${workItems.length} work items...`);
439
+ for (const workItem of workItems) {
440
+ try {
441
+ console.log(`[LeaveRequestTaskProvider] 📝 Processing work item:`, {
442
+ id: workItem.id,
443
+ title: workItem.title,
444
+ entityRefId: workItem.entityRefId,
445
+ entityRefType: workItem.entityRefType,
446
+ instanceId: workItem.instanceId,
447
+ instanceDefinitionId: workItem.instance?.definitionId,
448
+ });
449
+ // Load entity data if entityRefId is available
450
+ let entityData = null;
451
+ if (workItem.entityRefId) {
452
+ try {
453
+ console.log(`[LeaveRequestTaskProvider] 🔍 Loading entity data for entityRefId: ${workItem.entityRefId}`);
454
+ entityData = await this.leaveRequestData.byKey(workItem.entityRefId);
455
+ console.log(`[LeaveRequestTaskProvider] ✅ Entity data loaded:`, {
456
+ id: entityData?.id,
457
+ employee: entityData?.employee?.person?.displayName,
458
+ startDate: entityData?.startDate,
459
+ endDate: entityData?.endDate,
460
+ });
461
+ }
462
+ catch (error) {
463
+ console.warn(`[LeaveRequestTaskProvider] ⚠️ Failed to load entity data for ${workItem.entityRefId}:`, error);
464
+ // Continue without entity data - use work item data only
465
+ }
466
+ }
467
+ else {
468
+ console.log(`[LeaveRequestTaskProvider] ℹ️ No entityRefId, using work item data only`);
469
+ }
470
+ // Map work item to task
471
+ const task = this.mapWorkItemToTask(workItem, entityData);
472
+ console.log(`[LeaveRequestTaskProvider] ✅ Mapped to task:`, {
473
+ id: task.id,
474
+ title: task.title,
475
+ startDate: task.startDate,
476
+ endDate: task.endDate,
477
+ status: task.status,
478
+ });
479
+ tasks.push(task);
480
+ }
481
+ catch (error) {
482
+ console.error(`[LeaveRequestTaskProvider] ❌ Failed to process work item ${workItem.id}:`, error);
483
+ // Skip this work item and continue
484
+ }
485
+ }
486
+ console.log(`[LeaveRequestTaskProvider] ✅ Returning ${tasks.length} tasks (total: ${total})`);
273
487
  return { items: tasks, total };
274
488
  }
489
+ /**
490
+ * Map work item to task board task format.
491
+ */
492
+ mapWorkItemToTask(workItem, entityData) {
493
+ // Use entity data if available, otherwise use work item metadata
494
+ const statusId = entityData?.statusId ?? entityData?.status?.id ?? AXPSystemStatuses.Pending.name;
495
+ const statusTitle = entityData?.status?.title ??
496
+ this.translationService.translateSync(`@human-capital-management:leave-requests.states.${statusId}`);
497
+ // Extract dates from entity data or use work item dates
498
+ const startDate = entityData?.startDate
499
+ ? new Date(entityData.startDate)
500
+ : (workItem.dueDate ? new Date(workItem.dueDate) : new Date());
501
+ const endDate = entityData?.endDate
502
+ ? new Date(entityData.endDate)
503
+ : (workItem.dueDate ? new Date(workItem.dueDate) : new Date());
504
+ // Build title from entity data or work item
505
+ let title = workItem.title;
506
+ if (entityData) {
507
+ title = `${entityData.employee?.person?.displayName ?? ''} - ${this.translationService.translateSync(entityData.leaveType?.title ?? '')}`.trim();
508
+ if (!title)
509
+ title = workItem.title;
510
+ }
511
+ // Map work item status to task status
512
+ const taskStatus = this.mapWorkItemStatusToTaskStatus(workItem.status);
513
+ return {
514
+ id: workItem.id, // Use work item ID as task ID
515
+ title,
516
+ description: entityData?.reason ?? workItem.description ?? '',
517
+ startDate,
518
+ endDate,
519
+ allDay: true, // Leave requests are all-day events
520
+ assignee: workItem.assignedUser
521
+ ? {
522
+ id: workItem.assignedUser.id,
523
+ type: 'user',
524
+ fullName: workItem.assignedUser.displayName,
525
+ }
526
+ : workItem.claimedByUser
527
+ ? {
528
+ id: workItem.claimedByUser.id,
529
+ type: 'user',
530
+ fullName: workItem.claimedByUser.displayName,
531
+ }
532
+ : undefined,
533
+ index: 2,
534
+ status: taskStatus,
535
+ priority: this.mapWorkItemPriorityToTaskPriority(workItem.priority),
536
+ reporter: entityData?.employee
537
+ ? {
538
+ id: entityData.employee.userId ?? entityData.employeeId,
539
+ type: 'user',
540
+ fullName: entityData.employee.person?.displayName ?? '',
541
+ }
542
+ : workItem.createdByUser
543
+ ? {
544
+ id: workItem.createdByUser.id,
545
+ type: 'user',
546
+ fullName: workItem.createdByUser.displayName,
547
+ }
548
+ : {
549
+ id: '',
550
+ type: 'user',
551
+ fullName: '',
552
+ },
553
+ data: {
554
+ // Store entity data and work item metadata
555
+ entityData: entityData ?? null,
556
+ workItem: workItem,
557
+ workItemId: workItem.id,
558
+ instanceId: workItem.instanceId,
559
+ activityNodeId: workItem.activityNodeId,
560
+ entityRefId: workItem.entityRefId,
561
+ entityRefType: workItem.entityRefType,
562
+ }, // Additional metadata for task processing
563
+ };
564
+ }
565
+ /**
566
+ * Map work item status to task board status.
567
+ */
568
+ mapWorkItemStatusToTaskStatus(workItemStatus) {
569
+ // Map work item status to entity status
570
+ switch (workItemStatus) {
571
+ case 'Pending':
572
+ return {
573
+ id: AXPSystemStatuses.Pending.name,
574
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.pending'),
575
+ };
576
+ case 'Claimed':
577
+ case 'InProgress':
578
+ return {
579
+ id: AXPSystemStatuses.Pending.name, // Still pending from entity perspective
580
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.pending'),
581
+ };
582
+ case 'Completed':
583
+ return {
584
+ id: AXPSystemStatuses.Approved.name, // Completed work item = approved
585
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.approved'),
586
+ };
587
+ case 'Rejected':
588
+ return {
589
+ id: AXPSystemStatuses.Rejected.name,
590
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.rejected'),
591
+ };
592
+ case 'Cancelled':
593
+ return {
594
+ id: AXPSystemStatuses.Cancelled.name,
595
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.cancelled'),
596
+ };
597
+ default:
598
+ return {
599
+ id: AXPSystemStatuses.Pending.name,
600
+ title: this.translationService.translateSync('@human-capital-management:leave-requests.states.pending'),
601
+ };
602
+ }
603
+ }
604
+ /**
605
+ * Map work item priority to task board priority.
606
+ */
607
+ mapWorkItemPriorityToTaskPriority(workItemPriority) {
608
+ switch (workItemPriority) {
609
+ case 'Urgent':
610
+ return 'highest';
611
+ case 'High':
612
+ return 'high';
613
+ case 'Normal':
614
+ return 'medium';
615
+ case 'Low':
616
+ return 'low';
617
+ default:
618
+ return 'medium';
619
+ }
620
+ }
621
+ //#endregion
622
+ //#region ---- Task Updates ----
623
+ /**
624
+ * Update tasks (work items and/or entity data).
625
+ *
626
+ * Updates both work item (if task ID is work item ID) and entity data (if entityRefId is available).
627
+ */
275
628
  async updateTasks(tasksToUpdate) {
276
- // Update each task in the backend
277
629
  const updatedTasks = [];
278
630
  for (const task of tasksToUpdate) {
279
631
  try {
280
- // Build update payload with all changed fields
281
- const updatePayload = {};
282
- // Update status if present
283
- if (task.status?.id) {
284
- const statusId = task.status.id;
285
- updatePayload['statusId'] = statusId;
286
- }
287
- // Update dates if present
288
- if (task.startDate) {
289
- updatePayload['startDate'] = task.startDate;
632
+ const taskData = task.data;
633
+ const workItemId = taskData?.workItemId ?? task.id;
634
+ const entityRefId = taskData?.entityRefId;
635
+ // Update work item if task ID is work item ID
636
+ if (workItemId) {
637
+ const workItemUpdate = {};
638
+ if (task.status?.id) {
639
+ // Map task status back to work item status
640
+ const workItemStatus = this.mapTaskStatusToWorkItemStatus(String(task.status.id));
641
+ if (workItemStatus) {
642
+ workItemUpdate['status'] = workItemStatus;
643
+ }
644
+ }
645
+ if (task.assignee?.id) {
646
+ workItemUpdate['assignedUserId'] = task.assignee.id;
647
+ }
648
+ if (Object.keys(workItemUpdate).length > 0) {
649
+ await this.workItemData.update(workItemId, workItemUpdate);
650
+ }
290
651
  }
291
- if (task.endDate) {
292
- updatePayload['endDate'] = task.endDate;
652
+ // Update entity data if entityRefId is available
653
+ if (entityRefId) {
654
+ const entityUpdate = {};
655
+ if (task.status?.id) {
656
+ const statusId = task.status.id;
657
+ entityUpdate['statusId'] = statusId;
658
+ }
659
+ if (task.startDate) {
660
+ entityUpdate['startDate'] = task.startDate;
661
+ }
662
+ if (task.endDate) {
663
+ entityUpdate['endDate'] = task.endDate;
664
+ }
665
+ if (Object.keys(entityUpdate).length > 0) {
666
+ await this.leaveRequestData.update(entityRefId, entityUpdate);
667
+ }
293
668
  }
294
- // Only update if there's something to update
295
- if (Object.keys(updatePayload).length > 0) {
296
- await this.leaveRequestData.update(String(task.id), updatePayload);
297
- }
298
- // Return the task as-is (already updated in the task object)
299
669
  updatedTasks.push(task);
300
670
  }
301
671
  catch (error) {
302
672
  console.error(`[LeaveRequestTaskProvider] Failed to update task ${task.id}:`, error);
303
- // Still include the task even if update failed
304
- updatedTasks.push(task);
673
+ updatedTasks.push(task); // Still include the task even if update failed
305
674
  }
306
675
  }
307
676
  return updatedTasks;
308
677
  }
678
+ /**
679
+ * Map task status to work item status.
680
+ */
681
+ mapTaskStatusToWorkItemStatus(taskStatusId) {
682
+ switch (taskStatusId) {
683
+ case AXPSystemStatuses.Pending.name:
684
+ return 'Pending';
685
+ case AXPSystemStatuses.Approved.name:
686
+ return 'Completed';
687
+ case AXPSystemStatuses.Rejected.name:
688
+ return 'Rejected';
689
+ case AXPSystemStatuses.Cancelled.name:
690
+ return 'Cancelled';
691
+ default:
692
+ return null;
693
+ }
694
+ }
695
+ //#endregion
696
+ //#region ---- Command Execution ----
697
+ /**
698
+ * Execute commands on tasks.
699
+ *
700
+ * Supports:
701
+ * - Workflow work item commands (Claim, Complete, Cancel)
702
+ * - Leave request business commands (Approve, Reject, Cancel)
703
+ */
309
704
  async executeCommand(command) {
310
705
  if (!command?.name)
311
706
  return { success: true };
312
- const leaveRequestId = command.options?.['id'] ?? '';
707
+ const workItemId = command.options?.['workItemId'] ?? command.options?.['id'] ?? '';
708
+ const entityRefId = command.options?.['entityRefId'] ?? '';
709
+ // Handle workflow work item commands
710
+ if (command.name === 'WorkflowManagement.WorkItem:Claim') {
711
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Claim', {
712
+ id: workItemId,
713
+ });
714
+ return result ?? { success: true };
715
+ }
716
+ if (command.name === 'WorkflowManagement.WorkItem:Complete') {
717
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Complete', {
718
+ id: workItemId,
719
+ output: command.options?.['output'] ?? {},
720
+ outcome: command.options?.['outcome'] ?? 'Done',
721
+ });
722
+ return result ?? { success: true };
723
+ }
724
+ if (command.name === 'WorkflowManagement.WorkItem:Cancel') {
725
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Cancel', {
726
+ id: workItemId,
727
+ });
728
+ return result ?? { success: true };
729
+ }
730
+ // Handle leave request business commands (for backward compatibility)
313
731
  switch (command.name) {
314
732
  case 'approveLeave': {
315
- // Show approval dialog
316
733
  const dialogResult = await this.dialogService.showApprovalDialog();
317
- // If dialog was cancelled, return early
318
734
  if (dialogResult?.cancelled) {
319
735
  return { success: false };
320
736
  }
321
- // Execute the command - use fallback pattern like JSA
737
+ // Try to complete work item if available, otherwise use entity command
738
+ if (workItemId) {
739
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Complete', {
740
+ id: workItemId,
741
+ output: { reason: dialogResult?.reason, note: dialogResult?.note },
742
+ outcome: 'Approved',
743
+ });
744
+ return result ?? { success: true };
745
+ }
746
+ // Fallback to entity command
322
747
  const result = await this.commandExecutor.execute('HumanCapitalManagement.LeaveRequest:Approve', {
323
- id: leaveRequestId,
748
+ id: entityRefId || workItemId,
324
749
  reason: dialogResult?.reason,
325
750
  note: dialogResult?.note,
326
751
  });
327
- // Return result if exists, otherwise default to success (command might not be registered yet)
328
752
  return result ?? { success: true };
329
753
  }
330
754
  case 'rejectLeave': {
331
- // Show rejection dialog
332
755
  const dialogResult = await this.dialogService.showRejectionDialog();
333
- // If dialog was cancelled, return early
334
756
  if (dialogResult?.cancelled) {
335
757
  return { success: false };
336
758
  }
337
- // Execute the command - use fallback pattern like JSA
759
+ // Try to complete work item if available, otherwise use entity command
760
+ if (workItemId) {
761
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Complete', {
762
+ id: workItemId,
763
+ output: { reason: dialogResult?.reason, note: dialogResult?.note },
764
+ outcome: 'Rejected',
765
+ });
766
+ return result ?? { success: true };
767
+ }
768
+ // Fallback to entity command
338
769
  const result = await this.commandExecutor.execute('HumanCapitalManagement.LeaveRequest:Reject', {
339
- id: leaveRequestId,
770
+ id: entityRefId || workItemId,
340
771
  reason: dialogResult?.reason,
341
772
  note: dialogResult?.note,
342
773
  });
343
- // Return result if exists, otherwise default to success (command might not be registered yet)
344
774
  return result ?? { success: true };
345
775
  }
346
776
  case 'cancelLeave': {
347
- // Show cancellation dialog
348
777
  const dialogResult = await this.dialogService.showCancellationDialog();
349
- // If dialog was cancelled, return early
350
778
  if (dialogResult?.cancelled) {
351
779
  return { success: false };
352
780
  }
353
- // Execute the command - use fallback pattern like JSA
781
+ // Try to cancel work item if available, otherwise use entity command
782
+ if (workItemId) {
783
+ const result = await this.commandExecutor.execute('WorkflowManagement.WorkItem:Cancel', {
784
+ id: workItemId,
785
+ });
786
+ return result ?? { success: true };
787
+ }
788
+ // Fallback to entity command
354
789
  const result = await this.commandExecutor.execute('HumanCapitalManagement.LeaveRequest:Cancel', {
355
- id: leaveRequestId,
790
+ id: entityRefId || workItemId,
356
791
  reason: dialogResult?.reason,
357
792
  note: dialogResult?.note,
358
793
  });
359
- // Return result if exists, otherwise default to success (command might not be registered yet)
360
794
  return result ?? { success: true };
361
795
  }
362
796
  }
363
797
  return { success: true };
364
798
  }
799
+ //#endregion
800
+ //#region ---- Actions & Statuses ----
801
+ /**
802
+ * Get available actions for a task.
803
+ *
804
+ * Provides actions based on work item status and workflow activity.
805
+ */
365
806
  async getActions(task) {
366
807
  const actions = [];
367
- // Add approve action for pending requests
368
- if (task?.status?.id === AXPSystemStatuses.Pending.name || task?.status?.id === AXPSystemStatuses.Rejected.name) {
808
+ if (!task)
809
+ return actions;
810
+ const taskData = task.data;
811
+ const workItemId = taskData?.workItemId ?? task.id;
812
+ const entityRefId = taskData?.entityRefId;
813
+ // Add workflow work item actions
814
+ if (task.status?.id === AXPSystemStatuses.Pending.name) {
815
+ // Claim action (if not already claimed)
816
+ actions.push({
817
+ name: 'WorkflowManagement.WorkItem:Claim',
818
+ title: this.translationService.translateSync('@workflow-management:work-items.actions.claim.title'),
819
+ icon: 'fa-light fa-hand',
820
+ color: 'primary',
821
+ command: {
822
+ name: 'WorkflowManagement.WorkItem:Claim',
823
+ options: { id: workItemId, workItemId },
824
+ },
825
+ });
826
+ // Complete action (with approve/reject outcomes)
369
827
  actions.push({
370
828
  name: 'approveLeave',
371
829
  title: this.translationService.translateSync('@general:actions.approve.title'),
372
830
  icon: 'fa-light fa-check',
373
831
  color: 'success',
374
- command: { name: 'approveLeave', options: { id: task?.id ?? '' } },
832
+ command: {
833
+ name: 'approveLeave',
834
+ options: { id: entityRefId || workItemId, workItemId, entityRefId },
835
+ },
375
836
  });
376
- }
377
- // Add reject action for pending or approved requests
378
- if (task?.status?.id === AXPSystemStatuses.Pending.name || task?.status?.id === AXPSystemStatuses.Approved.name) {
379
837
  actions.push({
380
838
  name: 'rejectLeave',
381
839
  title: this.translationService.translateSync('@general:actions.reject.title'),
382
840
  icon: 'fa-light fa-xmark',
383
841
  color: 'danger',
384
- command: { name: 'rejectLeave', options: { id: task?.id ?? '' } },
842
+ command: {
843
+ name: 'rejectLeave',
844
+ options: { id: entityRefId || workItemId, workItemId, entityRefId },
845
+ },
385
846
  });
386
847
  }
387
- // Add cancel action for pending or approved requests
388
- if (task?.status?.id === AXPSystemStatuses.Pending.name || task?.status?.id === AXPSystemStatuses.Approved.name) {
848
+ // Add cancel action
849
+ if (task.status?.id === AXPSystemStatuses.Pending.name ||
850
+ task.status?.id === AXPSystemStatuses.Approved.name) {
389
851
  actions.push({
390
- name: 'rejectLeave',
852
+ name: 'cancelLeave',
391
853
  title: this.translationService.translateSync('@general:actions.cancel.title'),
392
854
  icon: 'fa-light fa-ban',
393
855
  color: 'secondary',
394
- command: { name: 'cancelLeave', options: { id: task?.id ?? '' } },
856
+ command: {
857
+ name: 'cancelLeave',
858
+ options: { id: entityRefId || workItemId, workItemId, entityRefId },
859
+ },
395
860
  });
396
861
  }
397
862
  return actions;
@@ -427,18 +892,8 @@ class AXMLeaveRequestTaskProvider extends AXPWorkflowTaskProvider {
427
892
  },
428
893
  ];
429
894
  }
430
- getPriorityFromStatus(statusId) {
431
- switch (statusId) {
432
- case AXPSystemStatuses.Pending.name:
433
- return 'medium';
434
- case AXPSystemStatuses.Approved.name:
435
- return 'lowest';
436
- case AXPSystemStatuses.Rejected.name:
437
- return 'high';
438
- default:
439
- return 'medium';
440
- }
441
- }
895
+ //#endregion
896
+ //#region ---- Component ----
442
897
  getComponent() {
443
898
  return () => import('./acorex-modules-human-capital-management-leave-request-task-popover.component-yGvT9kSL.mjs').then((m) => m.AXMLeaveRequestTaskPopoverComponent);
444
899
  }
@@ -924,7 +1379,7 @@ class AXMHumanCapitalModuleEntityProvider {
924
1379
  case RootConfig.entities.leaveType.name:
925
1380
  return (await import('./acorex-modules-human-capital-management-leave-type.entity-CY81Nohl.mjs')).factory();
926
1381
  case RootConfig.entities.leaveRequest.name:
927
- return (await import('./acorex-modules-human-capital-management-leave-request.entity-CITYAroH.mjs')).factory();
1382
+ return (await import('./acorex-modules-human-capital-management-leave-request.entity-Dn2LLbuq.mjs')).factory();
928
1383
  }
929
1384
  }
930
1385
  return null;