@dhyasama/totem-models 11.135.0 → 11.137.0
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/lib/Financials.js +161 -11
- package/package.json +1 -1
package/lib/Financials.js
CHANGED
|
@@ -85,9 +85,7 @@ module.exports = function(mongoose, config) {
|
|
|
85
85
|
|
|
86
86
|
year: { type: Number, default: 0 },
|
|
87
87
|
period: { type: String, enum: ['FY', 'H1', 'H2', 'Q1', 'Q2', 'Q3', 'Q4', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] },
|
|
88
|
-
|
|
89
|
-
beginDate: { type: Date },
|
|
90
|
-
endDate: { type: Date },
|
|
88
|
+
cuttoff: { type: Date },
|
|
91
89
|
|
|
92
90
|
form: [{
|
|
93
91
|
name: { type: String, trim: true },
|
|
@@ -145,7 +143,32 @@ module.exports = function(mongoose, config) {
|
|
|
145
143
|
timestamp: { type: Date, required: true, default: Date.now },
|
|
146
144
|
user: { type: String, trim: true, required: true },
|
|
147
145
|
action: { type: String, enum: ['updated', 'deleted', 'verified'], required: true },
|
|
148
|
-
}]
|
|
146
|
+
}],
|
|
147
|
+
previous: {
|
|
148
|
+
_id: false,
|
|
149
|
+
value: { type: Number },
|
|
150
|
+
breakdown: { type: Schema.Types.Mixed },
|
|
151
|
+
source: {
|
|
152
|
+
_id: false,
|
|
153
|
+
model: { type: String, enum: ['Document', 'Meeting', 'Message', 'Note'] },
|
|
154
|
+
ref: { type: Schema.Types.ObjectId },
|
|
155
|
+
position: {
|
|
156
|
+
_id: false,
|
|
157
|
+
page: { type: Number },
|
|
158
|
+
bbox: { _id: false, top: { type: Number }, left: { type: Number }, width: { type: Number }, height: { type: Number } },
|
|
159
|
+
row: { type: Number },
|
|
160
|
+
col: { type: Number },
|
|
161
|
+
sheet: { type: String }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
verified: { type: Boolean },
|
|
165
|
+
history: [{
|
|
166
|
+
_id: false,
|
|
167
|
+
timestamp: { type: Date },
|
|
168
|
+
user: { type: String, trim: true },
|
|
169
|
+
action: { type: String, enum: ['updated', 'deleted', 'verified'] },
|
|
170
|
+
}]
|
|
171
|
+
}
|
|
149
172
|
}],
|
|
150
173
|
|
|
151
174
|
questions: [{
|
|
@@ -369,12 +392,21 @@ module.exports = function(mongoose, config) {
|
|
|
369
392
|
console.log('Upserting metrics for snapshot', snapshotUUID)
|
|
370
393
|
console.log('Metrics to upsert:', metricsToUpdate)
|
|
371
394
|
|
|
372
|
-
session.then(_session => {
|
|
395
|
+
session.then(async _session => {
|
|
373
396
|
|
|
374
397
|
_session.startTransaction();
|
|
375
398
|
|
|
376
399
|
console.log('started session');
|
|
377
400
|
|
|
401
|
+
// Fetch the current document to read existing metric values before overwriting
|
|
402
|
+
let currentDoc = null;
|
|
403
|
+
try {
|
|
404
|
+
currentDoc = await self.findOne({ _id: financialsId, 'snapshots.uuid': snapshotUUID }, null, { session: _session });
|
|
405
|
+
} catch (e) {
|
|
406
|
+
console.log('Could not fetch current metrics for previous state:', e);
|
|
407
|
+
}
|
|
408
|
+
const currentSnapshot = currentDoc ? currentDoc.snapshots.find(s => s.uuid === snapshotUUID) : null;
|
|
409
|
+
|
|
378
410
|
const promises = metricsToUpdate.map(metric => {
|
|
379
411
|
|
|
380
412
|
console.log('Upserting metric:', metric);
|
|
@@ -383,9 +415,32 @@ module.exports = function(mongoose, config) {
|
|
|
383
415
|
console.log('Upserting metric value:', metric.value);
|
|
384
416
|
console.log('Upserting metric breakdown:', metric.breakdown);
|
|
385
417
|
|
|
418
|
+
// Capture the current metric state to save as `previous`
|
|
419
|
+
// Use null (not undefined) for missing fields so MongoDB persists them
|
|
420
|
+
const currentMetric = currentSnapshot ? currentSnapshot.metrics.find(m => m.name === metric.name && m.scenario === metric.scenario) : null;
|
|
421
|
+
const previousState = currentMetric ? {
|
|
422
|
+
value: currentMetric.value != null ? currentMetric.value : null,
|
|
423
|
+
breakdown: currentMetric.breakdown != null ? currentMetric.breakdown : null,
|
|
424
|
+
source: currentMetric.source || null,
|
|
425
|
+
verified: currentMetric.verified != null ? currentMetric.verified : null,
|
|
426
|
+
history: currentMetric.history || []
|
|
427
|
+
} : undefined;
|
|
428
|
+
|
|
386
429
|
// Check if the metric exists and update it
|
|
387
430
|
// Use $elemMatch to ensure we find a metric with BOTH name AND scenario matching
|
|
388
431
|
// (without $elemMatch, MongoDB could match name on one metric and scenario on another)
|
|
432
|
+
const $set = {
|
|
433
|
+
'snapshots.$.metrics.$[metric].value': metric.value,
|
|
434
|
+
'snapshots.$.metrics.$[metric].breakdown': metric.breakdown,
|
|
435
|
+
'snapshots.$.metrics.$[metric].source': metric.source,
|
|
436
|
+
'snapshots.$.metrics.$[metric].verified': metric.verified
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// Save previous state if the metric already exists
|
|
440
|
+
if (previousState) {
|
|
441
|
+
$set['snapshots.$.metrics.$[metric].previous'] = previousState;
|
|
442
|
+
}
|
|
443
|
+
|
|
389
444
|
return self.findOneAndUpdate(
|
|
390
445
|
{
|
|
391
446
|
_id: financialsId,
|
|
@@ -402,12 +457,7 @@ module.exports = function(mongoose, config) {
|
|
|
402
457
|
}
|
|
403
458
|
},
|
|
404
459
|
{
|
|
405
|
-
$set:
|
|
406
|
-
'snapshots.$.metrics.$[metric].value': metric.value,
|
|
407
|
-
'snapshots.$.metrics.$[metric].breakdown': metric.breakdown,
|
|
408
|
-
'snapshots.$.metrics.$[metric].source': metric.source,
|
|
409
|
-
'snapshots.$.metrics.$[metric].verified': metric.verified
|
|
410
|
-
},
|
|
460
|
+
$set: $set,
|
|
411
461
|
$push: {
|
|
412
462
|
'snapshots.$.metrics.$[metric].history': {
|
|
413
463
|
$each: metric.history || [],
|
|
@@ -462,6 +512,95 @@ module.exports = function(mongoose, config) {
|
|
|
462
512
|
|
|
463
513
|
}
|
|
464
514
|
|
|
515
|
+
Financials.statics.undoMetrics = function undoMetrics(financialsId, snapshotUUID, metricsToUndo, cb) {
|
|
516
|
+
|
|
517
|
+
const self = this;
|
|
518
|
+
const session = mongoose.startSession();
|
|
519
|
+
|
|
520
|
+
session.then(async _session => {
|
|
521
|
+
|
|
522
|
+
_session.startTransaction();
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
|
|
526
|
+
const currentDoc = await self.findOne({ _id: financialsId, 'snapshots.uuid': snapshotUUID }, null, { session: _session });
|
|
527
|
+
if (!currentDoc) { throw new Error('Financials not found'); }
|
|
528
|
+
|
|
529
|
+
const snapshot = currentDoc.snapshots.find(s => s.uuid === snapshotUUID);
|
|
530
|
+
if (!snapshot) { throw new Error('Snapshot not found'); }
|
|
531
|
+
|
|
532
|
+
const promises = metricsToUndo.map(metricId => {
|
|
533
|
+
|
|
534
|
+
const currentMetric = snapshot.metrics.find(m => m.name === metricId.name && m.scenario === metricId.scenario);
|
|
535
|
+
if (!currentMetric || !currentMetric.previous) {
|
|
536
|
+
console.log('No previous state for metric:', metricId.name, metricId.scenario);
|
|
537
|
+
return Promise.resolve();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const prev = currentMetric.previous;
|
|
541
|
+
const prefix = 'snapshots.$.metrics.$[metric]';
|
|
542
|
+
|
|
543
|
+
// Restore fields that had values; unset fields that were null (didn't exist before)
|
|
544
|
+
const $set = {};
|
|
545
|
+
const $unset = { [prefix + '.previous']: 1 };
|
|
546
|
+
|
|
547
|
+
const fields = ['value', 'breakdown', 'source', 'verified'];
|
|
548
|
+
fields.forEach(field => {
|
|
549
|
+
if (prev[field] != null) {
|
|
550
|
+
$set[prefix + '.' + field] = prev[field];
|
|
551
|
+
} else {
|
|
552
|
+
$unset[prefix + '.' + field] = 1;
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// History is always restored (empty array if it was empty)
|
|
557
|
+
$set[prefix + '.history'] = prev.history || [];
|
|
558
|
+
|
|
559
|
+
return self.findOneAndUpdate(
|
|
560
|
+
{
|
|
561
|
+
_id: financialsId,
|
|
562
|
+
snapshots: {
|
|
563
|
+
$elemMatch: {
|
|
564
|
+
uuid: snapshotUUID,
|
|
565
|
+
metrics: {
|
|
566
|
+
$elemMatch: {
|
|
567
|
+
name: metricId.name,
|
|
568
|
+
scenario: metricId.scenario
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
$set: $set,
|
|
576
|
+
$unset: $unset
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
arrayFilters: [{ 'metric.name': metricId.name, 'metric.scenario': metricId.scenario }],
|
|
580
|
+
new: true,
|
|
581
|
+
session: _session
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
await Promise.all(promises);
|
|
588
|
+
await _session.commitTransaction();
|
|
589
|
+
_session.endSession();
|
|
590
|
+
console.log('Undo complete.');
|
|
591
|
+
return cb();
|
|
592
|
+
|
|
593
|
+
} catch (error) {
|
|
594
|
+
await _session.abortTransaction();
|
|
595
|
+
_session.endSession();
|
|
596
|
+
console.error(error);
|
|
597
|
+
return cb(error);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
}
|
|
603
|
+
|
|
465
604
|
Financials.statics.getAll = function getAll(options, cb) {
|
|
466
605
|
|
|
467
606
|
const self = this;
|
|
@@ -1102,6 +1241,17 @@ module.exports = function(mongoose, config) {
|
|
|
1102
1241
|
|
|
1103
1242
|
updateStatus(self);
|
|
1104
1243
|
|
|
1244
|
+
// Wipe out previous values on metrics when a metric is verified
|
|
1245
|
+
_.each(self.snapshots, function(snapshot) {
|
|
1246
|
+
if (snapshot) {
|
|
1247
|
+
_.each(snapshot.metrics, function(metric) {
|
|
1248
|
+
if (metric && metric.verified && metric.previous) {
|
|
1249
|
+
metric.previous = undefined;
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1105
1255
|
return next();
|
|
1106
1256
|
|
|
1107
1257
|
});
|