vr-commons 1.0.159 → 1.0.160

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.
@@ -1,6 +1,6 @@
1
1
  import { Device, UserRole } from "vr-models";
2
2
  import { Transaction } from "sequelize";
3
- import { LockAllDevicesResult, LockDevicesOptions, StockChangeResult } from "./types";
3
+ import { DeviceResetOptions, DeviceResetResult, StockChangeResult } from "./types";
4
4
  export declare const stockUtil: {
5
5
  /**
6
6
  * Increment stock when devices are added/created
@@ -121,9 +121,10 @@ export declare const stockUtil: {
121
121
  devices: Device[];
122
122
  }>;
123
123
  /**
124
- * Lock ALL device pairs regardless of payment plan status
125
- * Locks both RIDER and PASSENGER devices in each pair together
126
- * Uses batch processing for large datasets
124
+ * Reset device pairs status based on conditions
125
+ * - Locks/unlocks both RIDER and PASSENGER devices in each pair together
126
+ * - Flexible conditions and updates
127
+ * - Uses batch processing for large datasets
127
128
  */
128
- lockAllDevices(options?: LockDevicesOptions): Promise<LockAllDevicesResult>;
129
+ resetAllDevicesStatus(options?: DeviceResetOptions): Promise<DeviceResetResult>;
129
130
  };
@@ -17,6 +17,155 @@ const extractDedicationFromSerial = (serialNumber) => {
17
17
  return "PASSENGER";
18
18
  return "N/A";
19
19
  };
