rez_core 2.2.81 → 2.2.83

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.81",
3
+ "version": "2.2.83",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -44,6 +44,7 @@
44
44
  "exceljs": "^4.4.0",
45
45
  "handlebars": "^4.7.8",
46
46
  "moment": "^2.30.1",
47
+ "moment-timezone": "^0.6.0",
47
48
  "multer": "^1.4.5-lts.2",
48
49
  "mysql2": "^3.12.0",
49
50
  "nodemailer": "^7.0.5",
@@ -2,7 +2,7 @@ import { InjectRepository } from '@nestjs/typeorm';
2
2
  import { MediaDataService } from 'src/module/meta/service/media-data.service';
3
3
  import { ActivityLog } from '../entity/activity-log.entity';
4
4
  import { Repository } from 'typeorm';
5
- import * as moment from 'moment-timezone';
5
+ import * as moment from 'moment';
6
6
 
7
7
  export const ACTIVITY_CATEGORIES = {
8
8
  ASSIGN: 'ASSIGN',
@@ -15,7 +15,7 @@ export const ACTIVITY_CATEGORIES = {
15
15
  STATUS: 'STATUS',
16
16
  TASK: 'TASK',
17
17
  LEAD: 'LEAD',
18
- STAGEGROUP: 'STAGE_GROUP_COMPLETED',
18
+ STAGEGROUP: 'STAGE_GROUP',
19
19
  } as const;
20
20
 
21
21
  export type ActivityCategoryType = keyof typeof ACTIVITY_CATEGORIES;
@@ -104,8 +104,12 @@ export class ActivityLogRepository {
104
104
 
105
105
  .getRawMany();
106
106
  return result.map((row) => {
107
+ const formattedLabel = row.category
108
+ .toLowerCase() // stage_group
109
+ .replace(/_/g, ' ') // stage group
110
+ .replace(/\b\w/g, (char) => char.toUpperCase()); // Stage Group
107
111
  return {
108
- label: row.category,
112
+ label: formattedLabel,
109
113
  value: row.category,
110
114
  };
111
115
  });
@@ -64,12 +64,8 @@ export class StageGroupRepository {
64
64
  const stagesRaw = await this.dataSource.query(
65
65
  `
66
66
  SELECT
67
- cr_wf_stage.*,
68
- eth_user_profile.name AS created_by_name,
69
- eth_user_profile.profile_image AS profile_image
67
+ cr_wf_stage.*
70
68
  FROM cr_wf_stage
71
- LEFT JOIN eth_user_profile
72
- ON cr_wf_stage.created_by = eth_user_profile.parent_id
73
69
  WHERE cr_wf_stage.stage_group_id IN (${stageGroupIds.map(() => '?').join(',')})
74
70
  AND cr_wf_stage.organization_id = ?
75
71
  ORDER BY cr_wf_stage.sequence ASC
@@ -131,26 +127,10 @@ export class StageGroupRepository {
131
127
  );
132
128
 
133
129
  // Step 4: Add counts & profile to each stage
134
- const stages = await Promise.all(
135
- stagesRaw.map(async (stage) => {
136
- const profileImageId = stage.profile_image;
137
- let profile: any = null;
138
- if (profileImageId) {
139
- profile = await this.mediaDataService.getMediaDownloadUrl(
140
- profileImageId,
141
- loggedInUser,
142
- );
143
- }
144
- return {
145
- ...stage,
146
- profile, // enriched profile object
147
- };
148
- }),
149
- );
150
130
 
151
131
  // Step 5: Group stages by stage_group_id
152
132
  const stageMap = new Map<number, any[]>();
153
- for (const stage of stages) {
133
+ for (const stage of stagesRaw) {
154
134
  const stageId = Number(stage.id); // ensure number
155
135
 
156
136
  stage.task_count = taskCountMap.get(stageId) || 0;
@@ -1,5 +1,8 @@
1
1
  import { Injectable } from '@nestjs/common';
2
- import { ActivityLogRepository } from '../repository/activity-log.repository';
2
+ import {
3
+ ACTIVITY_CATEGORIES,
4
+ ActivityLogRepository,
5
+ } from '../repository/activity-log.repository';
3
6
  import { ActivityLog } from '../entity/activity-log.entity';
4
7
  import { log } from 'console';
5
8
  import { EntityServiceImpl } from 'src/module/meta/service/entity-service-impl.service';
@@ -7,43 +10,50 @@ import { UserData } from 'src/module/user/entity/user.entity';
7
10
 
8
11
  function enrichData(category: string, logData: any): any {
9
12
  switch (category) {
10
- case 'STAGE':
11
- logData.color = '#f63d68';
13
+ case ACTIVITY_CATEGORIES.STAGE:
14
+ logData.color = '#06aed4';
12
15
  logData.icon = 'stage';
13
16
  break;
14
- case 'INTERACTION':
17
+ case ACTIVITY_CATEGORIES.INTERACTION:
15
18
  logData.color = '#7a5af8';
16
19
  logData.icon = 'interaction';
17
20
  break;
18
- case 'FORM':
21
+ case ACTIVITY_CATEGORIES.FORM:
19
22
  logData.color = '#eaaa08';
20
23
  logData.icon = 'form';
21
24
  break;
22
- case 'ASSIGN':
25
+ case ACTIVITY_CATEGORIES.ASSIGN:
23
26
  logData.color = '#16b364';
24
27
  logData.icon = 'assign';
25
28
  break;
26
- case 'MEETING':
27
- logData.color = '#f63d68';
29
+ case ACTIVITY_CATEGORIES.MEETING:
30
+ logData.color = '#7a5af8';
28
31
  logData.icon = 'meeting';
29
32
  break;
30
- case 'PROCESS':
31
- logData.color = '#f63d68';
33
+ case ACTIVITY_CATEGORIES.PROCESS:
34
+ logData.color = '#06aed4';
32
35
  logData.icon = 'process';
33
36
  break;
34
- case 'STATUS':
35
- logData.color = '#f63d68';
37
+ case ACTIVITY_CATEGORIES.STATUS:
38
+ logData.color = '#06aed4';
36
39
  logData.icon = 'status';
37
40
  break;
38
- case 'TASK':
39
- logData.color = '#06aed4';
41
+ case ACTIVITY_CATEGORIES.TASK:
42
+ logData.color = '#f63d68';
40
43
  logData.icon = 'task';
41
44
  break;
42
- case 'LEAD':
43
- logData.color = '#f63d68';
45
+ case ACTIVITY_CATEGORIES.LEAD:
46
+ logData.color = '#16b364';
44
47
  logData.icon = 'lead';
45
48
  break;
46
-
49
+ case ACTIVITY_CATEGORIES.ASSESSMENT:
50
+ logData.color = '#f63d68';
51
+ logData.icon = 'assessment';
52
+ break;
53
+ case ACTIVITY_CATEGORIES.STAGEGROUP:
54
+ logData.color = '#06aed4';
55
+ logData.icon = 'stage_group';
56
+ break;
47
57
  default:
48
58
  logData.color = '#ff0000';
49
59
  logData.icon = 'DEFAULT';
@@ -8,7 +8,8 @@ import { DataSource, Repository } from 'typeorm';
8
8
  import { WorkflowLevelMappingEntity } from '../entity/workflow-level-mapping.entity';
9
9
  import { InjectRepository } from '@nestjs/typeorm';
10
10
  import { StageMovementData } from '../entity/stage-movement-data.entity';
11
- import * as moment from 'moment-timezone';
11
+ import * as moment from 'moment';
12
+ import { MediaDataService } from 'src/module/meta/service/media-data.service';
12
13
 
13
14
  @Injectable()
14
15
  export class StageGroupService extends EntityServiceImpl {
@@ -21,6 +22,7 @@ export class StageGroupService extends EntityServiceImpl {
21
22
  private readonly workflowLevelMappingRepo: Repository<WorkflowLevelMappingEntity>,
22
23
  @InjectRepository(StageMovementData)
23
24
  private readonly stageMovementRepo: Repository<StageMovementData>,
25
+ private readonly mediaDataService: MediaDataService,
24
26
  ) {
25
27
  super();
26
28
  }
@@ -55,22 +57,21 @@ export class StageGroupService extends EntityServiceImpl {
55
57
 
56
58
  if (!workflowLevelMapping) return null;
57
59
 
58
- // 3. Get lead status
60
+ // 3) Lead status (parameterized)
59
61
  let leadStatus: string | null = null;
60
62
  const leadData = await this.dataSource.query(
61
- `SELECT lead_status FROM crm_lead WHERE id = ${lead_id}`,
63
+ `SELECT lead_status FROM crm_lead WHERE id = ?`,
64
+ [lead_id],
62
65
  );
63
-
64
- if (leadData.length > 0) {
66
+ if (leadData.length > 0 && leadData[0].lead_status != null) {
65
67
  const leadStatusResolved = await this.dataSource.query(
66
- `SELECT name FROM cr_list_master_items WHERE id = ${leadData[0].lead_status} and organization_id = ${loggedInUser.organization_id}`,
68
+ `SELECT name FROM cr_list_master_items WHERE id = ? AND organization_id = ?`,
69
+ [leadData[0].lead_status, loggedInUser.organization_id],
67
70
  );
68
-
69
71
  if (leadStatusResolved.length > 0) {
70
72
  leadStatus = leadStatusResolved[0].name;
71
73
  }
72
74
  }
73
-
74
75
  // 4. Fetch stage groups & hierarchy
75
76
  const stageGroupAndStageHierarchy =
76
77
  (await this.stageGroupRepository.getAllStageGroupAndStageHierarchy(
@@ -108,9 +109,14 @@ export class StageGroupService extends EntityServiceImpl {
108
109
 
109
110
  let foundInProgressGroup = false;
110
111
 
112
+ const userCache = new Map<
113
+ number,
114
+ { created_by_name: string | null; profile: any }
115
+ >();
116
+
111
117
  // 7. Loop & apply IST conversions
112
118
  for (const group of stageGroupAndStageHierarchy) {
113
- for (const stage of group.stages || []) {
119
+ const stagePromises = (group.stages || []).map(async (stage) => {
114
120
  const movement = stageMovementMap.get(Number(stage.id));
115
121
 
116
122
  if (movement) {
@@ -129,6 +135,17 @@ export class StageGroupService extends EntityServiceImpl {
129
135
  movement.start_date,
130
136
  movement.end_date ?? new Date(),
131
137
  );
138
+
139
+ if (movement.current_user_id) {
140
+ if (!userCache.has(movement.current_user_id)) {
141
+ const user = await this.getUserNameAndProfilePic(
142
+ movement.current_user_id,
143
+ loggedInUser,
144
+ );
145
+ userCache.set(movement.current_user_id, user);
146
+ }
147
+ Object.assign(stage, userCache.get(movement.current_user_id));
148
+ }
132
149
  } else {
133
150
  stage.is_current = false;
134
151
  stage.is_done = false;
@@ -136,12 +153,15 @@ export class StageGroupService extends EntityServiceImpl {
136
153
  stage.end_time = null;
137
154
  stage.time_spent = null;
138
155
  stage.created_by_name = null;
156
+ stage.profile = null;
139
157
  }
140
158
 
141
159
  if (leadStatus) {
142
160
  stage.lead_status = leadStatus;
143
161
  }
144
- }
162
+ });
163
+
164
+ await Promise.all(stagePromises);
145
165
 
146
166
  if (group.stages.length === 0) {
147
167
  group.stage_group_status = 'TODO';
@@ -166,6 +186,36 @@ export class StageGroupService extends EntityServiceImpl {
166
186
  return stageGroupAndStageHierarchy;
167
187
  }
168
188
 
189
+ async getUserNameAndProfilePic(
190
+ userId: number,
191
+ loggedInUser: UserData,
192
+ ): Promise<any> {
193
+ const user = await this.dataSource.query(
194
+ `SELECT eth_user_profile.name AS created_by_name,
195
+ eth_user_profile.profile_image AS profile_image
196
+ FROM eth_user_profile
197
+ WHERE eth_user_profile.id = ?;`,
198
+ [userId],
199
+ );
200
+
201
+ if (user.length === 0) return null;
202
+
203
+ let profileImageId = user[0].profile_image;
204
+ let profile: any = null;
205
+
206
+ if (profileImageId) {
207
+ profile = await this.mediaDataService.getMediaDownloadUrl(
208
+ profileImageId,
209
+ loggedInUser,
210
+ );
211
+ }
212
+
213
+ return {
214
+ created_by_name: user[0].created_by_name,
215
+ profile: profile,
216
+ };
217
+ }
218
+
169
219
  async calculateTimeSpent(
170
220
  start: Date | string,
171
221
  end?: Date | string,
@@ -85,8 +85,17 @@ export class TaskService extends EntityServiceImpl {
85
85
  entityData.is_completed = isCompletedStatus;
86
86
  entityData.is_done = isCompletedStatus;
87
87
 
88
+ const isMandatory =
89
+ String(entityData.is_mandatory).toLowerCase() === 'true' ||
90
+ entityData.is_mandatory === true ||
91
+ entityData.is_mandatory === 1 ||
92
+ entityData.is_mandatory === '1';
93
+
88
94
  const updatedEntity = await super.updateEntity(
89
- entityData,
95
+ {
96
+ ...entityData,
97
+ is_mandatory: isMandatory,
98
+ },
90
99
  loggedInUser,
91
100
  appcode,
92
101
  );
@@ -213,33 +222,36 @@ export class TaskService extends EntityServiceImpl {
213
222
  LEFT JOIN cr_user u ON t.created_by = u.id
214
223
  LEFT JOIN cr_user u2 ON t.task_owner = u2.id
215
224
  WHERE ${whereClauses.join(' AND ')}
216
- ORDER BY t.due_date ASC, t.due_time ASC
225
+ ORDER BY t.created_date DESC
217
226
  `;
218
227
 
219
228
  const taskData = await this.dataSource.query(sql, params);
220
229
 
221
- // Loop through each task and resolve task_status
222
- for (const task of taskData) {
223
- if (!task.status) {
224
- task.task_status = null;
225
- continue;
226
- }
230
+ if (taskData.length === 0) {
231
+ return [];
232
+ }
233
+
234
+ // Fetch all TKST statuses in a single query
235
+ const taskStatusQuery = `
236
+ SELECT id, name
237
+ FROM cr_list_master_items
238
+ WHERE organization_id = ?
239
+ AND listtype = 'TKST'
240
+ `;
241
+ const taskAllStatus = await this.dataSource.query(taskStatusQuery, [
242
+ loggedInUser.organization_id,
243
+ ]);
227
244
 
228
- const taskStatusQuery = `
229
- SELECT name
230
- FROM cr_list_master_items
231
- WHERE id = ?
232
- AND organization_id = ?
233
- AND listtype = 'TKST'
234
- LIMIT 1;
235
- `;
236
-
237
- const [statusRow] = await this.dataSource.query(taskStatusQuery, [
238
- task.status,
239
- loggedInUser.organization_id,
240
- ]);
241
-
242
- task.task_status = statusRow?.name || null;
245
+ // Build a hash map { id → name }
246
+ const statusMap = new Map(
247
+ taskAllStatus.map((row) => [String(row.id), row.name]),
248
+ );
249
+
250
+ // Assign task_status using the map
251
+ for (const task of taskData) {
252
+ task.task_status = task.status
253
+ ? statusMap.get(String(task.status)) || null
254
+ : null;
243
255
  }
244
256
 
245
257
  // Normalize is_mandatory to '0'/'1' as string