@ironcode/vas-lib 2.3.0 → 3.0.0-alpha.10
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/cjs/lib/control-value/vas-camera-control-value.model.d.ts +6 -2
- package/cjs/lib/control-value/vas-camera-control-value.model.d.ts.map +1 -1
- package/cjs/lib/control-value/vas-camera-control-value.model.js +10 -0
- package/cjs/lib/control-value/vas-camera-control-value.model.js.map +1 -1
- package/cjs/lib/entity/vas-base.model.d.ts +1 -1
- package/cjs/lib/entity/vas-base.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-base.model.js.map +1 -1
- package/cjs/lib/entity/vas-control.model.d.ts +4 -4
- package/cjs/lib/entity/vas-control.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-control.model.js +1 -1
- package/cjs/lib/entity/vas-control.model.js.map +1 -1
- package/cjs/lib/entity/vas-field.dto.d.ts +4 -7
- package/cjs/lib/entity/vas-field.dto.d.ts.map +1 -1
- package/cjs/lib/entity/vas-field.dto.js.map +1 -1
- package/cjs/lib/entity/vas-field.model.d.ts +4 -4
- package/cjs/lib/entity/vas-field.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-field.model.js +1 -1
- package/cjs/lib/entity/vas-field.model.js.map +1 -1
- package/cjs/lib/entity/vas-form.model.d.ts +4 -4
- package/cjs/lib/entity/vas-form.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-form.model.js +1 -1
- package/cjs/lib/entity/vas-form.model.js.map +1 -1
- package/cjs/lib/entity/vas-group.model.d.ts +2 -2
- package/cjs/lib/entity/vas-group.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-group.model.js.map +1 -1
- package/cjs/lib/entity/vas-job-copy.d.ts +12 -12
- package/cjs/lib/entity/vas-job-copy.d.ts.map +1 -1
- package/cjs/lib/entity/vas-job-copy.js.map +1 -1
- package/cjs/lib/entity/vas-job.dto.d.ts +12 -6
- package/cjs/lib/entity/vas-job.dto.d.ts.map +1 -1
- package/cjs/lib/entity/vas-job.dto.js.map +1 -1
- package/cjs/lib/entity/vas-job.model.d.ts +34 -142
- package/cjs/lib/entity/vas-job.model.d.ts.map +1 -1
- package/cjs/lib/entity/vas-job.model.js +248 -341
- package/cjs/lib/entity/vas-job.model.js.map +1 -1
- package/cjs/lib/model/estimate-control/estimate-control-value.dto.d.ts +14 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.dto.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.dto.js +3 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.dto.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.model.d.ts +11 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.model.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.model.js +84 -0
- package/cjs/lib/model/estimate-control/estimate-control-value.model.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-item.dto.d.ts +16 -0
- package/cjs/lib/model/estimate-control/estimate-item.dto.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-item.dto.js +3 -0
- package/cjs/lib/model/estimate-control/estimate-item.dto.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-item.model.d.ts +24 -0
- package/cjs/lib/model/estimate-control/estimate-item.model.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate-item.model.js +121 -0
- package/cjs/lib/model/estimate-control/estimate-item.model.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.dto.d.ts +37 -0
- package/cjs/lib/model/estimate-control/estimate.dto.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.dto.js +3 -0
- package/cjs/lib/model/estimate-control/estimate.dto.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.model.d.ts +55 -0
- package/cjs/lib/model/estimate-control/estimate.model.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.model.js +279 -0
- package/cjs/lib/model/estimate-control/estimate.model.js.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.types.d.ts +3 -0
- package/cjs/lib/model/estimate-control/estimate.types.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/estimate.types.js +3 -0
- package/cjs/lib/model/estimate-control/estimate.types.js.map +1 -0
- package/cjs/lib/model/estimate-control/index.d.ts +8 -0
- package/cjs/lib/model/estimate-control/index.d.ts.map +1 -0
- package/cjs/lib/model/estimate-control/index.js +11 -0
- package/cjs/lib/model/estimate-control/index.js.map +1 -0
- package/cjs/lib/model/firebase-document-meta.interface.d.ts +6 -0
- package/cjs/lib/model/firebase-document-meta.interface.d.ts.map +1 -0
- package/cjs/lib/model/firebase-document-meta.interface.js +3 -0
- package/cjs/lib/model/firebase-document-meta.interface.js.map +1 -0
- package/cjs/lib/model/index.d.ts +3 -0
- package/cjs/lib/model/index.d.ts.map +1 -1
- package/cjs/lib/model/index.js +3 -0
- package/cjs/lib/model/index.js.map +1 -1
- package/cjs/lib/model/report-invocation.d.ts +40 -0
- package/cjs/lib/model/report-invocation.d.ts.map +1 -0
- package/cjs/lib/model/report-invocation.js +3 -0
- package/cjs/lib/model/report-invocation.js.map +1 -0
- package/cjs/lib/utils/get-value-by-path.d.ts +5 -16
- package/cjs/lib/utils/get-value-by-path.d.ts.map +1 -1
- package/cjs/lib/utils/get-value-by-path.js +48 -59
- package/cjs/lib/utils/get-value-by-path.js.map +1 -1
- package/cjs/lib/utils/index.d.ts +8 -0
- package/cjs/lib/utils/index.d.ts.map +1 -0
- package/cjs/lib/utils/index.js +11 -0
- package/cjs/lib/utils/index.js.map +1 -0
- package/cjs/lib/utils/is-array-string.d.ts +3 -0
- package/cjs/lib/utils/is-array-string.d.ts.map +1 -1
- package/cjs/lib/utils/is-array-string.js +3 -0
- package/cjs/lib/utils/is-array-string.js.map +1 -1
- package/cjs/lib/utils/is-numeric.d.ts +2 -0
- package/cjs/lib/utils/is-numeric.d.ts.map +1 -0
- package/cjs/lib/utils/is-numeric.js +10 -0
- package/cjs/lib/utils/is-numeric.js.map +1 -0
- package/cjs/lib/utils/is-plain-object.d.ts +5 -0
- package/cjs/lib/utils/is-plain-object.d.ts.map +1 -0
- package/cjs/lib/utils/is-plain-object.js +10 -0
- package/cjs/lib/utils/is-plain-object.js.map +1 -0
- package/cjs/lib/utils/try-json.d.ts +6 -0
- package/cjs/lib/utils/try-json.d.ts.map +1 -0
- package/cjs/lib/utils/try-json.js +20 -0
- package/cjs/lib/utils/try-json.js.map +1 -0
- package/cjs/public-api.d.ts +1 -0
- package/cjs/public-api.d.ts.map +1 -1
- package/cjs/public-api.js +1 -0
- package/cjs/public-api.js.map +1 -1
- package/fesm2022/ironcode-vas-lib.mjs +831 -390
- package/fesm2022/ironcode-vas-lib.mjs.map +1 -1
- package/lib/control-value/vas-camera-control-value.model.d.ts +6 -2
- package/lib/entity/vas-base.model.d.ts +1 -1
- package/lib/entity/vas-control.model.d.ts +4 -4
- package/lib/entity/vas-field.dto.d.ts +4 -7
- package/lib/entity/vas-field.model.d.ts +4 -4
- package/lib/entity/vas-form.model.d.ts +4 -4
- package/lib/entity/vas-group.model.d.ts +2 -2
- package/lib/entity/vas-job-copy.d.ts +12 -12
- package/lib/entity/vas-job.dto.d.ts +12 -6
- package/lib/entity/vas-job.model.d.ts +34 -142
- package/lib/model/estimate-control/estimate-control-value.dto.d.ts +13 -0
- package/lib/model/estimate-control/estimate-control-value.model.d.ts +10 -0
- package/lib/model/estimate-control/estimate-item.dto.d.ts +15 -0
- package/lib/model/estimate-control/estimate-item.model.d.ts +23 -0
- package/lib/model/estimate-control/estimate.dto.d.ts +36 -0
- package/lib/model/estimate-control/estimate.model.d.ts +54 -0
- package/lib/model/estimate-control/estimate.types.d.ts +2 -0
- package/lib/model/estimate-control/index.d.ts +7 -0
- package/lib/model/firebase-document-meta.interface.d.ts +5 -0
- package/lib/model/index.d.ts +3 -0
- package/lib/model/report-invocation.d.ts +39 -0
- package/lib/utils/get-value-by-path.d.ts +5 -16
- package/lib/utils/index.d.ts +7 -0
- package/lib/utils/is-array-string.d.ts +3 -0
- package/lib/utils/is-numeric.d.ts +1 -0
- package/lib/utils/is-plain-object.d.ts +4 -0
- package/lib/utils/omit.d.ts +7 -0
- package/lib/utils/try-json.d.ts +5 -0
- package/package.json +3 -2
- package/public-api.d.ts +1 -0
- package/cjs/lib/entity/vas-job.model.spec-data.d.ts +0 -2
- package/cjs/lib/entity/vas-job.model.spec-data.d.ts.map +0 -1
- package/cjs/lib/entity/vas-job.model.spec-data.js +0 -29
- package/cjs/lib/entity/vas-job.model.spec-data.js.map +0 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { UUID } from 'angular2-uuid';
|
|
2
2
|
import moment from 'moment';
|
|
3
|
+
import { Big } from 'big.js';
|
|
4
|
+
import { v4 } from 'uuid';
|
|
3
5
|
|
|
4
6
|
function isCameraControlValueV1(object) {
|
|
5
7
|
return object instanceof Object
|
|
@@ -8,6 +10,15 @@ function isCameraControlValueV1(object) {
|
|
|
8
10
|
&& 'url' in object
|
|
9
11
|
&& 'id' in object;
|
|
10
12
|
}
|
|
13
|
+
function isCameraControlValueV2(object) {
|
|
14
|
+
return object instanceof Object
|
|
15
|
+
&& 'id' in object
|
|
16
|
+
&& 'name' in object
|
|
17
|
+
&& 'fileSize' in object
|
|
18
|
+
&& 'mimeType' in object
|
|
19
|
+
&& 'sequence' in object
|
|
20
|
+
&& 'config' in object;
|
|
21
|
+
}
|
|
11
22
|
|
|
12
23
|
var VasControlConfigDirection;
|
|
13
24
|
(function (VasControlConfigDirection) {
|
|
@@ -1341,7 +1352,7 @@ class VasControlModel extends VasRestrictedAccountObjectModel {
|
|
|
1341
1352
|
}
|
|
1342
1353
|
/**
|
|
1343
1354
|
* @param {any} value the value to be parsed
|
|
1344
|
-
* @return {
|
|
1355
|
+
* @return {FieldValueType} the parsed value
|
|
1345
1356
|
*/
|
|
1346
1357
|
parseMaybeJson(value) {
|
|
1347
1358
|
if (typeof value !== 'string') {
|
|
@@ -1528,7 +1539,7 @@ class VasFieldModel extends VasJobDataModel {
|
|
|
1528
1539
|
* @return {VasFieldModel}
|
|
1529
1540
|
*/
|
|
1530
1541
|
static fromDto(fieldDto) {
|
|
1531
|
-
return new VasFieldModel(fieldDto.id || '', fieldDto.created || '', fieldDto.serverCreated || '', fieldDto.createdBy || '', fieldDto.modified || '', fieldDto.serverModified || '', fieldDto.modifiedBy || '', fieldDto.createdByName || '', fieldDto.modifiedByName || '', fieldDto.account || '', fieldDto.job || '', fieldDto.control || '', fieldDto.fieldJobPointers || [], fieldDto.value
|
|
1542
|
+
return new VasFieldModel(fieldDto.id || '', fieldDto.created || '', fieldDto.serverCreated || '', fieldDto.createdBy || '', fieldDto.modified || '', fieldDto.serverModified || '', fieldDto.modifiedBy || '', fieldDto.createdByName || '', fieldDto.modifiedByName || '', fieldDto.account || '', fieldDto.job || '', fieldDto.control || '', fieldDto.fieldJobPointers || [], fieldDto.value !== undefined ? fieldDto.value : null, fieldDto.version || 0, fieldDto.controlName || '', fieldDto.controlTitle || '', fieldDto.controlTypeName || '');
|
|
1532
1543
|
}
|
|
1533
1544
|
/**
|
|
1534
1545
|
* @return {VasFieldDto}
|
|
@@ -2250,7 +2261,7 @@ class VasFormModel extends VasRestrictedAccountObjectModel {
|
|
|
2250
2261
|
}
|
|
2251
2262
|
/**
|
|
2252
2263
|
* @param {VasJobModel} jobModel
|
|
2253
|
-
* @return {Array<{id: string, value:
|
|
2264
|
+
* @return {Array<{id: string, value: FieldValueType}>}
|
|
2254
2265
|
*/
|
|
2255
2266
|
getValuesByControlId(jobModel) {
|
|
2256
2267
|
const items = [];
|
|
@@ -3249,69 +3260,541 @@ class VasJobTypeModel extends VasAccountObjectModel {
|
|
|
3249
3260
|
}
|
|
3250
3261
|
}
|
|
3251
3262
|
|
|
3263
|
+
function isNumeric(value) {
|
|
3264
|
+
if (typeof value === 'number') {
|
|
3265
|
+
return !isNaN(value);
|
|
3266
|
+
}
|
|
3267
|
+
return /^-?\d+$/.test(value.trim());
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
function coalescePath(pathOrPaths) {
|
|
3271
|
+
let keys = [];
|
|
3272
|
+
if (typeof pathOrPaths === 'string') {
|
|
3273
|
+
keys = pathOrPaths.split('.');
|
|
3274
|
+
}
|
|
3275
|
+
else if (typeof pathOrPaths === 'number') {
|
|
3276
|
+
keys = [pathOrPaths];
|
|
3277
|
+
}
|
|
3278
|
+
else if (Array.isArray(pathOrPaths)) {
|
|
3279
|
+
keys = pathOrPaths;
|
|
3280
|
+
}
|
|
3281
|
+
else {
|
|
3282
|
+
throw Error('Expected string, number or array of strings or numbers');
|
|
3283
|
+
}
|
|
3284
|
+
const result = [];
|
|
3285
|
+
keys.forEach(key => {
|
|
3286
|
+
if (typeof key === 'number') {
|
|
3287
|
+
result.push(key);
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
if (key.endsWith(']')) {
|
|
3291
|
+
const match = /^([\w_#$-]+)\[([\w_#$-]+)]$/.exec(key.toString());
|
|
3292
|
+
if (match !== null) {
|
|
3293
|
+
result.push(match[1]);
|
|
3294
|
+
result.push(isNumeric(match[2]) ? parseInt(match[2], 10) : match[2]);
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
result.push(key);
|
|
3299
|
+
});
|
|
3300
|
+
return result;
|
|
3301
|
+
}
|
|
3252
3302
|
/**
|
|
3253
|
-
*
|
|
3254
|
-
*
|
|
3255
|
-
* getValueByPath(
|
|
3256
|
-
* ['foo', 'bar'],
|
|
3257
|
-
* {
|
|
3258
|
-
* foo: {
|
|
3259
|
-
* bar: 'value'
|
|
3260
|
-
* }
|
|
3261
|
-
* }
|
|
3262
|
-
* );
|
|
3303
|
+
* Get a value from an object by path.
|
|
3263
3304
|
*
|
|
3264
|
-
*
|
|
3265
|
-
*
|
|
3266
|
-
*
|
|
3267
|
-
* @return {T | undefined}
|
|
3305
|
+
* Path can be an array of strings or numbers.
|
|
3306
|
+
* Path can be a string using dot notation, which can include square brackets to
|
|
3307
|
+
* access array positions e.g. foo.bar[0].baz
|
|
3268
3308
|
*/
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3309
|
+
function getValueByPath(path, object) {
|
|
3310
|
+
const p = coalescePath(path);
|
|
3311
|
+
let scope = object === undefined ? null : object;
|
|
3312
|
+
let index = 0;
|
|
3313
|
+
const length = p.length;
|
|
3314
|
+
while (index < length) {
|
|
3315
|
+
if (scope === undefined || scope === null) {
|
|
3316
|
+
return undefined;
|
|
3273
3317
|
}
|
|
3274
|
-
|
|
3275
|
-
|
|
3318
|
+
scope = scope[p[index++]];
|
|
3319
|
+
}
|
|
3320
|
+
return scope;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
const b$2 = (value = '0') => {
|
|
3324
|
+
try {
|
|
3325
|
+
if (typeof value === 'string') {
|
|
3326
|
+
return new Big(value.replace(/[^\d^.]/g, ''));
|
|
3276
3327
|
}
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3328
|
+
return new Big(value);
|
|
3329
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3330
|
+
}
|
|
3331
|
+
catch (e) {
|
|
3332
|
+
console.error(`failed to construct Big from value '${value}'`);
|
|
3333
|
+
return new Big('0');
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
class EstimateItemModel {
|
|
3337
|
+
id;
|
|
3338
|
+
description;
|
|
3339
|
+
type;
|
|
3340
|
+
quantity;
|
|
3341
|
+
quantityAdjusted;
|
|
3342
|
+
units;
|
|
3343
|
+
unitsAdjusted;
|
|
3344
|
+
price;
|
|
3345
|
+
priceAdjusted;
|
|
3346
|
+
workCategory;
|
|
3347
|
+
costCategory;
|
|
3348
|
+
comment;
|
|
3349
|
+
constructor(id, description, type, quantity, quantityAdjusted, units, unitsAdjusted, price, priceAdjusted, workCategory, costCategory, comment) {
|
|
3350
|
+
this.id = id;
|
|
3351
|
+
this.description = description;
|
|
3352
|
+
this.type = type;
|
|
3353
|
+
this.quantity = quantity;
|
|
3354
|
+
this.quantityAdjusted = quantityAdjusted;
|
|
3355
|
+
this.units = units;
|
|
3356
|
+
this.unitsAdjusted = unitsAdjusted;
|
|
3357
|
+
this.price = price;
|
|
3358
|
+
this.priceAdjusted = priceAdjusted;
|
|
3359
|
+
this.workCategory = workCategory;
|
|
3360
|
+
this.costCategory = costCategory;
|
|
3361
|
+
this.comment = comment;
|
|
3362
|
+
}
|
|
3363
|
+
getTotalUnits(adjusted = false) {
|
|
3364
|
+
return adjusted ?
|
|
3365
|
+
b$2(this.quantityAdjusted || '0').mul(this.unitsAdjusted || '0').toFixed(2) :
|
|
3366
|
+
b$2(this.quantity || '0').mul(this.units || '0').toFixed(2);
|
|
3367
|
+
}
|
|
3368
|
+
getLineValue(labourRate = '1', timeBasis = '1', adjusted = false) {
|
|
3369
|
+
const cat = this.costCategory || 'none';
|
|
3370
|
+
if (this.workCategory === 'removed') {
|
|
3371
|
+
return '0';
|
|
3283
3372
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3373
|
+
const units = (adjusted ? this.unitsAdjusted : this.units) || 0;
|
|
3374
|
+
const quantity = (adjusted ? this.quantityAdjusted : this.quantity) || 0;
|
|
3375
|
+
const price = (adjusted ? this.priceAdjusted : this.price) || '';
|
|
3376
|
+
try {
|
|
3377
|
+
switch (cat) {
|
|
3378
|
+
case 'labour': {
|
|
3379
|
+
return price != '' ?
|
|
3380
|
+
price :
|
|
3381
|
+
b$2(quantity || '0')
|
|
3382
|
+
.mul(units || '0')
|
|
3383
|
+
.mul(labourRate || '0')
|
|
3384
|
+
.div(timeBasis)
|
|
3385
|
+
.toFixed(2);
|
|
3386
|
+
}
|
|
3387
|
+
case 'pm': {
|
|
3388
|
+
return b$2(price || '0')
|
|
3389
|
+
.mul(quantity || '0')
|
|
3390
|
+
.toFixed(2);
|
|
3391
|
+
}
|
|
3392
|
+
case 'parts': {
|
|
3393
|
+
return b$2(price || '0')
|
|
3394
|
+
.mul(quantity || '0')
|
|
3395
|
+
.toFixed(2);
|
|
3396
|
+
}
|
|
3397
|
+
case 'specialist': {
|
|
3398
|
+
return b$2(price || '0')
|
|
3399
|
+
.mul(quantity || '0')
|
|
3400
|
+
.toFixed(2);
|
|
3401
|
+
}
|
|
3402
|
+
default: {
|
|
3403
|
+
return '0';
|
|
3404
|
+
}
|
|
3292
3405
|
}
|
|
3293
|
-
return val1[path[2]];
|
|
3294
3406
|
}
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
if (!val0) {
|
|
3298
|
-
return undefined;
|
|
3299
|
-
}
|
|
3300
|
-
const val1 = val0[path[1]];
|
|
3301
|
-
if (!val1) {
|
|
3302
|
-
return undefined;
|
|
3303
|
-
}
|
|
3304
|
-
const val2 = val1[path[2]];
|
|
3305
|
-
if (!val2) {
|
|
3306
|
-
return undefined;
|
|
3307
|
-
}
|
|
3308
|
-
return val2[path[3]];
|
|
3407
|
+
catch (e) {
|
|
3408
|
+
console.warn(e);
|
|
3309
3409
|
}
|
|
3310
|
-
|
|
3311
|
-
|
|
3410
|
+
return '0';
|
|
3411
|
+
}
|
|
3412
|
+
toDto() {
|
|
3413
|
+
return {
|
|
3414
|
+
id: this.id,
|
|
3415
|
+
description: this.description,
|
|
3416
|
+
type: this.type,
|
|
3417
|
+
quantity: this.quantity,
|
|
3418
|
+
quantityAdjusted: this.quantityAdjusted,
|
|
3419
|
+
units: this.units,
|
|
3420
|
+
unitsAdjusted: this.unitsAdjusted,
|
|
3421
|
+
price: this.price,
|
|
3422
|
+
priceAdjusted: this.priceAdjusted,
|
|
3423
|
+
workCategory: this.workCategory,
|
|
3424
|
+
costCategory: this.costCategory,
|
|
3425
|
+
comment: this.comment
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
static create() {
|
|
3429
|
+
return new EstimateItemModel(v4(), '', '', 1, 1, 0, 0, '', '', 'none', 'none', '');
|
|
3430
|
+
}
|
|
3431
|
+
static fromDto(dto) {
|
|
3432
|
+
return new EstimateItemModel(dto.id || v4(), dto.description || '', dto.type || '', dto.quantity || 0, dto.quantityAdjusted || 0, dto.units || 0, dto.unitsAdjusted || 0, dto.price || '', dto.priceAdjusted || '', dto.workCategory || 'none', dto.costCategory || 'none', dto.comment || '');
|
|
3433
|
+
}
|
|
3434
|
+
static equal(a, b) {
|
|
3435
|
+
return (a?.id === b?.id &&
|
|
3436
|
+
a?.description === b?.description &&
|
|
3437
|
+
a?.type === b?.type &&
|
|
3438
|
+
a?.quantity === b?.quantity &&
|
|
3439
|
+
a?.quantityAdjusted === b?.quantityAdjusted &&
|
|
3440
|
+
a?.units === b?.units &&
|
|
3441
|
+
a?.unitsAdjusted === b?.unitsAdjusted &&
|
|
3442
|
+
a?.price === b?.price &&
|
|
3443
|
+
a?.priceAdjusted === b?.priceAdjusted &&
|
|
3444
|
+
a?.workCategory === b?.workCategory &&
|
|
3445
|
+
a?.costCategory === b?.costCategory &&
|
|
3446
|
+
a?.comment === b?.comment);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
const b$1 = (value = '0') => {
|
|
3451
|
+
try {
|
|
3452
|
+
if (typeof value === 'string') {
|
|
3453
|
+
return new Big(value.replace(/[^\d^.]/g, ''));
|
|
3312
3454
|
}
|
|
3455
|
+
return new Big(value);
|
|
3456
|
+
}
|
|
3457
|
+
catch (e) {
|
|
3458
|
+
console.error(`failed to construct Big from value '${value}'`);
|
|
3459
|
+
return new Big('0');
|
|
3313
3460
|
}
|
|
3314
3461
|
};
|
|
3462
|
+
function isLabourWorkCategory(value) {
|
|
3463
|
+
return ['check', 'rr', 'repair', 'removed'].includes(value);
|
|
3464
|
+
}
|
|
3465
|
+
function isPaintWorkCategory(value) {
|
|
3466
|
+
return ['blend', 'paint'].includes(value);
|
|
3467
|
+
}
|
|
3468
|
+
class EstimateModel {
|
|
3469
|
+
id = v4();
|
|
3470
|
+
source = 'manual';
|
|
3471
|
+
label = '';
|
|
3472
|
+
comment = '';
|
|
3473
|
+
timestamp = moment().toISOString();
|
|
3474
|
+
version = 1;
|
|
3475
|
+
net = '0.00';
|
|
3476
|
+
vat = '0.00';
|
|
3477
|
+
gross = '0.00';
|
|
3478
|
+
labourRate = '1';
|
|
3479
|
+
vatRate = '0.00';
|
|
3480
|
+
sundryRate = '0.00';
|
|
3481
|
+
sundryMax = '0.00';
|
|
3482
|
+
sundryFixed = '0.00';
|
|
3483
|
+
sundryMethod = 'fixed';
|
|
3484
|
+
partsDiscount = '0.00';
|
|
3485
|
+
paintDiscount = '0.00';
|
|
3486
|
+
timeBasis = '1';
|
|
3487
|
+
adjusted = false;
|
|
3488
|
+
items = [];
|
|
3489
|
+
summaryNet = {
|
|
3490
|
+
labour: '0.00',
|
|
3491
|
+
none: '0.00',
|
|
3492
|
+
parts: '0.00',
|
|
3493
|
+
pm: '0.00',
|
|
3494
|
+
pl: '0.00',
|
|
3495
|
+
specialist: '0.00',
|
|
3496
|
+
other: '0.00',
|
|
3497
|
+
gross: '0.00',
|
|
3498
|
+
net: '0.00',
|
|
3499
|
+
hours: '0.00',
|
|
3500
|
+
vat: '0.00',
|
|
3501
|
+
labourRate: '0.00',
|
|
3502
|
+
};
|
|
3503
|
+
summary = {
|
|
3504
|
+
labour: '0.00',
|
|
3505
|
+
none: '0.00',
|
|
3506
|
+
parts: '0.00',
|
|
3507
|
+
pm: '0.00',
|
|
3508
|
+
pl: '0.00',
|
|
3509
|
+
specialist: '0.00',
|
|
3510
|
+
other: '0.00',
|
|
3511
|
+
gross: '0.00',
|
|
3512
|
+
net: '0.00',
|
|
3513
|
+
hours: '0.00',
|
|
3514
|
+
vat: '0.00',
|
|
3515
|
+
labourRate: '0.00',
|
|
3516
|
+
};
|
|
3517
|
+
summaryAdjusted = {
|
|
3518
|
+
labour: '0.00',
|
|
3519
|
+
none: '0.00',
|
|
3520
|
+
parts: '0.00',
|
|
3521
|
+
pm: '0.00',
|
|
3522
|
+
pl: '0.00',
|
|
3523
|
+
specialist: '0.00',
|
|
3524
|
+
other: '0.00',
|
|
3525
|
+
gross: '0.00',
|
|
3526
|
+
net: '0.00',
|
|
3527
|
+
hours: '0.00',
|
|
3528
|
+
vat: '0.00',
|
|
3529
|
+
labourRate: '0.00',
|
|
3530
|
+
};
|
|
3531
|
+
get timeBasisSafe() {
|
|
3532
|
+
return (this.timeBasis || '1') === '0' ? '1' : this.timeBasis;
|
|
3533
|
+
}
|
|
3534
|
+
static create(source = 'manual') {
|
|
3535
|
+
const model = new EstimateModel();
|
|
3536
|
+
model.source = source;
|
|
3537
|
+
return model;
|
|
3538
|
+
}
|
|
3539
|
+
static fromDto(dto) {
|
|
3540
|
+
const model = new this();
|
|
3541
|
+
Object.assign(model, {
|
|
3542
|
+
...dto,
|
|
3543
|
+
items: dto.items?.map(item => EstimateItemModel.fromDto(item)),
|
|
3544
|
+
});
|
|
3545
|
+
model.calculate();
|
|
3546
|
+
return model;
|
|
3547
|
+
}
|
|
3548
|
+
addItem(modelOrDto, opts) {
|
|
3549
|
+
this.items.push(modelOrDto instanceof EstimateItemModel ?
|
|
3550
|
+
modelOrDto :
|
|
3551
|
+
EstimateItemModel.fromDto(modelOrDto));
|
|
3552
|
+
if (!opts || (opts && opts.calculate)) {
|
|
3553
|
+
this.calculate();
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
addItems(items, opts) {
|
|
3557
|
+
this.items = this.items.concat(items);
|
|
3558
|
+
if (!opts || (opts && opts.calculate)) {
|
|
3559
|
+
this.calculate();
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
calculate() {
|
|
3563
|
+
this.summaryNet = this.getSummaryNet(false);
|
|
3564
|
+
if (this.adjusted) {
|
|
3565
|
+
this.summaryAdjusted = this.getSummary(this.summaryNet, this.sundryRate, this.sundryMax, this.sundryFixed, this.sundryMethod, this.partsDiscount, this.paintDiscount, this.adjusted);
|
|
3566
|
+
}
|
|
3567
|
+
this.summary = this.getSummary(this.summaryNet, this.sundryRate, this.sundryMax, this.sundryFixed, this.sundryMethod, this.partsDiscount, this.paintDiscount, this.adjusted);
|
|
3568
|
+
this.net = b$1(0)
|
|
3569
|
+
.add(this.summary.labour)
|
|
3570
|
+
.add(this.summary.none)
|
|
3571
|
+
.add(this.summary.parts)
|
|
3572
|
+
.add(this.summary.pm)
|
|
3573
|
+
.add(this.summary.specialist)
|
|
3574
|
+
.toFixed(2);
|
|
3575
|
+
this.vat = b$1(this.net || '0').mul(this.vatRate || '0').toFixed(2);
|
|
3576
|
+
this.gross = b$1(this.net || '0').add(this.vat || '0').toFixed(2);
|
|
3577
|
+
}
|
|
3578
|
+
/**
|
|
3579
|
+
* Returns the net summary (before sundries and discounts) of the estimate.
|
|
3580
|
+
* By summary, we mean grouping the costs by cost category.
|
|
3581
|
+
*
|
|
3582
|
+
* @param adjusted if the values should be calculated using the original or
|
|
3583
|
+
* adjusted values
|
|
3584
|
+
*/
|
|
3585
|
+
getSummaryNet(adjusted = false) {
|
|
3586
|
+
const summary = this.items.reduce((map, item) => {
|
|
3587
|
+
map[item.costCategory] = b$1(map[item.costCategory] || '0')
|
|
3588
|
+
.add(item.getLineValue(this.labourRate, this.timeBasisSafe, adjusted))
|
|
3589
|
+
.toFixed(2);
|
|
3590
|
+
return map;
|
|
3591
|
+
}, {
|
|
3592
|
+
labour: '0.00',
|
|
3593
|
+
none: '0.00',
|
|
3594
|
+
parts: '0.00',
|
|
3595
|
+
pm: '0.00',
|
|
3596
|
+
pl: '0.00',
|
|
3597
|
+
specialist: '0.00',
|
|
3598
|
+
other: '0.00',
|
|
3599
|
+
net: '0.00',
|
|
3600
|
+
vat: '0.00',
|
|
3601
|
+
gross: '0.00',
|
|
3602
|
+
hours: '0.00',
|
|
3603
|
+
labourRate: '0',
|
|
3604
|
+
});
|
|
3605
|
+
if (this.source === 'audatex') {
|
|
3606
|
+
const mechHours = this.items
|
|
3607
|
+
.reduce((h, item) => {
|
|
3608
|
+
return isLabourWorkCategory(item.workCategory) ?
|
|
3609
|
+
h.add(b$1(item.quantity || '0').mul(item.units || '0')) :
|
|
3610
|
+
h;
|
|
3611
|
+
}, b$1())
|
|
3612
|
+
.div(this.timeBasisSafe || '1')
|
|
3613
|
+
.round(2);
|
|
3614
|
+
const paintHours = this.items
|
|
3615
|
+
.reduce((h, item) => {
|
|
3616
|
+
return isPaintWorkCategory(item.workCategory) ?
|
|
3617
|
+
h.add(b$1(item.quantity || '0').mul(item.units || '0')) :
|
|
3618
|
+
h;
|
|
3619
|
+
}, b$1())
|
|
3620
|
+
.div(this.timeBasisSafe || '1')
|
|
3621
|
+
.round(1);
|
|
3622
|
+
const mechCost = mechHours.mul(this.labourRate || '0');
|
|
3623
|
+
const paintCost = paintHours.mul(this.labourRate || '0');
|
|
3624
|
+
summary.labour = mechCost.add(paintCost).toFixed(2);
|
|
3625
|
+
}
|
|
3626
|
+
return summary;
|
|
3627
|
+
}
|
|
3628
|
+
/**
|
|
3629
|
+
* Applies the sundry and discount values to the net summary.
|
|
3630
|
+
*/
|
|
3631
|
+
getSummary(summaryNet, sundryRate, sundryMax, sundryFixed, sundryMethod, partsDiscount, paintDiscount, adjusted) {
|
|
3632
|
+
const partsDisc = b$1('1').minus(partsDiscount || '0');
|
|
3633
|
+
const paintDisc = b$1('1').minus(paintDiscount || '0');
|
|
3634
|
+
let partsSundry = '0.00';
|
|
3635
|
+
if (sundryMethod === 'percentage') {
|
|
3636
|
+
partsSundry = b$1(summaryNet.parts).mul(sundryRate || '1').toFixed(2);
|
|
3637
|
+
}
|
|
3638
|
+
else if (sundryMethod === 'percentage-with-max') {
|
|
3639
|
+
partsSundry = b$1(summaryNet.parts).mul(sundryRate || '1').toFixed(2);
|
|
3640
|
+
partsSundry = Math.min(parseFloat(sundryMax || '0'), parseFloat(partsSundry)).toFixed(2);
|
|
3641
|
+
}
|
|
3642
|
+
else if (sundryMethod === 'fixed') {
|
|
3643
|
+
partsSundry = b$1(sundryFixed).toFixed(2);
|
|
3644
|
+
}
|
|
3645
|
+
const parts = b$1(summaryNet.parts).add(partsSundry);
|
|
3646
|
+
const net = b$1(0)
|
|
3647
|
+
.add(summaryNet.labour)
|
|
3648
|
+
.add(summaryNet.none)
|
|
3649
|
+
.add(parts)
|
|
3650
|
+
.add(summaryNet.pm)
|
|
3651
|
+
.add(summaryNet.specialist)
|
|
3652
|
+
.toFixed(2);
|
|
3653
|
+
const vat = b$1(net || '0')
|
|
3654
|
+
.mul(this.vatRate || '0')
|
|
3655
|
+
.toFixed(2);
|
|
3656
|
+
const gross = b$1(net || '0').add(vat || '0').toFixed(2);
|
|
3657
|
+
return {
|
|
3658
|
+
labour: summaryNet.labour,
|
|
3659
|
+
none: summaryNet.none,
|
|
3660
|
+
parts: b$1(parts).mul(partsDisc).toFixed(2),
|
|
3661
|
+
pm: b$1(summaryNet.pm).mul(paintDisc).toFixed(2),
|
|
3662
|
+
pl: summaryNet.pl,
|
|
3663
|
+
specialist: summaryNet.specialist,
|
|
3664
|
+
other: summaryNet.other,
|
|
3665
|
+
net: net,
|
|
3666
|
+
vat: vat,
|
|
3667
|
+
gross: gross,
|
|
3668
|
+
labourRate: this.labourRate,
|
|
3669
|
+
hours: this.getTotalHours(adjusted)
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
toDto() {
|
|
3673
|
+
return {
|
|
3674
|
+
id: this.id,
|
|
3675
|
+
source: this.source,
|
|
3676
|
+
label: this.label,
|
|
3677
|
+
comment: this.comment,
|
|
3678
|
+
timestamp: this.timestamp,
|
|
3679
|
+
version: this.version,
|
|
3680
|
+
net: this.net,
|
|
3681
|
+
vat: this.vat,
|
|
3682
|
+
gross: this.gross,
|
|
3683
|
+
labourRate: this.labourRate,
|
|
3684
|
+
vatRate: this.vatRate,
|
|
3685
|
+
sundryRate: this.sundryRate,
|
|
3686
|
+
sundryMax: this.sundryMax,
|
|
3687
|
+
sundryFixed: this.sundryFixed,
|
|
3688
|
+
sundryMethod: this.sundryMethod,
|
|
3689
|
+
partsDiscount: this.partsDiscount,
|
|
3690
|
+
paintDiscount: this.paintDiscount,
|
|
3691
|
+
timeBasis: this.timeBasisSafe,
|
|
3692
|
+
items: this.items.map(i => i.toDto()),
|
|
3693
|
+
adjusted: this.adjusted,
|
|
3694
|
+
summary: this.summary,
|
|
3695
|
+
summaryAdjusted: this.summaryAdjusted
|
|
3696
|
+
};
|
|
3697
|
+
}
|
|
3698
|
+
getTotalHours(adjusted = false) {
|
|
3699
|
+
if (this.source === 'audatex') {
|
|
3700
|
+
const mechUnitsHours = this.items
|
|
3701
|
+
.filter(item => item.workCategory !== 'paint')
|
|
3702
|
+
.reduce((h, item) => h.add(b$1(item.getTotalUnits(adjusted))), b$1())
|
|
3703
|
+
.round(2)
|
|
3704
|
+
.div(this.timeBasisSafe);
|
|
3705
|
+
const paintHours = this.items
|
|
3706
|
+
.filter(item => item.workCategory === 'paint')
|
|
3707
|
+
.reduce((h, item) => h.add(b$1(item.getTotalUnits(adjusted))), b$1())
|
|
3708
|
+
.round(1)
|
|
3709
|
+
.div(this.timeBasisSafe);
|
|
3710
|
+
return mechUnitsHours.add(paintHours).toFixed(2);
|
|
3711
|
+
}
|
|
3712
|
+
const units = this.items
|
|
3713
|
+
.filter(i => i.costCategory === 'labour')
|
|
3714
|
+
.reduce((p, c) => p.add(b$1(c.getTotalUnits(adjusted))), b$1('0'))
|
|
3715
|
+
.toFixed(2);
|
|
3716
|
+
return b$1(units).div(this.timeBasisSafe).toFixed(2);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
const b = (value = '0') => new Big(value);
|
|
3721
|
+
/**
|
|
3722
|
+
* Utility class for computing the summary value for an estimate control given
|
|
3723
|
+
* the first and last estimates
|
|
3724
|
+
*/
|
|
3725
|
+
class EstimateControlValueModel {
|
|
3726
|
+
static calculateSummary(original, latest) {
|
|
3727
|
+
const o = original.summary;
|
|
3728
|
+
const l = latest.adjusted ?
|
|
3729
|
+
latest.summaryAdjusted :
|
|
3730
|
+
latest.summary;
|
|
3731
|
+
const s = {
|
|
3732
|
+
labour: b(o.labour).minus(l.labour).toFixed(2),
|
|
3733
|
+
none: b(o.none).minus(l.none).toFixed(2),
|
|
3734
|
+
parts: b(o.parts).minus(l.parts).toFixed(2),
|
|
3735
|
+
pm: b(o.pm).minus(l.pm).toFixed(2),
|
|
3736
|
+
pl: b(o.pm).minus(l.pl).toFixed(2),
|
|
3737
|
+
specialist: b(o.specialist).minus(l.specialist).toFixed(2),
|
|
3738
|
+
other: b(o.other).minus(l.other).toFixed(2),
|
|
3739
|
+
net: b(o.net).minus(l.net).toFixed(2),
|
|
3740
|
+
vat: b(o.vat).minus(l.vat).toFixed(2),
|
|
3741
|
+
gross: b(o.gross).minus(l.gross).toFixed(2),
|
|
3742
|
+
hours: b(o.hours).minus(l.hours).toFixed(0),
|
|
3743
|
+
labourRate: b(o.labourRate).minus(l.labourRate).toFixed(0)
|
|
3744
|
+
};
|
|
3745
|
+
return {
|
|
3746
|
+
original: o,
|
|
3747
|
+
latest: l,
|
|
3748
|
+
savings: s
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3751
|
+
static getEmptySummary() {
|
|
3752
|
+
return {
|
|
3753
|
+
original: {
|
|
3754
|
+
labour: '0.00',
|
|
3755
|
+
none: '0.00',
|
|
3756
|
+
parts: '0.00',
|
|
3757
|
+
pm: '0.00',
|
|
3758
|
+
pl: '0.00',
|
|
3759
|
+
specialist: '0.00',
|
|
3760
|
+
other: '0.00',
|
|
3761
|
+
net: '0.00',
|
|
3762
|
+
vat: '0.00',
|
|
3763
|
+
gross: '0.00',
|
|
3764
|
+
hours: '0',
|
|
3765
|
+
labourRate: '0.00'
|
|
3766
|
+
},
|
|
3767
|
+
latest: {
|
|
3768
|
+
labour: '0.00',
|
|
3769
|
+
none: '0.00',
|
|
3770
|
+
parts: '0.00',
|
|
3771
|
+
pm: '0.00',
|
|
3772
|
+
pl: '0.00',
|
|
3773
|
+
specialist: '0.00',
|
|
3774
|
+
other: '0.00',
|
|
3775
|
+
net: '0.00',
|
|
3776
|
+
vat: '0.00',
|
|
3777
|
+
gross: '0.00',
|
|
3778
|
+
hours: '0',
|
|
3779
|
+
labourRate: '0.00'
|
|
3780
|
+
},
|
|
3781
|
+
savings: {
|
|
3782
|
+
labour: '0.00',
|
|
3783
|
+
none: '0.00',
|
|
3784
|
+
parts: '0.00',
|
|
3785
|
+
pm: '0.00',
|
|
3786
|
+
pl: '0.00',
|
|
3787
|
+
specialist: '0.00',
|
|
3788
|
+
other: '0.00',
|
|
3789
|
+
net: '0.00',
|
|
3790
|
+
vat: '0.00',
|
|
3791
|
+
gross: '0.00',
|
|
3792
|
+
hours: '0',
|
|
3793
|
+
labourRate: '0.00'
|
|
3794
|
+
}
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3315
3798
|
|
|
3316
3799
|
const getEmptyGeoLocation = () => ({
|
|
3317
3800
|
accuracy: 0,
|
|
@@ -3325,14 +3808,72 @@ const getEmptyGeoLocation = () => ({
|
|
|
3325
3808
|
timestamp: ''
|
|
3326
3809
|
});
|
|
3327
3810
|
|
|
3811
|
+
/**
|
|
3812
|
+
* Return true IFF the array contains only strings
|
|
3813
|
+
*/
|
|
3328
3814
|
const isArrayString = (value) => {
|
|
3329
3815
|
return Array.isArray(value) && value.every(i => typeof i === 'string');
|
|
3330
3816
|
};
|
|
3331
3817
|
|
|
3332
3818
|
/**
|
|
3333
|
-
*
|
|
3819
|
+
* Return true if the argument is a plain object {}, i.e. not an array, null.
|
|
3820
|
+
*/
|
|
3821
|
+
function isPlainObject(value) {
|
|
3822
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
/**
|
|
3826
|
+
* Return an object omitting properties specified by key(s)
|
|
3827
|
+
*
|
|
3828
|
+
* @param obj object to be processed
|
|
3829
|
+
* @param omitKey key or array of keys to be omitted
|
|
3830
|
+
*/
|
|
3831
|
+
const omit = (obj, omitKey) => {
|
|
3832
|
+
return Object.keys(obj).reduce((result, key) => {
|
|
3833
|
+
if (Array.isArray(omitKey)) {
|
|
3834
|
+
if (!omitKey.includes(key)) {
|
|
3835
|
+
result[key] = obj[key];
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
else {
|
|
3839
|
+
if (omitKey !== key) {
|
|
3840
|
+
result[key] = obj[key];
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
return result;
|
|
3844
|
+
}, {});
|
|
3845
|
+
};
|
|
3846
|
+
|
|
3847
|
+
/**
|
|
3848
|
+
* Try to parse the value as JSON if it looks like a JSON encoded string, return
|
|
3849
|
+
* the original value if it's not a JSON string or parsing fails.
|
|
3850
|
+
*/
|
|
3851
|
+
const tryJson = (value) => {
|
|
3852
|
+
if (typeof value === 'string' && ['{', '['].includes(value.substring(0, 1))) {
|
|
3853
|
+
try {
|
|
3854
|
+
return JSON.parse(value);
|
|
3855
|
+
}
|
|
3856
|
+
catch (e) {
|
|
3857
|
+
return value;
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
return value;
|
|
3861
|
+
};
|
|
3862
|
+
|
|
3863
|
+
const nullables = new Set([
|
|
3864
|
+
'created',
|
|
3865
|
+
'modified',
|
|
3866
|
+
'reference',
|
|
3867
|
+
'jobDate',
|
|
3868
|
+
'followUpDate',
|
|
3869
|
+
'followUpStatus',
|
|
3870
|
+
'followUpNote',
|
|
3871
|
+
]);
|
|
3872
|
+
let jobStaticProps;
|
|
3873
|
+
/**
|
|
3874
|
+
* VasJobBaseModel
|
|
3334
3875
|
*/
|
|
3335
|
-
class
|
|
3876
|
+
class VasJobBaseModel extends VasRestrictedAccountObjectModel {
|
|
3336
3877
|
id;
|
|
3337
3878
|
created;
|
|
3338
3879
|
serverCreated;
|
|
@@ -3347,6 +3888,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3347
3888
|
reference;
|
|
3348
3889
|
jobDate;
|
|
3349
3890
|
jobStatus;
|
|
3891
|
+
jobStatusLastUpdate;
|
|
3350
3892
|
jobType;
|
|
3351
3893
|
assigneeId;
|
|
3352
3894
|
formId;
|
|
@@ -3354,8 +3896,9 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3354
3896
|
pendingFields;
|
|
3355
3897
|
childModified;
|
|
3356
3898
|
version;
|
|
3357
|
-
|
|
3358
|
-
|
|
3899
|
+
followUpDate;
|
|
3900
|
+
followUpStatus;
|
|
3901
|
+
followUpNote;
|
|
3359
3902
|
geoLocation;
|
|
3360
3903
|
files;
|
|
3361
3904
|
fields;
|
|
@@ -3364,7 +3907,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3364
3907
|
reports;
|
|
3365
3908
|
tasks;
|
|
3366
3909
|
instruction;
|
|
3367
|
-
constructor(id, created, serverCreated, createdBy, modified, serverModified, modifiedBy, createdByName, modifiedByName, account, accessGroup, reference, jobDate, jobStatus, jobType, assigneeId, formId, timeZoneOffset, pendingFields, childModified, version,
|
|
3910
|
+
constructor(id, created, serverCreated, createdBy, modified, serverModified, modifiedBy, createdByName, modifiedByName, account, accessGroup, reference, jobDate, jobStatus, jobStatusLastUpdate, jobType, assigneeId, formId, timeZoneOffset, pendingFields, childModified, version, followUpDate, followUpStatus, followUpNote, geoLocation, files, fields, jobEmails, notes, reports, tasks, instruction) {
|
|
3368
3911
|
super(id, created, serverCreated, createdBy, modified, serverModified, modifiedBy, createdByName, modifiedByName, account, accessGroup);
|
|
3369
3912
|
this.id = id;
|
|
3370
3913
|
this.created = created;
|
|
@@ -3380,6 +3923,7 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3380
3923
|
this.reference = reference;
|
|
3381
3924
|
this.jobDate = jobDate;
|
|
3382
3925
|
this.jobStatus = jobStatus;
|
|
3926
|
+
this.jobStatusLastUpdate = jobStatusLastUpdate;
|
|
3383
3927
|
this.jobType = jobType;
|
|
3384
3928
|
this.assigneeId = assigneeId;
|
|
3385
3929
|
this.formId = formId;
|
|
@@ -3387,8 +3931,9 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3387
3931
|
this.pendingFields = pendingFields;
|
|
3388
3932
|
this.childModified = childModified;
|
|
3389
3933
|
this.version = version;
|
|
3390
|
-
this.
|
|
3391
|
-
this.
|
|
3934
|
+
this.followUpDate = followUpDate;
|
|
3935
|
+
this.followUpStatus = followUpStatus;
|
|
3936
|
+
this.followUpNote = followUpNote;
|
|
3392
3937
|
this.geoLocation = geoLocation;
|
|
3393
3938
|
this.files = files;
|
|
3394
3939
|
this.fields = fields;
|
|
@@ -3398,102 +3943,21 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3398
3943
|
this.tasks = tasks;
|
|
3399
3944
|
this.instruction = instruction;
|
|
3400
3945
|
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
* @return {VasJobModelDynamicInterface}
|
|
3404
|
-
*/
|
|
3405
|
-
get $this() {
|
|
3406
|
-
return this;
|
|
3946
|
+
get createdByDisplayName() {
|
|
3947
|
+
return this.createdByName;
|
|
3407
3948
|
}
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
* dynamic forms
|
|
3411
|
-
*/
|
|
3412
|
-
get dynamicProperties() {
|
|
3413
|
-
const nativeProps = this.staticProperties;
|
|
3414
|
-
return Object
|
|
3415
|
-
.getOwnPropertyNames(this)
|
|
3416
|
-
.filter(prop => !nativeProps.includes(prop));
|
|
3949
|
+
get modifiedByDisplayName() {
|
|
3950
|
+
return this.modifiedByName;
|
|
3417
3951
|
}
|
|
3418
3952
|
/**
|
|
3419
|
-
*
|
|
3953
|
+
* Return the list of Job properties
|
|
3420
3954
|
*/
|
|
3421
3955
|
get staticProperties() {
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
static empty() {
|
|
3428
|
-
return new VasJobModel('', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', moment().utcOffset(), 0, '', 0, '', '', getEmptyGeoLocation(), [], [], [], [], [], [], null);
|
|
3429
|
-
}
|
|
3430
|
-
static fromDto(dto) {
|
|
3431
|
-
const model = new VasJobModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobType || '', dto.assigneeId || '', dto.formId || dto.lastForm || '', dto.timeZoneOffset || moment().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.createdByDisplayName || '', dto.modifiedByDisplayName || '', dto.geoLocation || getEmptyGeoLocation(), dto.files || [], dto.fields || [], dto.jobEmails || [], dto.notes || [], dto.reports || [], dto.tasks || [], dto.instruction || null);
|
|
3432
|
-
const emptyKeys = model.staticProperties;
|
|
3433
|
-
Object
|
|
3434
|
-
.keys(dto)
|
|
3435
|
-
.filter(key => emptyKeys.indexOf(key) === -1)
|
|
3436
|
-
.forEach((key) => model.$this[key] = dto[key]);
|
|
3437
|
-
return model;
|
|
3438
|
-
}
|
|
3439
|
-
/**
|
|
3440
|
-
* This method will instantiate a new JobModel. The difference with this
|
|
3441
|
-
* method of instantiation is that we are coming from a relation frame i.e.
|
|
3442
|
-
* the job has a list of {@link VasFieldDto} instead of a Job document.
|
|
3443
|
-
*
|
|
3444
|
-
*/
|
|
3445
|
-
static fromRelational(dto, form) {
|
|
3446
|
-
const model = new VasJobModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobType || '', dto.assigneeId || '', dto.formId || '', dto.timeZoneOffset || moment().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.createdByDisplayName || '', dto.modifiedByDisplayName || '', dto.geoLocation || getEmptyGeoLocation(), dto.files || [], dto.fields || [], dto.jobEmails || [], dto.notes || [], dto.reports || [], dto.tasks || [], dto.instruction || null);
|
|
3447
|
-
form.groups
|
|
3448
|
-
.forEach(group => {
|
|
3449
|
-
group.controls
|
|
3450
|
-
.forEach(control => {
|
|
3451
|
-
const field = dto.fields?.find(f => f.control === control.id);
|
|
3452
|
-
if (!field) {
|
|
3453
|
-
return;
|
|
3454
|
-
}
|
|
3455
|
-
model.getGroup(group.name)[control.name] = field.value;
|
|
3456
|
-
});
|
|
3457
|
-
});
|
|
3458
|
-
return model;
|
|
3459
|
-
}
|
|
3460
|
-
/**
|
|
3461
|
-
* @param {VasFormModel} formModel
|
|
3462
|
-
* @return {Record<string, VasFieldDtoValue>}
|
|
3463
|
-
*/
|
|
3464
|
-
getFields(formModel) {
|
|
3465
|
-
const fields = {};
|
|
3466
|
-
formModel.groups
|
|
3467
|
-
.forEach(group => {
|
|
3468
|
-
group.controls.forEach(control => {
|
|
3469
|
-
(fields)[control.reportTemplateName] =
|
|
3470
|
-
this.getValueByPath([group.name, control.name]) || null;
|
|
3471
|
-
});
|
|
3472
|
-
});
|
|
3473
|
-
return fields;
|
|
3474
|
-
}
|
|
3475
|
-
/**
|
|
3476
|
-
* Will return an object whose keys are the names of all fields in the job.
|
|
3477
|
-
* This method is similar to `getFields` except that this method will only
|
|
3478
|
-
* return values where a value was set, as opposed to `getFields` which will
|
|
3479
|
-
* return a value for every control even if no value was set, because it uses
|
|
3480
|
-
* the form to drive the logic. Note also this method will return values for
|
|
3481
|
-
* any Fields in the fields array.
|
|
3482
|
-
* @return {Record<string, VasFieldDtoValue>}
|
|
3483
|
-
*/
|
|
3484
|
-
getFields2() {
|
|
3485
|
-
let fields = {};
|
|
3486
|
-
this.dynamicProperties
|
|
3487
|
-
.forEach((groupName) => {
|
|
3488
|
-
Object
|
|
3489
|
-
.keys(this.$this[groupName])
|
|
3490
|
-
.forEach(controlName => {
|
|
3491
|
-
fields[controlName] =
|
|
3492
|
-
this.getValueByPath([groupName, controlName]) || null;
|
|
3493
|
-
});
|
|
3494
|
-
}, {});
|
|
3495
|
-
this.fields.forEach(field => fields[field.controlName] = field.value);
|
|
3496
|
-
return fields;
|
|
3956
|
+
if (jobStaticProps) {
|
|
3957
|
+
return jobStaticProps;
|
|
3958
|
+
}
|
|
3959
|
+
return jobStaticProps =
|
|
3960
|
+
new Set(Object.getOwnPropertyNames(VasJobDocumentModel.empty()));
|
|
3497
3961
|
}
|
|
3498
3962
|
/**
|
|
3499
3963
|
* Returns an object describing how many attachments were added to this job
|
|
@@ -3503,22 +3967,12 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3503
3967
|
* job was created
|
|
3504
3968
|
*/
|
|
3505
3969
|
getFilesUploadStatus(formModel) {
|
|
3506
|
-
const
|
|
3507
|
-
.
|
|
3508
|
-
.
|
|
3509
|
-
|
|
3510
|
-
if (value && !!value.id) {
|
|
3511
|
-
return path;
|
|
3512
|
-
}
|
|
3513
|
-
return null;
|
|
3514
|
-
})
|
|
3515
|
-
.filter(path => path !== null ? path : null)
|
|
3516
|
-
.filter(isArrayString);
|
|
3517
|
-
let total = pathsWithValue.length;
|
|
3518
|
-
total += this.files.length;
|
|
3970
|
+
const cameraFieldValues = Object
|
|
3971
|
+
.values(this.fields)
|
|
3972
|
+
.filter(value => isCameraControlValueV2(value) || isCameraControlValueV1(value));
|
|
3973
|
+
let total = cameraFieldValues.length + this.files.length;
|
|
3519
3974
|
// calculate the number of camera images still to upload
|
|
3520
|
-
let pending =
|
|
3521
|
-
.map(path => this.getValueByPath(path))
|
|
3975
|
+
let pending = cameraFieldValues
|
|
3522
3976
|
.map((value) => {
|
|
3523
3977
|
if (isFileDto(value)) {
|
|
3524
3978
|
return value.id && value.status !== 'COMPLETE';
|
|
@@ -3531,218 +3985,18 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3531
3985
|
})
|
|
3532
3986
|
.filter(hasUploaded => hasUploaded)
|
|
3533
3987
|
.length;
|
|
3534
|
-
// add
|
|
3535
|
-
pending += this.files
|
|
3536
|
-
.filter(value => value.status !== 'COMPLETE')
|
|
3537
|
-
.length;
|
|
3988
|
+
// add count of job.files that are complete
|
|
3989
|
+
pending += this.files.filter(value => value.status !== 'COMPLETE').length;
|
|
3538
3990
|
return {
|
|
3539
3991
|
pending,
|
|
3540
3992
|
total
|
|
3541
3993
|
};
|
|
3542
3994
|
}
|
|
3543
|
-
/**
|
|
3544
|
-
* This method will return the dynamic property from the JobModel that
|
|
3545
|
-
* represent a group (from a form).
|
|
3546
|
-
*
|
|
3547
|
-
* @param name the name of the group
|
|
3548
|
-
* @param init if true (default) and group is not found, initialise an empty
|
|
3549
|
-
* group, otherwise throw an error
|
|
3550
|
-
*/
|
|
3551
|
-
getGroup(name, init = true) {
|
|
3552
|
-
let prop;
|
|
3553
|
-
if (this.staticProperties.includes(name)) {
|
|
3554
|
-
throw Error(`invalid group name ${name}, not a dynamic property`);
|
|
3555
|
-
}
|
|
3556
|
-
else if (this.$this[name] === undefined) {
|
|
3557
|
-
if (init) {
|
|
3558
|
-
prop = this.$this[name] = {};
|
|
3559
|
-
}
|
|
3560
|
-
else {
|
|
3561
|
-
throw Error(`invalid group name ${name}, not found`);
|
|
3562
|
-
}
|
|
3563
|
-
}
|
|
3564
|
-
else {
|
|
3565
|
-
prop = this.$this[name];
|
|
3566
|
-
if (typeof prop !== 'object') {
|
|
3567
|
-
throw Error(`invalid group name ${name}, not an object`);
|
|
3568
|
-
}
|
|
3569
|
-
}
|
|
3570
|
-
return prop;
|
|
3571
|
-
}
|
|
3572
3995
|
/**
|
|
3573
3996
|
* @param path path segments
|
|
3574
3997
|
*/
|
|
3575
3998
|
getValueByPath(path = []) {
|
|
3576
|
-
return getValueByPath(path, this
|
|
3577
|
-
}
|
|
3578
|
-
/**
|
|
3579
|
-
* In order to understand why we need this method it is important to
|
|
3580
|
-
* understand that within the system, Jobs can be represented in one of two
|
|
3581
|
-
* ways, document and relational.
|
|
3582
|
-
*
|
|
3583
|
-
* The important distinction is how values submitted by a form are stored.
|
|
3584
|
-
*
|
|
3585
|
-
* Jobs stored as documents (JSON objects) will store user values, as dynamic
|
|
3586
|
-
* properties of the document.
|
|
3587
|
-
*
|
|
3588
|
-
* Whereas, Jobs stored as relational, will store user values in an array of
|
|
3589
|
-
* {@link VasFieldDto} objects.
|
|
3590
|
-
*
|
|
3591
|
-
* Depending on where we are in the system, either one of these approaches can
|
|
3592
|
-
* be more useful than the other.
|
|
3593
|
-
*
|
|
3594
|
-
* This method, assumes that the JobModel has been instantiated from a
|
|
3595
|
-
* document representation, and serves to hydrate the fields array. In order
|
|
3596
|
-
* to achieve this, knowledge of the {@link VasFormDto} that created the job
|
|
3597
|
-
* is required.
|
|
3598
|
-
*
|
|
3599
|
-
*
|
|
3600
|
-
* Job in document representation
|
|
3601
|
-
* {
|
|
3602
|
-
* id: <guid>,
|
|
3603
|
-
* reference: "something"
|
|
3604
|
-
* <other static job properties>...
|
|
3605
|
-
* foo: {
|
|
3606
|
-
* bar: "value"
|
|
3607
|
-
* }
|
|
3608
|
-
* }
|
|
3609
|
-
*
|
|
3610
|
-
* Job in relational representation
|
|
3611
|
-
* {
|
|
3612
|
-
* id: <guid>,
|
|
3613
|
-
* reference: "something"
|
|
3614
|
-
* <other static job properties>...
|
|
3615
|
-
* <will not have dynamic properties>...
|
|
3616
|
-
* fields: [
|
|
3617
|
-
* {
|
|
3618
|
-
* id: <guid>,
|
|
3619
|
-
* <other field properties>,
|
|
3620
|
-
* value: "value"
|
|
3621
|
-
* }
|
|
3622
|
-
* ]
|
|
3623
|
-
* }
|
|
3624
|
-
*
|
|
3625
|
-
*
|
|
3626
|
-
* @param formModel the VasFormModel that was used to create the job
|
|
3627
|
-
* @param controlFieldIdMap This is used to determine the id each field.
|
|
3628
|
-
* Either one will be found in the map, or a new one is generated.
|
|
3629
|
-
* @param controlNames if a value is provided, it will be used
|
|
3630
|
-
* to filter the fields that are returned.
|
|
3631
|
-
* @return {Array<VasFieldDto>}
|
|
3632
|
-
*/
|
|
3633
|
-
hydrateFields(formModel, controlFieldIdMap = new Map(), controlNames) {
|
|
3634
|
-
const fields = [];
|
|
3635
|
-
formModel.groups
|
|
3636
|
-
.forEach(group => {
|
|
3637
|
-
group.controls.forEach(control => {
|
|
3638
|
-
if (!control.name.startsWith('fields.')) {
|
|
3639
|
-
return;
|
|
3640
|
-
}
|
|
3641
|
-
if (controlNames && !controlNames.includes(control.name)) {
|
|
3642
|
-
return;
|
|
3643
|
-
}
|
|
3644
|
-
fields.push(VasFieldModel.fromDto({
|
|
3645
|
-
// if the controlId exists in the map, use the associated fieldId,
|
|
3646
|
-
// otherwise generate a new id for the field.
|
|
3647
|
-
id: controlFieldIdMap.get(control.id) || UUID.UUID(),
|
|
3648
|
-
account: this.account,
|
|
3649
|
-
job: this.id,
|
|
3650
|
-
createdBy: this.createdBy,
|
|
3651
|
-
created: this.created,
|
|
3652
|
-
modifiedBy: this.modifiedBy,
|
|
3653
|
-
modified: this.modified,
|
|
3654
|
-
control: control.id,
|
|
3655
|
-
value: this.getValueByPath([group.name, control.name])
|
|
3656
|
-
}));
|
|
3657
|
-
});
|
|
3658
|
-
});
|
|
3659
|
-
this.fields = fields;
|
|
3660
|
-
}
|
|
3661
|
-
/**
|
|
3662
|
-
* A very non sophisticated way to set values in the job via paths
|
|
3663
|
-
*
|
|
3664
|
-
* For example:
|
|
3665
|
-
*
|
|
3666
|
-
* setValueByPath('value', ['foo', 'bar']);
|
|
3667
|
-
*
|
|
3668
|
-
* will make results in job
|
|
3669
|
-
* {
|
|
3670
|
-
* foo: {
|
|
3671
|
-
* bar: 'value'
|
|
3672
|
-
* }
|
|
3673
|
-
* }
|
|
3674
|
-
*
|
|
3675
|
-
* @param value the value to set
|
|
3676
|
-
* @param path path segments
|
|
3677
|
-
*/
|
|
3678
|
-
setValueByPath(value, path = []) {
|
|
3679
|
-
switch (path.length) {
|
|
3680
|
-
case 0: {
|
|
3681
|
-
return;
|
|
3682
|
-
}
|
|
3683
|
-
case 1: {
|
|
3684
|
-
this[path[0]] = value;
|
|
3685
|
-
return;
|
|
3686
|
-
}
|
|
3687
|
-
case 2: {
|
|
3688
|
-
// eslint-disable-next-line max-len
|
|
3689
|
-
this.$this[path[0]][path[1]] = value;
|
|
3690
|
-
return;
|
|
3691
|
-
}
|
|
3692
|
-
case 3: {
|
|
3693
|
-
// eslint-disable-next-line max-len
|
|
3694
|
-
this.$this[path[0]][path[1]][path[2]] = value;
|
|
3695
|
-
return;
|
|
3696
|
-
}
|
|
3697
|
-
default: {
|
|
3698
|
-
throw Error('path has too many segments');
|
|
3699
|
-
}
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
/**
|
|
3703
|
-
* @param staticOnly if true, will only output values for the static
|
|
3704
|
-
* properties in the dto
|
|
3705
|
-
*/
|
|
3706
|
-
toDto(staticOnly = false) {
|
|
3707
|
-
if (staticOnly) {
|
|
3708
|
-
return {
|
|
3709
|
-
id: this.id,
|
|
3710
|
-
created: this.created,
|
|
3711
|
-
createdBy: this.createdBy,
|
|
3712
|
-
modified: this.modified,
|
|
3713
|
-
modifiedBy: this.modifiedBy,
|
|
3714
|
-
serverCreated: this.serverCreated,
|
|
3715
|
-
serverModified: this.serverModified,
|
|
3716
|
-
createdByName: this.createdByName,
|
|
3717
|
-
modifiedByName: this.modifiedByName,
|
|
3718
|
-
account: this.account,
|
|
3719
|
-
accessGroup: this.accessGroup,
|
|
3720
|
-
reference: this.reference,
|
|
3721
|
-
jobDate: this.jobDate,
|
|
3722
|
-
jobStatus: this.jobStatus,
|
|
3723
|
-
jobType: this.jobType,
|
|
3724
|
-
assigneeId: this.assigneeId,
|
|
3725
|
-
formId: this.formId,
|
|
3726
|
-
timeZoneOffset: this.timeZoneOffset,
|
|
3727
|
-
pendingFields: this.pendingFields,
|
|
3728
|
-
childModified: this.childModified,
|
|
3729
|
-
version: this.version,
|
|
3730
|
-
createdByDisplayName: this.createdByDisplayName,
|
|
3731
|
-
modifiedByDisplayName: this.modifiedByDisplayName,
|
|
3732
|
-
geoLocation: this.geoLocation,
|
|
3733
|
-
files: this.files,
|
|
3734
|
-
fields: this.fields,
|
|
3735
|
-
jobEmails: this.jobEmails,
|
|
3736
|
-
notes: this.notes,
|
|
3737
|
-
reports: this.reports,
|
|
3738
|
-
tasks: this.tasks,
|
|
3739
|
-
instruction: this.instruction
|
|
3740
|
-
};
|
|
3741
|
-
}
|
|
3742
|
-
const dto = {};
|
|
3743
|
-
[...this.staticProperties, ...this.dynamicProperties]
|
|
3744
|
-
.forEach(prop => dto[prop] = this.$this[prop]);
|
|
3745
|
-
return dto;
|
|
3999
|
+
return getValueByPath(Array.isArray(path) ? path : path.split('.'), this);
|
|
3746
4000
|
}
|
|
3747
4001
|
/**
|
|
3748
4002
|
* This method will parse a syntax that allows us to build strings from the
|
|
@@ -3776,7 +4030,6 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3776
4030
|
if (!value || typeof value !== 'string') {
|
|
3777
4031
|
return '';
|
|
3778
4032
|
}
|
|
3779
|
-
const fields = this.getFields2();
|
|
3780
4033
|
return value.replace(/({[^}]*})/g, (match, token) => {
|
|
3781
4034
|
const syntax = token.substring(1, token.length - 1);
|
|
3782
4035
|
let [key, filter] = syntax.split('|');
|
|
@@ -3803,23 +4056,10 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3803
4056
|
result = (this.getValueByPath(path) || '').toString();
|
|
3804
4057
|
}
|
|
3805
4058
|
else if (objectKey === 'fields') {
|
|
3806
|
-
result = (getValueByPath(['fields
|
|
4059
|
+
result = (this.getValueByPath(['fields', ...path]) || '').toString();
|
|
3807
4060
|
}
|
|
3808
|
-
else if (objectKey.length) {
|
|
3809
|
-
|
|
3810
|
-
if (options.objects[objectKey] === undefined) {
|
|
3811
|
-
console.debug(`objectKey ${objectKey} is not present in options.object`);
|
|
3812
|
-
result = '';
|
|
3813
|
-
}
|
|
3814
|
-
else {
|
|
3815
|
-
result = (getValueByPath(path, options.objects[objectKey]) || '')
|
|
3816
|
-
.toString();
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
else {
|
|
3820
|
-
console.debug(`objectKey ${objectKey} was used but options.object is not set`);
|
|
3821
|
-
return '';
|
|
3822
|
-
}
|
|
4061
|
+
else if (objectKey.length && options.objects && options.objects[objectKey] !== undefined) {
|
|
4062
|
+
result = (getValueByPath(path, options.objects[objectKey]) || '').toString();
|
|
3823
4063
|
}
|
|
3824
4064
|
else {
|
|
3825
4065
|
return '';
|
|
@@ -3874,8 +4114,209 @@ class VasJobModel extends VasRestrictedAccountObjectModel {
|
|
|
3874
4114
|
'assigneeId',
|
|
3875
4115
|
'timeZoneOffset',
|
|
3876
4116
|
'pendingFields',
|
|
3877
|
-
'
|
|
3878
|
-
|
|
4117
|
+
'formId',
|
|
4118
|
+
'followUpDate',
|
|
4119
|
+
'followUpStatus',
|
|
4120
|
+
'followUpNote',
|
|
4121
|
+
], (value, key) => value[key] === null && nullables.has(key) ? '~' : value[key]);
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
class VasJobRelationalModel extends VasJobBaseModel {
|
|
4125
|
+
getValueByPath(path = []) {
|
|
4126
|
+
const parts = Array.isArray(path) ? path : path.split('.');
|
|
4127
|
+
if (parts[0] === 'fields') {
|
|
4128
|
+
return this.getFieldValueByPath(parts[1], parts.slice(2));
|
|
4129
|
+
}
|
|
4130
|
+
return getValueByPath(parts, this);
|
|
4131
|
+
}
|
|
4132
|
+
getFieldsMap() {
|
|
4133
|
+
return this.fields.reduce((p, c) => {
|
|
4134
|
+
p[c.controlName] = c.value;
|
|
4135
|
+
return p;
|
|
4136
|
+
}, {});
|
|
4137
|
+
}
|
|
4138
|
+
getFieldValueByPath(controlName, path) {
|
|
4139
|
+
for (const field of this.fields) {
|
|
4140
|
+
if (field.controlName === controlName) {
|
|
4141
|
+
if (path.length === 0) {
|
|
4142
|
+
return field.value;
|
|
4143
|
+
}
|
|
4144
|
+
if (field.value === null) {
|
|
4145
|
+
return path.length >= 1 ? undefined : null;
|
|
4146
|
+
}
|
|
4147
|
+
if (isPlainObject(field.value)) {
|
|
4148
|
+
return getValueByPath(path, field.value);
|
|
4149
|
+
}
|
|
4150
|
+
return undefined;
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
return undefined;
|
|
4154
|
+
}
|
|
4155
|
+
static fromDto(dto) {
|
|
4156
|
+
if (dto.fields !== undefined && !Array.isArray(dto.fields)) {
|
|
4157
|
+
throw Error('fields must be an array');
|
|
4158
|
+
}
|
|
4159
|
+
return new VasJobRelationalModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobStatusLastUpdate || '', dto.jobType || '', dto.assigneeId || '', dto.formId || dto.lastForm || '', dto.timeZoneOffset || moment().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.followUpDate || '', dto.followUpStatus || '', dto.followUpNote || '', dto.geoLocation || getEmptyGeoLocation(), dto.files || [], (dto.fields || []).map(f => VasFieldModel.fromDto(f)), dto.jobEmails || [], dto.notes || [], dto.reports || [], dto.tasks || [], dto.instruction || null);
|
|
4160
|
+
}
|
|
4161
|
+
static empty() {
|
|
4162
|
+
return new VasJobRelationalModel('', // id
|
|
4163
|
+
'', // created
|
|
4164
|
+
'', // serverCreated
|
|
4165
|
+
'', // createdBy
|
|
4166
|
+
'', // modified
|
|
4167
|
+
'', // serverModified
|
|
4168
|
+
'', // modifiedBy
|
|
4169
|
+
'', // createdByName
|
|
4170
|
+
'', // modifiedByName
|
|
4171
|
+
'', // account
|
|
4172
|
+
'', // accessGroup
|
|
4173
|
+
'', // reference
|
|
4174
|
+
'', // jobDate
|
|
4175
|
+
'', // jobStatus
|
|
4176
|
+
'', // jobStatusLastUpdate
|
|
4177
|
+
'', // jobType
|
|
4178
|
+
'', // assigneeId
|
|
4179
|
+
'', // formId
|
|
4180
|
+
moment().utcOffset(), // timeZoneOffset
|
|
4181
|
+
0, // pendingFields: number,
|
|
4182
|
+
'', // childModified: string,
|
|
4183
|
+
0, // version: number,
|
|
4184
|
+
'', // followUpDate: string,
|
|
4185
|
+
'', // followUpStatus: string,
|
|
4186
|
+
'', // followUpNote: string,
|
|
4187
|
+
getEmptyGeoLocation(), // geoLocation: GeoLocation,
|
|
4188
|
+
[], // files: Array<VasFileDto>,
|
|
4189
|
+
[], // fields: FType,
|
|
4190
|
+
[], // jobEmails: Array<VasJobEmailDto>,
|
|
4191
|
+
[], // notes: Array<VasNoteDto>,
|
|
4192
|
+
[], // reports: Array<VasReportDto>,
|
|
4193
|
+
[], // tasks: Array<VasTaskDto>,
|
|
4194
|
+
null // instruction: VasInstructionDto | null
|
|
4195
|
+
);
|
|
4196
|
+
}
|
|
4197
|
+
toDto() {
|
|
4198
|
+
return {
|
|
4199
|
+
id: this.id,
|
|
4200
|
+
created: this.created,
|
|
4201
|
+
createdBy: this.createdBy,
|
|
4202
|
+
modified: this.modified,
|
|
4203
|
+
modifiedBy: this.modifiedBy,
|
|
4204
|
+
serverCreated: this.serverCreated,
|
|
4205
|
+
serverModified: this.serverModified,
|
|
4206
|
+
createdByName: this.createdByName,
|
|
4207
|
+
modifiedByName: this.modifiedByName,
|
|
4208
|
+
account: this.account,
|
|
4209
|
+
accessGroup: this.accessGroup,
|
|
4210
|
+
reference: this.reference,
|
|
4211
|
+
jobDate: this.jobDate,
|
|
4212
|
+
jobStatus: this.jobStatus,
|
|
4213
|
+
jobStatusLastUpdate: this.jobStatusLastUpdate,
|
|
4214
|
+
jobType: this.jobType,
|
|
4215
|
+
assigneeId: this.assigneeId,
|
|
4216
|
+
formId: this.formId,
|
|
4217
|
+
timeZoneOffset: this.timeZoneOffset,
|
|
4218
|
+
pendingFields: this.pendingFields,
|
|
4219
|
+
childModified: this.childModified,
|
|
4220
|
+
version: this.version,
|
|
4221
|
+
followUpDate: this.followUpDate,
|
|
4222
|
+
followUpStatus: this.followUpStatus,
|
|
4223
|
+
followUpNote: this.followUpNote,
|
|
4224
|
+
geoLocation: this.geoLocation,
|
|
4225
|
+
files: this.files,
|
|
4226
|
+
fields: this.fields.map(f => f.toDto()),
|
|
4227
|
+
jobEmails: this.jobEmails,
|
|
4228
|
+
notes: this.notes,
|
|
4229
|
+
reports: this.reports,
|
|
4230
|
+
tasks: this.tasks,
|
|
4231
|
+
instruction: this.instruction
|
|
4232
|
+
};
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
class VasJobDocumentModel extends VasJobBaseModel {
|
|
4236
|
+
static fromRelationalDto(dto) {
|
|
4237
|
+
return new VasJobDocumentModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobStatusLastUpdate || '', dto.jobType || '', dto.assigneeId || '', dto.formId || dto.lastForm || '', dto.timeZoneOffset || moment().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.followUpDate || '', dto.followUpStatus || '', dto.followUpNote || '', dto.geoLocation || getEmptyGeoLocation(), dto.files || [], (dto.fields || []).reduce((p, c) => {
|
|
4238
|
+
p[c.controlName.replace('fields.', '')] = c.value;
|
|
4239
|
+
return p;
|
|
4240
|
+
}, {}), dto.jobEmails || [], dto.notes || [], dto.reports || [], dto.tasks || [], dto.instruction || null);
|
|
4241
|
+
}
|
|
4242
|
+
static fromDto(dto) {
|
|
4243
|
+
if (dto.fields !== undefined && !isPlainObject(dto.fields)) {
|
|
4244
|
+
throw Error('fields must be an object');
|
|
4245
|
+
}
|
|
4246
|
+
return new VasJobDocumentModel(dto.id || '', dto.created || '', dto.serverCreated || '', dto.createdBy || '', dto.modified || '', dto.serverModified || '', dto.modifiedBy || '', dto.createdByName || '', dto.modifiedByName || '', dto.account || '', dto.accessGroup || '', dto.reference || '', dto.jobDate || '', dto.jobStatus || '', dto.jobStatusLastUpdate || '', dto.jobType || '', dto.assigneeId || '', dto.formId || dto.lastForm || '', dto.timeZoneOffset || moment().utcOffset(), dto.pendingFields || 0, dto.childModified || '', dto.version || 0, dto.followUpDate || '', dto.followUpStatus || '', dto.followUpNote || '', dto.geoLocation || getEmptyGeoLocation(), dto.files || [], dto.fields || {}, dto.jobEmails || [], dto.notes || [], dto.reports || [], dto.tasks || [], dto.instruction || null);
|
|
4247
|
+
}
|
|
4248
|
+
static empty() {
|
|
4249
|
+
return new VasJobDocumentModel('', // id
|
|
4250
|
+
'', // created
|
|
4251
|
+
'', // serverCreated
|
|
4252
|
+
'', // createdBy
|
|
4253
|
+
'', // modified
|
|
4254
|
+
'', // serverModified
|
|
4255
|
+
'', // modifiedBy
|
|
4256
|
+
'', // createdByName
|
|
4257
|
+
'', // modifiedByName
|
|
4258
|
+
'', // account
|
|
4259
|
+
'', // accessGroup
|
|
4260
|
+
'', // reference
|
|
4261
|
+
'', // jobDate
|
|
4262
|
+
'', // jobStatus
|
|
4263
|
+
'', // jobStatusLastUpdate
|
|
4264
|
+
'', // jobType
|
|
4265
|
+
'', // assigneeId
|
|
4266
|
+
'', // formId
|
|
4267
|
+
moment().utcOffset(), // timeZoneOffset
|
|
4268
|
+
0, // pendingFields: number,
|
|
4269
|
+
'', // childModified: string,
|
|
4270
|
+
0, // version: number,
|
|
4271
|
+
'', // followUpDate: string,
|
|
4272
|
+
'', // followUpStatus: string,
|
|
4273
|
+
'', // followUpNote: string,
|
|
4274
|
+
getEmptyGeoLocation(), // geoLocation: GeoLocation,
|
|
4275
|
+
[], // files: Array<VasFileDto>,
|
|
4276
|
+
{}, // fields: FType,
|
|
4277
|
+
[], // jobEmails: Array<VasJobEmailDto>,
|
|
4278
|
+
[], // notes: Array<VasNoteDto>,
|
|
4279
|
+
[], // reports: Array<VasReportDto>,
|
|
4280
|
+
[], // tasks: Array<VasTaskDto>,
|
|
4281
|
+
null // instruction: VasInstructionDto | null
|
|
4282
|
+
);
|
|
4283
|
+
}
|
|
4284
|
+
toDto() {
|
|
4285
|
+
return {
|
|
4286
|
+
id: this.id,
|
|
4287
|
+
created: this.created,
|
|
4288
|
+
createdBy: this.createdBy,
|
|
4289
|
+
modified: this.modified,
|
|
4290
|
+
modifiedBy: this.modifiedBy,
|
|
4291
|
+
serverCreated: this.serverCreated,
|
|
4292
|
+
serverModified: this.serverModified,
|
|
4293
|
+
createdByName: this.createdByName,
|
|
4294
|
+
modifiedByName: this.modifiedByName,
|
|
4295
|
+
account: this.account,
|
|
4296
|
+
accessGroup: this.accessGroup,
|
|
4297
|
+
reference: this.reference,
|
|
4298
|
+
jobDate: this.jobDate,
|
|
4299
|
+
jobStatus: this.jobStatus,
|
|
4300
|
+
jobStatusLastUpdate: this.jobStatusLastUpdate,
|
|
4301
|
+
jobType: this.jobType,
|
|
4302
|
+
assigneeId: this.assigneeId,
|
|
4303
|
+
formId: this.formId,
|
|
4304
|
+
timeZoneOffset: this.timeZoneOffset,
|
|
4305
|
+
pendingFields: this.pendingFields,
|
|
4306
|
+
childModified: this.childModified,
|
|
4307
|
+
version: this.version,
|
|
4308
|
+
followUpDate: this.followUpDate,
|
|
4309
|
+
followUpStatus: this.followUpStatus,
|
|
4310
|
+
followUpNote: this.followUpNote,
|
|
4311
|
+
geoLocation: this.geoLocation,
|
|
4312
|
+
files: this.files,
|
|
4313
|
+
fields: this.fields,
|
|
4314
|
+
jobEmails: this.jobEmails,
|
|
4315
|
+
notes: this.notes,
|
|
4316
|
+
reports: this.reports,
|
|
4317
|
+
tasks: this.tasks,
|
|
4318
|
+
instruction: this.instruction
|
|
4319
|
+
};
|
|
3879
4320
|
}
|
|
3880
4321
|
}
|
|
3881
4322
|
|
|
@@ -4486,5 +4927,5 @@ class VasVehicleModel extends VasAccountObjectModel {
|
|
|
4486
4927
|
* Generated bundle index. Do not edit.
|
|
4487
4928
|
*/
|
|
4488
4929
|
|
|
4489
|
-
export { VasAccessGroupModel, VasAccountIndexingMode, VasAccountObjectModel, VasBaseModel, VasBranchModel, VasContactModel, VasContactTypeModel, VasControlConfigDirection, VasControlModel, VasControlTypeModel, VasFieldModel, VasFileModel, VasFireUserModel, VasFormConfigModel, VasFormModel, VasGroupModel, VasInstructionJobFieldModel, VasInstructionJobModel, VasInstructionModel, VasInstructionProviderModel, VasInvitationModel, VasInvitationTypeEnum, VasJobActionControlsStepModel, VasJobActionEmailFormStepModel, VasJobActionInstructionStepModel, VasJobActionModel, VasJobActionNotePromptStepModel, VasJobActionSetDatetimeStepModel, VasJobActionSetJobStatusStepModel, VasJobActionSetValueStepModel, VasJobActionWebhookStepModel, VasJobCopyModel, VasJobDataModel, VasJobEmailModel,
|
|
4930
|
+
export { EstimateControlValueModel, EstimateItemModel, EstimateModel, VasAccessGroupModel, VasAccountIndexingMode, VasAccountObjectModel, VasBaseModel, VasBranchModel, VasContactModel, VasContactTypeModel, VasControlConfigDirection, VasControlModel, VasControlTypeModel, VasFieldModel, VasFileModel, VasFireUserModel, VasFormConfigModel, VasFormModel, VasGroupModel, VasInstructionJobFieldModel, VasInstructionJobModel, VasInstructionModel, VasInstructionProviderModel, VasInvitationModel, VasInvitationTypeEnum, VasJobActionControlsStepModel, VasJobActionEmailFormStepModel, VasJobActionInstructionStepModel, VasJobActionModel, VasJobActionNotePromptStepModel, VasJobActionSetDatetimeStepModel, VasJobActionSetJobStatusStepModel, VasJobActionSetValueStepModel, VasJobActionWebhookStepModel, VasJobBaseModel, VasJobCopyModel, VasJobDataModel, VasJobDocumentModel, VasJobEmailModel, VasJobRelationalModel, VasJobStatusModel, VasJobTypeModel, VasLookupModel, VasLookupTypeModel, VasMembershipModel, VasNoteModel, VasReportLayoutModel, VasReportModel, VasReportRequestModel, VasRestrictedAccountObjectModel, VasTaskModel, VasTaskTypeModel, VasUserModel, VasVehicleModel, getEmptyGeoLocation, getValueByPath, isArrayString, isCameraControlValueV1, isCameraControlValueV2, isFileDto, isJobAutomationStep, isJobControlsStep, isJobEmailFormStep, isJobInstructionStep, isJobNotePromptStep, isJobSetDatetimeStep, isJobSetJobStatusStep, isJobSetValueStep, isJobWebhookStep, isNumeric, isPlainObject, omit, only, prepareFieldApiValue, stepModelFactory, tryJson };
|
|
4490
4931
|
//# sourceMappingURL=ironcode-vas-lib.mjs.map
|