20
+ // ==================== HELPER FUNCTIONS ====================
21
+ function buildWhereClause(condition) {
22
+ const parts = [];
23
+ if (condition.status) {
24
+ const statuses = Array.isArray(condition.status)
25
+ ? condition.status
26
+ : [condition.status];
27
+ const quoted = statuses.map((s) => `'${s}'`).join(", ");
28
+ parts.push(`"status" IN (${quoted})`);
29
+ }
30
+ if (condition.excludeStatus) {
31
+ const statuses = Array.isArray(condition.excludeStatus)
32
+ ? condition.excludeStatus
33
+ : [condition.excludeStatus];
34
+ const quoted = statuses.map((s) => `'${s}'`).join(", ");
35
+ parts.push(`"status" NOT IN (${quoted})`);
36
+ }
37
+ if (condition.isPermanentlyUnlocked !== undefined) {
38
+ parts.push(`"isPermanentlyUnlocked" = ${condition.isPermanentlyUnlocked}`);
39
+ }
40
+ if (condition.pairId) {
41
+ const pairIds = Array.isArray(condition.pairId)
42
+ ? condition.pairId
43
+ : [condition.pairId];
44
+ const quoted = pairIds.map((p) => `'${p}'`).join(", ");
45
+ parts.push(`"pairId" IN (${quoted})`);
46
+ }
47
+ if (condition.excludePairIds) {
48
+ const pairIds = Array.isArray(condition.excludePairIds)
49
+ ? condition.excludePairIds
50
+ : [condition.excludePairIds];
51
+ const quoted = pairIds.map((p) => `'${p}'`).join(", ");
52
+ parts.push(`"pairId" NOT IN (${quoted})`);
53
+ }
54
+ if (condition.productId) {
55
+ const productIds = Array.isArray(condition.productId)
56
+ ? condition.productId
57
+ : [condition.productId];
58
+ const quoted = productIds.map((p) => `'${p}'`).join(", ");
59
+ parts.push(`"productId" IN (${quoted})`);
60
+ }
61
+ if (condition.dedicatedUser) {
62
+ const users = Array.isArray(condition.dedicatedUser)
63
+ ? condition.dedicatedUser
64
+ : [condition.dedicatedUser];
65
+ const quoted = users.map((u) => `'${u}'`).join(", ");
66
+ parts.push(`"dedicatedUser" IN (${quoted})`);
67
+ }
68
+ if (condition.createdBefore) {
69
+ parts.push(`"createdAt" < '${condition.createdBefore.toISOString()}'`);
70
+ }
71
+ if (condition.createdAfter) {
72
+ parts.push(`"createdAt" > '${condition.createdAfter.toISOString()}'`);
73
+ }
74
+ return parts.length > 0 ? parts.join(" AND ") : "1=1";
75
+ }
76
+ function buildSequelizeWhere(condition) {
77
+ const where = {};
78
+ if (condition.status) {
79
+ where.status = {
80
+ [sequelize_1.Op.in]: Array.isArray(condition.status)
81
+ ? condition.status
82
+ : [condition.status],
83
+ };
84
+ }
85
+ if (condition.excludeStatus) {
86
+ where.status = {
87
+ ...where.status,
88
+ [sequelize_1.Op.notIn]: Array.isArray(condition.excludeStatus)
89
+ ? condition.excludeStatus
90
+ : [condition.excludeStatus],
91
+ };
92
+ }
93
+ if (condition.isPermanentlyUnlocked !== undefined) {
94
+ where.isPermanentlyUnlocked = condition.isPermanentlyUnlocked;
95
+ }
96
+ if (condition.pairId) {
97
+ where.pairId = {
98
+ [sequelize_1.Op.in]: Array.isArray(condition.pairId)
99
+ ? condition.pairId
100
+ : [condition.pairId],
101
+ };
102
+ }
103
+ if (condition.excludePairIds) {
104
+ where.pairId = {
105
+ [sequelize_1.Op.notIn]: Array.isArray(condition.excludePairIds)
106
+ ? condition.excludePairIds
107
+ : [condition.excludePairIds],
108
+ };
109
+ }
110
+ if (condition.productId) {
111
+ where.productId = {
112
+ [sequelize_1.Op.in]: Array.isArray(condition.productId)
113
+ ? condition.productId
114
+ : [condition.productId],
115
+ };
116
+ }
117
+ if (condition.dedicatedUser) {
118
+ where.dedicatedUser = {
119
+ [sequelize_1.Op.in]: Array.isArray(condition.dedicatedUser)
120
+ ? condition.dedicatedUser
121
+ : [condition.dedicatedUser],
122
+ };
123
+ }
124
+ if (condition.createdBefore) {
125
+ where.createdAt = { [sequelize_1.Op.lt]: condition.createdBefore };
126
+ }
127
+ if (condition.createdAfter) {
128
+ where.createdAt = { ...where.createdAt, [sequelize_1.Op.gt]: condition.createdAfter };
129
+ }
130
+ return where;
131
+ }
132
+ function buildSetClause(update) {
133
+ const parts = [];
134
+ if (update.status !== undefined) {
135
+ parts.push(`"status" = '${update.status}'`);
136
+ }
137
+ if (update.isPermanentlyUnlocked !== undefined) {
138
+ parts.push(`"isPermanentlyUnlocked" = ${update.isPermanentlyUnlocked}`);
139
+ }
140
+ if (update.dedicatedUser !== undefined) {
141
+ parts.push(`"dedicatedUser" = '${update.dedicatedUser}'`);
142
+ }
143
+ if (update.devicePaymentPlanId !== undefined) {
144
+ parts.push(`"devicePaymentPlanId" = ${update.devicePaymentPlanId === null
145
+ ? "NULL"
146
+ : `'${update.devicePaymentPlanId}'`}`);
147
+ }
148
+ if (update.activatedAt !== undefined) {
149
+ parts.push(`"activatedAt" = ${update.activatedAt === null
150
+ ? "NULL"
151
+ : `'${update.activatedAt.toISOString()}'`}`);
152
+ }
153
+ return parts.length > 0 ? parts.join(", ") : "1=1";
154
+ }
155
+ function buildUpdateObject(update) {
156
+ const obj = {};
157
+ if (update.status !== undefined)
158
+ obj.status = update.status;
159
+ if (update.isPermanentlyUnlocked !== undefined)
160
+ obj.isPermanentlyUnlocked = update.isPermanentlyUnlocked;
161
+ if (update.dedicatedUser !== undefined)
162
+ obj.dedicatedUser = update.dedicatedUser;
163
+ if (update.devicePaymentPlanId !== undefined)
164
+ obj.devicePaymentPlanId = update.devicePaymentPlanId;
165
+ if (update.activatedAt !== undefined)
166
+ obj.activatedAt = update.activatedAt;
167
+ return obj;
168
+ }
20
169
  exports.stockUtil = {
21
170
  /**
22
171
  * Increment stock when devices are added/created
@@ -453,41 +602,48 @@ exports.stockUtil = {
453
602
  };
454
603
  },
455
604
  /**
456
- * Lock ALL device pairs regardless of payment plan status
457
- * Locks both RIDER and PASSENGER devices in each pair together
458
- * Uses batch processing for large datasets
605
+ * Reset device pairs status based on conditions
606
+ * - Locks/unlocks both RIDER and PASSENGER devices in each pair together
607
+ * - Flexible conditions and updates
608
+ * - Uses batch processing for large datasets
459
609
  */
460
- async lockAllDevices(options = {}) {
461
- const { actorId = "SYSTEM", actorRole = "SYSTEM", transaction, batchSize = 100, maxRetries = 3, triggeredBy = "manual", } = options;
610
+ async resetAllDevicesStatus(options = {}) {
611
+ const { actorId = "SYSTEM", actorRole = "SYSTEM", transaction, batchSize = 100, maxRetries = 3, triggeredBy = "manual", condition = {}, update = {}, } = options;
462
612
  const isSystem = actorRole === "SYSTEM" || triggeredBy === "cronjob";
463
613
  try {
464
- const lockedPairIds = [];
614
+ const updatedPairIds = [];
465
615
  const failedPairIds = [];
466
616
  const errors = [];
467
- let totalLockedDevices = 0;
617
+ const allDeviceIds = [];
618
+ let totalUpdatedDevices = 0;
468
619
  let totalProcessedPairs = 0;
469
620
  let batchNumber = 0;
470
621
  let hasMore = true;
471
622
  let offset = 0;
472
- console.log(`🔒 Locking ALL device pairs at ${new Date().toISOString()}`);
473
- // Log start of job
623
+ // Build WHERE clause for the query
624
+ const whereClause = buildWhereClause(condition);
625
+ // Build UPDATE SET clause
626
+ const setClause = buildSetClause(update);
627
+ console.log(`🔄 Resetting device pairs at ${new Date().toISOString()}`);
628
+ console.log(`📋 WHERE: ${whereClause}`);
629
+ console.log(`📝 SET: ${setClause}`);
630
+ // Log start
474
631
  if (isSystem) {
475
- await (0, systemLog_utils_1.logInfo)(`Lock all device pairs job started`, {
632
+ await (0, systemLog_utils_1.logInfo)(`Reset device pairs job started`, {
476
633
  category: "CRONJOB",
477
- source: "device-lock.cronjob",
478
- details: { triggeredBy, batchSize, maxRetries },
634
+ source: "device-reset.cronjob",
635
+ details: { triggeredBy, batchSize, maxRetries, condition, update },
479
636
  });
480
637
  }
481
638
  else {
482
- await (0, systemLog_utils_1.logInfo)(`Lock all device pairs triggered by ${actorRole}`, {
639
+ await (0, systemLog_utils_1.logInfo)(`Reset device pairs triggered by ${actorRole}`, {
483
640
  category: "DEVICE",
484
641
  userId: actorId || undefined,
485
642
  userRole: actorRole,
486
- source: "device-lock.manual",
487
- details: { triggeredBy, batchSize, maxRetries },
643
+ source: "device-reset.manual",
644
+ details: { triggeredBy, batchSize, maxRetries, condition, update },
488
645
  });
489
646
  }
490
- // ✅ Get the sequelize instance from the model
491
647
  const sequelizeInstance = vr_models_1.Device.sequelize;
492
648
  if (!sequelizeInstance) {
493
649
  throw new Error("Sequelize instance not available");
@@ -496,12 +652,12 @@ exports.stockUtil = {
496
652
  while (hasMore) {
497
653
  batchNumber++;
498
654
  offset = (batchNumber - 1) * batchSize;
499
- // ✅ Use raw query to get distinct pairIds with both devices
655
+ // Get distinct pairIds that match the condition
500
656
  const [pairIdsResult] = await sequelizeInstance.query(`
501
657
  SELECT "pairId"
502
658
  FROM "devices"
503
- WHERE "status" != 'disabled'
504
- AND "deletedAt" IS NULL
659
+ WHERE "deletedAt" IS NULL
660
+ AND ${whereClause}
505
661
  GROUP BY "pairId"
506
662
  HAVING COUNT(*) = 2
507
663
  ORDER BY "pairId" ASC
@@ -522,71 +678,48 @@ exports.stockUtil = {
522
678
  }
523
679
  totalProcessedPairs += pairIds.length;
524
680
  console.log(`📦 Batch ${batchNumber}: Processing ${pairIds.length} pairs (Total: ${totalProcessedPairs})`);
525
- // Process each pair
526
681
  for (const pairId of pairIds) {
527
682
  let retries = 0;
528
- let pairLocked = false;
529
- while (!pairLocked && retries < maxRetries) {
683
+ let pairUpdated = false;
684
+ while (!pairUpdated && retries < maxRetries) {
530
685
  try {
531
- // Get both devices in the pair
532
686
  const devices = await vr_models_1.Device.findAll({
533
687
  where: {
534
688
  pairId: pairId,
535
- status: { [sequelize_1.Op.ne]: "disabled" },
536
689
  deletedAt: null,
690
+ ...buildSequelizeWhere(condition),
537
691
  },
538
692
  transaction: transaction,
539
693
  });
540
694
  if (devices.length !== 2) {
541
695
  throw new Error(`Incomplete pair ${pairId}: expected 2 devices, found ${devices.length}`);
542
696
  }
543
- // Check if both devices are already locked
544
- const allLocked = devices.every((d) => d.status === "locked");
545
- if (allLocked) {
546
- console.log(`🔒 Pair ${pairId} already fully locked`);
547
- pairLocked = true;
697
+ // Check if update would be a no-op
698
+ const shouldUpdate = devices.some((d) => {
699
+ const updateObj = buildUpdateObject(update);
700
+ return Object.keys(updateObj).some((key) => d[key] !== updateObj[key]);
701
+ });
702
+ if (!shouldUpdate) {
703
+ console.log(`⏭️ Pair ${pairId} already matches target state`);
704
+ pairUpdated = true;
548
705
  break;
549
706
  }
550
- // Lock both devices
551
707
  const deviceIds = devices.map((d) => d.id);
552
- await vr_models_1.Device.update({ status: "locked" }, {
708
+ allDeviceIds.push(...deviceIds);
709
+ await vr_models_1.Device.update(buildUpdateObject(update), {
553
710
  where: {
554
711
  id: { [sequelize_1.Op.in]: deviceIds },
555
712
  },
556
713
  transaction: transaction,
557
714
  });
558
- lockedPairIds.push(pairId);
559
- totalLockedDevices += devices.length;
560
- pairLocked = true;
561
- console.log(`🔒 Locked pair ${pairId} (${devices.length} devices)`);
562
- // Log event for each device in the pair
563
- for (const device of devices) {
564
- await (0, eventLog_utils_1.logEvent)({
565
- actorId: isSystem ? null : actorId,
566
- actorType: isSystem ? "ADMIN" : actorRole,
567
- action: "DEVICES_LOCKED",
568
- entity: "Device",
569
- entityId: device.id,
570
- metadata: {
571
- serialNumber: device.serialNumber,
572
- pairId: device.pairId,
573
- dedicatedUser: device.dedicatedUser,
574
- productId: device.productId,
575
- lockedAt: new Date().toISOString(),
576
- reason: "Scheduled lock all devices",
577
- triggeredBy: triggeredBy,
578
- batchNumber,
579
- previousStatus: device.status,
580
- isPartOfPair: true,
581
- },
582
- deviceSession: null,
583
- transaction: transaction,
584
- });
585
- }
715
+ updatedPairIds.push(pairId);
716
+ totalUpdatedDevices += devices.length;
717
+ pairUpdated = true;
718
+ console.log(`✅ Updated pair ${pairId} (${devices.length} devices)`);
586
719
  }
587
720
  catch (err) {
588
721
  retries++;
589
- console.error(`❌ Failed to lock pair ${pairId} (attempt ${retries}/${maxRetries}):`, err instanceof Error ? err.message : "Unknown error");
722
+ console.error(`❌ Failed to update pair ${pairId} (attempt ${retries}/${maxRetries}):`, err instanceof Error ? err.message : "Unknown error");
590
723
  if (retries >= maxRetries) {
591
724
  failedPairIds.push(pairId);
592
725
  errors.push(`Pair ${pairId}: ${err instanceof Error ? err.message : "Unknown error"}`);
@@ -603,25 +736,56 @@ exports.stockUtil = {
603
736
  }
604
737
  const result = {
605
738
  success: true,
606
- lockedCount: totalLockedDevices,
739
+ updatedCount: totalUpdatedDevices,
607
740
  failedCount: failedPairIds.length,
608
741
  failedPairIds,
609
742
  errors,
610
743
  totalProcessed: totalProcessedPairs,
611
744
  pairsProcessed: totalProcessedPairs,
612
- message: `Successfully locked ${totalLockedDevices} devices (${lockedPairIds.length} pairs) out of ${totalProcessedPairs} pairs processed${failedPairIds.length > 0
745
+ message: `Successfully updated ${totalUpdatedDevices} devices (${updatedPairIds.length} pairs) out of ${totalProcessedPairs} pairs processed${failedPairIds.length > 0
613
746
  ? `, ${failedPairIds.length} pairs failed`
614
747
  : ""}`,
615
748
  };
616
- console.log(`✅ Lock all device pairs completed:`, result);
617
- // Log completion
749
+ console.log(`✅ Reset device pairs completed:`, result);
750
+ // Log single event with summary
751
+ await (0, eventLog_utils_1.logEvent)({
752
+ actorId: isSystem ? null : actorId,
753
+ actorType: isSystem ? "ADMIN" : actorRole,
754
+ action: "DEVICES_UPDATED",
755
+ entity: "Device",
756
+ entityId: null,
757
+ metadata: {
758
+ action: "RESET_DEVICES_STATUS",
759
+ triggeredBy: triggeredBy,
760
+ condition: condition,
761
+ update: update,
762
+ summary: {
763
+ totalPairsProcessed: result.pairsProcessed,
764
+ totalDevicesUpdated: result.updatedCount,
765
+ totalPairsUpdated: updatedPairIds.length,
766
+ failedPairs: result.failedCount,
767
+ failedPairIds: result.failedPairIds,
768
+ errors: result.errors,
769
+ deviceIds: allDeviceIds.slice(0, 100),
770
+ },
771
+ batchDetails: {
772
+ totalBatches: batchNumber,
773
+ batchSize: batchSize,
774
+ maxRetries: maxRetries,
775
+ },
776
+ timestamp: new Date().toISOString(),
777
+ },
778
+ deviceSession: null,
779
+ transaction: transaction,
780
+ });
781
+ // Log to system logs
618
782
  if (isSystem) {
619
- await (0, systemLog_utils_1.logInfo)(`Lock all device pairs job completed`, {
783
+ await (0, systemLog_utils_1.logInfo)(`Reset device pairs job completed`, {
620
784
  category: "CRONJOB",
621
- source: "device-lock.cronjob",
785
+ source: "device-reset.cronjob",
622
786
  details: {
623
- lockedDevices: result.lockedCount,
624
- lockedPairs: lockedPairIds.length,
787
+ updatedDevices: result.updatedCount,
788
+ updatedPairs: updatedPairIds.length,
625
789
  failedPairs: result.failedCount,
626
790
  totalPairs: result.pairsProcessed,
627
791
  hasErrors: result.errors.length > 0,
@@ -629,14 +793,14 @@ exports.stockUtil = {
629
793
  });
630
794
  }
631
795
  else {
632
- await (0, systemLog_utils_1.logInfo)(`Lock all device pairs completed by ${actorRole}`, {
796
+ await (0, systemLog_utils_1.logInfo)(`Reset device pairs completed by ${actorRole}`, {
633
797
  category: "DEVICE",
634
798
  userId: actorId || undefined,
635
799
  userRole: actorRole,
636
- source: "device-lock.manual",
800
+ source: "device-reset.manual",
637
801
  details: {
638
- lockedDevices: result.lockedCount,
639
- lockedPairs: lockedPairIds.length,
802
+ updatedDevices: result.updatedCount,
803
+ updatedPairs: updatedPairIds.length,
640
804
  failedPairs: result.failedCount,
641
805
  totalPairs: result.pairsProcessed,
642
806
  hasErrors: result.errors.length > 0,
@@ -644,9 +808,9 @@ exports.stockUtil = {
644
808
  });
645
809
  }
646
810
  if (result.errors.length > 0) {
647
- await (0, systemLog_utils_1.logError)(`Lock all device pairs had ${result.errors.length} failures`, {
811
+ await (0, systemLog_utils_1.logError)(`Reset device pairs had ${result.errors.length} failures`, {
648
812
  category: isSystem ? "CRONJOB" : "DEVICE",
649
- source: isSystem ? "device-lock.cronjob" : "device-lock.manual",
813
+ source: isSystem ? "device-reset.cronjob" : "device-reset.manual",
650
814
  userId: isSystem ? null : actorId || undefined,
651
815
  userRole: isSystem ? null : actorRole,
652
816
  details: {
@@ -658,31 +822,31 @@ exports.stockUtil = {
658
822
  return result;
659
823
  }
660
824
  catch (err) {
661
- console.error("❌ Lock all device pairs failed:", err);
825
+ console.error("❌ Reset device pairs failed:", err);
662
826
  const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
663
827
  const result = {
664
828
  success: false,
665
- lockedCount: 0,
829
+ updatedCount: 0,
666
830
  failedCount: 0,
667
831
  failedPairIds: [],
668
832
  errors: [errorMessage],
669
833
  totalProcessed: 0,
670
834
  pairsProcessed: 0,
671
- message: `Lock all device pairs job failed: ${errorMessage}`,
835
+ message: `Reset device pairs job failed: ${errorMessage}`,
672
836
  };
673
837
  if (isSystem) {
674
- await (0, systemLog_utils_1.logError)(`Lock all device pairs job failed`, {
838
+ await (0, systemLog_utils_1.logError)(`Reset device pairs job failed`, {
675
839
  category: "CRONJOB",
676
- source: "device-lock.cronjob",
840
+ source: "device-reset.cronjob",
677
841
  details: { error: errorMessage },
678
842
  });
679
843
  }
680
844
  else {
681
- await (0, systemLog_utils_1.logError)(`Lock all device pairs failed`, {
845
+ await (0, systemLog_utils_1.logError)(`Reset device pairs failed`, {
682
846
  category: "DEVICE",
683
847
  userId: actorId || undefined,
684
848
  userRole: actorRole,
685
- source: "device-lock.manual",
849
+ source: "device-reset.manual",
686
850
  details: { error: errorMessage },
687
851
  });
688
852
  }
@@ -35,9 +35,9 @@ export interface StockChangeResult {
35
35
  productId: string;
36
36
  productName: string;
37
37
  }
38
- export interface LockAllDevicesResult {
38
+ export interface DeviceResetResult {
39
39
  success: boolean;
40
- lockedCount: number;
40
+ updatedCount: number;
41
41
  failedCount: number;
42
42
  failedPairIds: string[];
43
43
  errors: string[];
@@ -45,11 +45,29 @@ export interface LockAllDevicesResult {
45
45
  totalProcessed: number;
46
46
  pairsProcessed: number;
47
47
  }
48
- export interface LockDevicesOptions {
48
+ export interface DeviceResetOptions {
49
49
  actorId?: string;
50
50
  actorRole?: UserRole | string;
51
51
  transaction?: any;
52
52
  batchSize?: number;
53
53
  maxRetries?: number;
54
54
  triggeredBy?: "cronjob" | "manual";
55
+ condition?: {
56
+ status?: string | string[];
57
+ isPermanentlyUnlocked?: boolean;
58
+ pairId?: string | string[];
59
+ productId?: string | string[];
60
+ dedicatedUser?: string | string[];
61
+ createdBefore?: Date;
62
+ createdAfter?: Date;
63
+ excludeStatus?: string | string[];
64
+ excludePairIds?: string | string[];
65
+ };
66
+ update?: {
67
+ status?: string;
68
+ isPermanentlyUnlocked?: boolean;
69
+ dedicatedUser?: string;
70
+ devicePaymentPlanId?: string | null;
71
+ activatedAt?: Date | null;
72
+ };
55
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vr-commons",
3
- "version": "1.0.159",
3
+ "version": "1.0.160",
4
4
  "description": "Shared functions package",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",