@pdtf/schemas 3.6.0-41 → 3.6.0-43
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/index.js +4 -0
- package/package.json +1 -1
- package/src/schemas/v3/combined.json +156 -21
- package/src/schemas/v3/overlays/extensions/ac.json +288 -0
- package/src/schemas/v3/overlays/extensions/ta.json +240 -0
- package/src/schemas/v3/overlays/sef25.json +572 -0
- package/src/schemas/v3/overlays/ta6ed6.json +55 -20
- package/src/schemas/v3/overlays/ta7ed5.json +17 -5
- package/src/tests/v3/extensionOverlays.test.js +135 -1
- package/src/tests/v3/ta6ed6.test.js +58 -1
- package/src/tests/v3/ta7ed5.test.js +34 -1
- package/src/utils/extractExtensionOverlays.js +43 -0
- package/src/utils/extractOverlay.js +22 -20
- package/.claude/plans/not-seeing-associated-costs.md +0 -99
- package/.claude/settings.local.json +0 -61
|
@@ -117,7 +117,12 @@
|
|
|
117
117
|
],
|
|
118
118
|
"required": [
|
|
119
119
|
"role"
|
|
120
|
-
]
|
|
120
|
+
],
|
|
121
|
+
"properties": {
|
|
122
|
+
"role": {
|
|
123
|
+
"ta6ed6Ref": "1"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
121
126
|
}
|
|
122
127
|
},
|
|
123
128
|
"propertyPack": {
|
|
@@ -838,7 +843,12 @@
|
|
|
838
843
|
],
|
|
839
844
|
"required": [
|
|
840
845
|
"yesNo"
|
|
841
|
-
]
|
|
846
|
+
],
|
|
847
|
+
"properties": {
|
|
848
|
+
"yesNo": {
|
|
849
|
+
"ta6ed6Ref": "5.1a(f)"
|
|
850
|
+
}
|
|
851
|
+
}
|
|
842
852
|
},
|
|
843
853
|
"deedRestrictionConsent": {
|
|
844
854
|
"ta6ed6Ref": "5.1a(g)",
|
|
@@ -868,7 +878,12 @@
|
|
|
868
878
|
],
|
|
869
879
|
"required": [
|
|
870
880
|
"yesNo"
|
|
871
|
-
]
|
|
881
|
+
],
|
|
882
|
+
"properties": {
|
|
883
|
+
"yesNo": {
|
|
884
|
+
"ta6ed6Ref": "5.1a(g)"
|
|
885
|
+
}
|
|
886
|
+
}
|
|
872
887
|
},
|
|
873
888
|
"workCompleted": {
|
|
874
889
|
"ta6ed6Ref": "5.1a(b)",
|
|
@@ -1255,7 +1270,12 @@
|
|
|
1255
1270
|
],
|
|
1256
1271
|
"required": [
|
|
1257
1272
|
"yesNo"
|
|
1258
|
-
]
|
|
1273
|
+
],
|
|
1274
|
+
"properties": {
|
|
1275
|
+
"yesNo": {
|
|
1276
|
+
"ta6ed6Ref": "5.1c(f)"
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1259
1279
|
},
|
|
1260
1280
|
"deedRestrictionConsent": {
|
|
1261
1281
|
"ta6ed6Ref": "5.1c(g)",
|
|
@@ -1285,7 +1305,12 @@
|
|
|
1285
1305
|
],
|
|
1286
1306
|
"required": [
|
|
1287
1307
|
"yesNo"
|
|
1288
|
-
]
|
|
1308
|
+
],
|
|
1309
|
+
"properties": {
|
|
1310
|
+
"yesNo": {
|
|
1311
|
+
"ta6ed6Ref": "5.1c(g)"
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1289
1314
|
},
|
|
1290
1315
|
"workCompleted": {
|
|
1291
1316
|
"ta6ed6Ref": "5.1c(b)",
|
|
@@ -2895,7 +2920,12 @@
|
|
|
2895
2920
|
],
|
|
2896
2921
|
"required": [
|
|
2897
2922
|
"yesNo"
|
|
2898
|
-
]
|
|
2923
|
+
],
|
|
2924
|
+
"properties": {
|
|
2925
|
+
"yesNo": {
|
|
2926
|
+
"ta6ed6Ref": "5.6c"
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2899
2929
|
},
|
|
2900
2930
|
"panelProviderLease": {
|
|
2901
2931
|
"ta6ed6Ref": "5.6d",
|
|
@@ -3367,6 +3397,7 @@
|
|
|
3367
3397
|
],
|
|
3368
3398
|
"properties": {
|
|
3369
3399
|
"yesNo": {
|
|
3400
|
+
"ta6ed6Ref": "11.5b",
|
|
3370
3401
|
"enum": [
|
|
3371
3402
|
"Yes",
|
|
3372
3403
|
"No",
|
|
@@ -3655,7 +3686,12 @@
|
|
|
3655
3686
|
],
|
|
3656
3687
|
"required": [
|
|
3657
3688
|
"yesNo"
|
|
3658
|
-
]
|
|
3689
|
+
],
|
|
3690
|
+
"properties": {
|
|
3691
|
+
"yesNo": {
|
|
3692
|
+
"ta6ed6Ref": "11.7j"
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3659
3695
|
},
|
|
3660
3696
|
"plantDrainsIntoWaterway": {
|
|
3661
3697
|
"ta6ed6Ref": "11.7f",
|
|
@@ -4210,8 +4246,7 @@
|
|
|
4210
4246
|
],
|
|
4211
4247
|
"required": [
|
|
4212
4248
|
"isInsured",
|
|
4213
|
-
"difficultiesObtainingInsurance"
|
|
4214
|
-
"insuranceClaims"
|
|
4249
|
+
"difficultiesObtainingInsurance"
|
|
4215
4250
|
],
|
|
4216
4251
|
"properties": {
|
|
4217
4252
|
"difficultiesObtainingInsurance": {
|
|
@@ -4483,12 +4518,7 @@
|
|
|
4483
4518
|
"description": "If yes, give details of how much, how often and who you pay:",
|
|
4484
4519
|
"properties": {
|
|
4485
4520
|
"yesNo": {
|
|
4486
|
-
"ta6ed6Ref": "9.2.1"
|
|
4487
|
-
"enum": [
|
|
4488
|
-
"Yes",
|
|
4489
|
-
"No",
|
|
4490
|
-
"Not applicable"
|
|
4491
|
-
]
|
|
4521
|
+
"ta6ed6Ref": "9.2.1"
|
|
4492
4522
|
}
|
|
4493
4523
|
}
|
|
4494
4524
|
},
|
|
@@ -6288,7 +6318,12 @@
|
|
|
6288
6318
|
"ta6ed6Ref": "13.6a",
|
|
6289
6319
|
"required": [
|
|
6290
6320
|
"yesNo"
|
|
6291
|
-
]
|
|
6321
|
+
],
|
|
6322
|
+
"properties": {
|
|
6323
|
+
"yesNo": {
|
|
6324
|
+
"ta6ed6Ref": "13.6a"
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6292
6327
|
},
|
|
6293
6328
|
"aged17OrOverWillSignToConfirmWillVacate": {
|
|
6294
6329
|
"ta6ed6Ref": "13.6b",
|
|
@@ -6377,19 +6412,19 @@
|
|
|
6377
6412
|
"properties": {
|
|
6378
6413
|
"removeRubbish": {
|
|
6379
6414
|
"ta6ed6Ref": "14.2a",
|
|
6380
|
-
"title": "
|
|
6415
|
+
"title": "All rubbish is removed from the property (including from the loft, garden, outbuildings, garages and sheds) and that the property will be left in a clean and tidy condition?"
|
|
6381
6416
|
},
|
|
6382
6417
|
"replaceLightFittings": {
|
|
6383
6418
|
"ta6ed6Ref": "14.2b",
|
|
6384
|
-
"title": "
|
|
6419
|
+
"title": "If light fittings are removed, the fittings will be replaced with ceiling rose, flex, bulb holder and bulb?"
|
|
6385
6420
|
},
|
|
6386
6421
|
"takeReasonableCare": {
|
|
6387
6422
|
"ta6ed6Ref": "14.2c",
|
|
6388
|
-
"title": "
|
|
6423
|
+
"title": "Reasonable care will be taken when removing any other fittings or contents?"
|
|
6389
6424
|
},
|
|
6390
6425
|
"leaveKeys": {
|
|
6391
6426
|
"ta6ed6Ref": "14.2d",
|
|
6392
|
-
"title": "
|
|
6427
|
+
"title": "Keys to all windows and doors and details of codes for alarms and any other equipment will be left at the property or with the estate agent?"
|
|
6393
6428
|
}
|
|
6394
6429
|
}
|
|
6395
6430
|
},
|
|
@@ -74,8 +74,6 @@
|
|
|
74
74
|
"ta7ed5Ref": "2",
|
|
75
75
|
"required": [
|
|
76
76
|
"typeOfLeasehold",
|
|
77
|
-
"sharedOwnership",
|
|
78
|
-
"leaseTerm",
|
|
79
77
|
"contactDetails",
|
|
80
78
|
"consents",
|
|
81
79
|
"groundRent",
|
|
@@ -84,8 +82,6 @@
|
|
|
84
82
|
"disputes",
|
|
85
83
|
"buildingSafetyAct",
|
|
86
84
|
"enfranchisement",
|
|
87
|
-
"buildingSafety",
|
|
88
|
-
"general",
|
|
89
85
|
"requiredDocuments",
|
|
90
86
|
"additionalInformation",
|
|
91
87
|
"confirmationOfAccuracy"
|
|
@@ -1451,6 +1447,17 @@
|
|
|
1451
1447
|
"ta7ed5Ref": "12.2"
|
|
1452
1448
|
}
|
|
1453
1449
|
}
|
|
1450
|
+
},
|
|
1451
|
+
"confirmationOfAccuracy": {
|
|
1452
|
+
"ta7ed5Ref": "13",
|
|
1453
|
+
"required": [
|
|
1454
|
+
"confirmation"
|
|
1455
|
+
],
|
|
1456
|
+
"properties": {
|
|
1457
|
+
"confirmation": {
|
|
1458
|
+
"ta7ed5Ref": "13.1"
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1454
1461
|
}
|
|
1455
1462
|
}
|
|
1456
1463
|
}
|
|
@@ -1472,7 +1479,12 @@
|
|
|
1472
1479
|
"ta7ed5Ref": "2",
|
|
1473
1480
|
"required": [
|
|
1474
1481
|
"ownershipType"
|
|
1475
|
-
]
|
|
1482
|
+
],
|
|
1483
|
+
"properties": {
|
|
1484
|
+
"ownershipType": {
|
|
1485
|
+
"ta7ed5Ref": "2"
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1476
1488
|
}
|
|
1477
1489
|
}
|
|
1478
1490
|
}
|
|
@@ -241,6 +241,8 @@ describe("Extension Overlays", () => {
|
|
|
241
241
|
"nd",
|
|
242
242
|
"mi",
|
|
243
243
|
"tr", // SEF25 (new)
|
|
244
|
+
"ac", // SEF25 (new)
|
|
245
|
+
"ta", // Compatibility
|
|
244
246
|
];
|
|
245
247
|
|
|
246
248
|
expect(Object.keys(extensionOverlays).sort()).toEqual(
|
|
@@ -258,6 +260,65 @@ describe("Extension Overlays", () => {
|
|
|
258
260
|
});
|
|
259
261
|
});
|
|
260
262
|
|
|
263
|
+
describe("TA6 edition 6 compatibility extension", () => {
|
|
264
|
+
test("keeps parking enum strict to TA6 edition 6 when compatibility is applied", () => {
|
|
265
|
+
const ta6Validator = getSubschemaValidator(
|
|
266
|
+
"/propertyPack/parking/controlledParking",
|
|
267
|
+
schemaId,
|
|
268
|
+
["ta6ed6"]
|
|
269
|
+
);
|
|
270
|
+
expect(ta6Validator({ yesNo: "Not known" })).toBe(false);
|
|
271
|
+
|
|
272
|
+
const compatValidator = getSubschemaValidator(
|
|
273
|
+
"/propertyPack/parking/controlledParking",
|
|
274
|
+
schemaId,
|
|
275
|
+
["nts2025", "ta"]
|
|
276
|
+
);
|
|
277
|
+
expect(compatValidator({ yesNo: "Not known" })).toBe(false);
|
|
278
|
+
expect(compatValidator({ yesNo: "No" })).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("applies TA6 edition 6 selector values on shared rights and boundaries paths", () => {
|
|
282
|
+
const schema = getTransactionSchema(schemaId, [
|
|
283
|
+
"nts2025",
|
|
284
|
+
"ta",
|
|
285
|
+
]);
|
|
286
|
+
const propertyPack = schema.properties.propertyPack.properties;
|
|
287
|
+
|
|
288
|
+
expect(
|
|
289
|
+
propertyPack.rightsAndInformalArrangements.properties
|
|
290
|
+
.sharedContributions.oneOf[0].properties.yesNo.enum
|
|
291
|
+
).toEqual(["No", "Not applicable"]);
|
|
292
|
+
|
|
293
|
+
expect(
|
|
294
|
+
propertyPack.rightsAndInformalArrangements.properties
|
|
295
|
+
.accessRestrictionAttempts.oneOf[0].properties.yesNo.enum
|
|
296
|
+
).toEqual(["No", "Not known"]);
|
|
297
|
+
|
|
298
|
+
expect(
|
|
299
|
+
propertyPack.legalBoundaries.properties.flyingFreehold.oneOf[0]
|
|
300
|
+
.properties.yesNo.enum
|
|
301
|
+
).toEqual(["No", "Not known"]);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("applies TA6 edition 6 heating values and report enum when compatibility is applied", () => {
|
|
305
|
+
const heatingFeatures = getSubschemaValidator(
|
|
306
|
+
"/propertyPack/heating/otherHeatingFeatures",
|
|
307
|
+
schemaId,
|
|
308
|
+
["nts2025", "ta"]
|
|
309
|
+
);
|
|
310
|
+
expect(heatingFeatures(["Mains gas", "LPG", "Biomass"])).toBe(true);
|
|
311
|
+
|
|
312
|
+
const compatReport = getSubschemaValidator(
|
|
313
|
+
"/propertyPack/heating/heatingSystem/centralHeatingDetails/heatingLastServicedReport",
|
|
314
|
+
schemaId,
|
|
315
|
+
["nts2025", "ta"]
|
|
316
|
+
);
|
|
317
|
+
expect(compatReport("Not available")).toBe(false);
|
|
318
|
+
expect(compatReport("Attached")).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
261
322
|
describe("NTS2 equivalence test", () => {
|
|
262
323
|
test("should produce equivalent result to nts2 when all relevant extensions are combined", () => {
|
|
263
324
|
// Get the full NTS2 schema
|
|
@@ -1404,8 +1465,81 @@ describe("Extension Overlays", () => {
|
|
|
1404
1465
|
});
|
|
1405
1466
|
});
|
|
1406
1467
|
|
|
1468
|
+
describe("Alterations and changes (ac)", () => {
|
|
1469
|
+
const alterationFields = [
|
|
1470
|
+
"extension",
|
|
1471
|
+
"loftConversion",
|
|
1472
|
+
"garageConversion",
|
|
1473
|
+
"removalOfInternalWalls",
|
|
1474
|
+
"changeOfUse",
|
|
1475
|
+
];
|
|
1476
|
+
|
|
1477
|
+
test("should expose only selected alterations and changes fields", () => {
|
|
1478
|
+
const schema = getTransactionSchema(schemaId, ["ac"]);
|
|
1479
|
+
const alterations =
|
|
1480
|
+
schema.properties?.propertyPack?.properties?.alterationsAndChanges;
|
|
1481
|
+
const acOverlayAlterations =
|
|
1482
|
+
extensionOverlays.ac.properties?.propertyPack?.properties
|
|
1483
|
+
?.alterationsAndChanges;
|
|
1484
|
+
|
|
1485
|
+
expect(alterations?.ntsRef).toBe("A1");
|
|
1486
|
+
expect(alterations?.required).toEqual(
|
|
1487
|
+
expect.arrayContaining(alterationFields)
|
|
1488
|
+
);
|
|
1489
|
+
expect(Object.keys(acOverlayAlterations?.properties || {}).sort()).toEqual(
|
|
1490
|
+
alterationFields.slice().sort()
|
|
1491
|
+
);
|
|
1492
|
+
|
|
1493
|
+
alterationFields.forEach((field) => {
|
|
1494
|
+
const fieldSchema = alterations.properties?.[field];
|
|
1495
|
+
const yesBranch = fieldSchema?.oneOf?.[1];
|
|
1496
|
+
const overlayYesBranch =
|
|
1497
|
+
acOverlayAlterations.properties?.[field]?.oneOf?.[1];
|
|
1498
|
+
|
|
1499
|
+
expect(fieldSchema?.required).toContain("yesNo");
|
|
1500
|
+
expect(fieldSchema?.properties?.yesNo?.ntsRef).toMatch(/^A1\./);
|
|
1501
|
+
expect(yesBranch?.required).toEqual(
|
|
1502
|
+
expect.arrayContaining([
|
|
1503
|
+
"details",
|
|
1504
|
+
"planningPermission",
|
|
1505
|
+
"buildingRegApproval",
|
|
1506
|
+
])
|
|
1507
|
+
);
|
|
1508
|
+
expect(Object.keys(overlayYesBranch?.properties || {}).sort()).toEqual([
|
|
1509
|
+
"buildingRegApproval",
|
|
1510
|
+
"details",
|
|
1511
|
+
"planningPermission",
|
|
1512
|
+
]);
|
|
1513
|
+
expect(yesBranch?.properties?.workCompleted?.ntsRef).toBeUndefined();
|
|
1514
|
+
expect(yesBranch?.properties?.documents?.ntsRef).toBeUndefined();
|
|
1515
|
+
expect(yesBranch?.properties?.yearCompleted?.ntsRef).toBeUndefined();
|
|
1516
|
+
expect(yesBranch?.properties?.listedBuildingConsent?.ntsRef).toBeUndefined();
|
|
1517
|
+
expect(yesBranch?.properties?.deedRestrictionConsent?.ntsRef).toBeUndefined();
|
|
1518
|
+
});
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
test("should require details and consent yesNo fields for Yes answers", () => {
|
|
1522
|
+
const validator = getSubschemaValidator(
|
|
1523
|
+
"/propertyPack/alterationsAndChanges/extension",
|
|
1524
|
+
schemaId,
|
|
1525
|
+
["ac"]
|
|
1526
|
+
);
|
|
1527
|
+
|
|
1528
|
+
expect(validator({ yesNo: "No" })).toBe(true);
|
|
1529
|
+
expect(validator({ yesNo: "Yes" })).toBe(false);
|
|
1530
|
+
expect(
|
|
1531
|
+
validator({
|
|
1532
|
+
yesNo: "Yes",
|
|
1533
|
+
details: "Single-storey rear extension in 2018",
|
|
1534
|
+
planningPermission: { yesNo: "Not required" },
|
|
1535
|
+
buildingRegApproval: { yesNo: "Yes" },
|
|
1536
|
+
})
|
|
1537
|
+
).toBe(true);
|
|
1538
|
+
});
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1407
1541
|
describe("All SEF25 extensions combined", () => {
|
|
1408
|
-
const allSef25 = ["sc", "pc", "ph", "dk", "rw", "sd", "lc", "wg", "ic", "nd", "mi", "tr"];
|
|
1542
|
+
const allSef25 = ["sc", "pc", "ph", "dk", "rw", "sd", "lc", "wg", "ic", "nd", "mi", "tr", "ac"];
|
|
1409
1543
|
|
|
1410
1544
|
test("should load all SEF25 extensions together without error", () => {
|
|
1411
1545
|
const schema = getTransactionSchema(schemaId, ["baspiV5", ...allSef25]);
|
|
@@ -2,10 +2,38 @@ const {
|
|
|
2
2
|
getTransactionSchema,
|
|
3
3
|
getSubschemaValidator,
|
|
4
4
|
} = require("../../../index.js");
|
|
5
|
+
const ta6ed6Overlay = require("../../schemas/v3/overlays/ta6ed6.json");
|
|
5
6
|
|
|
6
7
|
const schemaId =
|
|
7
8
|
"https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json";
|
|
8
9
|
|
|
10
|
+
const collectRequiredWithoutPropertyIssues = (schema) => {
|
|
11
|
+
const issues = [];
|
|
12
|
+
|
|
13
|
+
const walk = (node, path = "#") => {
|
|
14
|
+
if (!node || typeof node !== "object") return;
|
|
15
|
+
|
|
16
|
+
if (Array.isArray(node.required)) {
|
|
17
|
+
node.required.forEach((propertyName) => {
|
|
18
|
+
if (!node.properties?.[propertyName]) {
|
|
19
|
+
issues.push(`${path} requires ${propertyName}`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Object.entries(node.properties || {}).forEach(([key, value]) =>
|
|
25
|
+
walk(value, `${path}/properties/${key}`)
|
|
26
|
+
);
|
|
27
|
+
(node.oneOf || []).forEach((value, index) =>
|
|
28
|
+
walk(value, `${path}/oneOf/${index}`)
|
|
29
|
+
);
|
|
30
|
+
if (node.items) walk(node.items, `${path}/items`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
walk(schema);
|
|
34
|
+
return issues;
|
|
35
|
+
};
|
|
36
|
+
|
|
9
37
|
describe("TA6 Edition 6 overlay", () => {
|
|
10
38
|
describe("Schema merging", () => {
|
|
11
39
|
test("merged schema contains ta6ed6Ref annotations", () => {
|
|
@@ -21,6 +49,10 @@ describe("TA6 Edition 6 overlay", () => {
|
|
|
21
49
|
).toBe("11");
|
|
22
50
|
});
|
|
23
51
|
|
|
52
|
+
test("generated overlay exposes every TA6 edition 6 required property", () => {
|
|
53
|
+
expect(collectRequiredWithoutPropertyIssues(ta6ed6Overlay)).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
|
|
24
56
|
test("merged schema has all 22 ta6ed6 required propertyPack sections", () => {
|
|
25
57
|
const schema = getTransactionSchema(schemaId, ["ta6ed6"]);
|
|
26
58
|
const required = schema.properties.propertyPack.required;
|
|
@@ -751,11 +783,26 @@ describe("TA6 Edition 6 overlay", () => {
|
|
|
751
783
|
refused: { yesNo: "No" },
|
|
752
784
|
},
|
|
753
785
|
whoInsures: "Landlord insures the building",
|
|
754
|
-
insuranceClaims: { yesNo: "No" },
|
|
755
786
|
};
|
|
756
787
|
expect(validator(data)).toBe(true);
|
|
757
788
|
});
|
|
758
789
|
|
|
790
|
+
test("isInsured Yes without insuranceClaims is invalid", () => {
|
|
791
|
+
const data = {
|
|
792
|
+
isInsured: "Yes",
|
|
793
|
+
difficultiesObtainingInsurance: {
|
|
794
|
+
abnormalRiseInPremiums: { yesNo: "No" },
|
|
795
|
+
subjectToHighExcesses: { yesNo: "No" },
|
|
796
|
+
subjectToUnusualConditions: { yesNo: "No" },
|
|
797
|
+
refused: { yesNo: "No" },
|
|
798
|
+
},
|
|
799
|
+
};
|
|
800
|
+
expect(validator(data)).toBe(false);
|
|
801
|
+
expect(
|
|
802
|
+
validator.errors.some((e) => e.message.includes("'insuranceClaims'"))
|
|
803
|
+
).toBe(true);
|
|
804
|
+
});
|
|
805
|
+
|
|
759
806
|
test("isInsured No without whoInsures is invalid", () => {
|
|
760
807
|
const data = {
|
|
761
808
|
isInsured: "No",
|
|
@@ -1585,6 +1632,16 @@ describe("TA6 Edition 6 overlay", () => {
|
|
|
1585
1632
|
expect(mfd.discriminator).toBeDefined();
|
|
1586
1633
|
});
|
|
1587
1634
|
|
|
1635
|
+
test("plant registered yesNo carries TA6 edition 6 ref", () => {
|
|
1636
|
+
const schema = getTransactionSchema(schemaId, ["ta6ed6"]);
|
|
1637
|
+
const plantRegistered =
|
|
1638
|
+
schema.properties.propertyPack.properties.waterAndDrainage.properties
|
|
1639
|
+
.drainage.properties.mainsFoulDrainage.oneOf[2].properties
|
|
1640
|
+
.offMainsDrainageSystem.properties.plantRegistered;
|
|
1641
|
+
expect(plantRegistered.ta6ed6Ref).toBe("11.7j");
|
|
1642
|
+
expect(plantRegistered.properties.yesNo.ta6ed6Ref).toBe("11.7j");
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1588
1645
|
test("mainsFoulDrainage No with object is accepted", () => {
|
|
1589
1646
|
const data = {
|
|
1590
1647
|
water: {
|
|
@@ -2,12 +2,40 @@ const {
|
|
|
2
2
|
getTransactionSchema,
|
|
3
3
|
getSubschemaValidator,
|
|
4
4
|
} = require("../../../index.js");
|
|
5
|
+
const ta7ed5Overlay = require("../../schemas/v3/overlays/ta7ed5.json");
|
|
5
6
|
|
|
6
7
|
const schemaId =
|
|
7
8
|
"https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json";
|
|
8
9
|
|
|
9
10
|
const overlays = ["ta7ed5"];
|
|
10
11
|
|
|
12
|
+
const collectRequiredWithoutPropertyIssues = (schema) => {
|
|
13
|
+
const issues = [];
|
|
14
|
+
|
|
15
|
+
const walk = (node, path = "#") => {
|
|
16
|
+
if (!node || typeof node !== "object") return;
|
|
17
|
+
|
|
18
|
+
if (Array.isArray(node.required)) {
|
|
19
|
+
node.required.forEach((propertyName) => {
|
|
20
|
+
if (!node.properties?.[propertyName]) {
|
|
21
|
+
issues.push(`${path} requires ${propertyName}`);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Object.entries(node.properties || {}).forEach(([key, value]) =>
|
|
27
|
+
walk(value, `${path}/properties/${key}`)
|
|
28
|
+
);
|
|
29
|
+
(node.oneOf || []).forEach((value, index) =>
|
|
30
|
+
walk(value, `${path}/oneOf/${index}`)
|
|
31
|
+
);
|
|
32
|
+
if (node.items) walk(node.items, `${path}/items`);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
walk(schema);
|
|
36
|
+
return issues;
|
|
37
|
+
};
|
|
38
|
+
|
|
11
39
|
// Helper: navigate to a deeply nested property in the merged schema
|
|
12
40
|
const getSchemaProperty = (schema, dotPath) => {
|
|
13
41
|
return dotPath.split(".").reduce((obj, key) => obj?.[key], schema);
|
|
@@ -37,7 +65,11 @@ describe("TA7 5th edition overlay", () => {
|
|
|
37
65
|
expect(required).toContain("ownership");
|
|
38
66
|
});
|
|
39
67
|
|
|
40
|
-
test("
|
|
68
|
+
test("generated overlay exposes every TA7 edition 5 required property", () => {
|
|
69
|
+
expect(collectRequiredWithoutPropertyIssues(ta7ed5Overlay)).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("leasehold branch requires the referenced ta7ed5 sections", () => {
|
|
41
73
|
const leaseholdOneOf =
|
|
42
74
|
schema.properties?.propertyPack?.properties?.ownership?.properties
|
|
43
75
|
?.ownershipsToBeTransferred?.items?.oneOf;
|
|
@@ -60,6 +92,7 @@ describe("TA7 5th edition overlay", () => {
|
|
|
60
92
|
expect(required).toContain("requiredDocuments");
|
|
61
93
|
expect(required).toContain("additionalInformation");
|
|
62
94
|
expect(required).toContain("confirmationOfAccuracy");
|
|
95
|
+
expect(required).not.toContain("buildingSafety");
|
|
63
96
|
});
|
|
64
97
|
});
|
|
65
98
|
|
|
@@ -307,6 +307,49 @@ const extensionMappings = {
|
|
|
307
307
|
"/properties/propertyPack/properties/ownership/properties/ownershipsToBeTransferred/items/oneOf/0/properties/titleRestrictions",
|
|
308
308
|
],
|
|
309
309
|
},
|
|
310
|
+
|
|
311
|
+
// Alterations and changes
|
|
312
|
+
ac: {
|
|
313
|
+
name: "alterationsAndChanges",
|
|
314
|
+
description: "Selected alterations and changes fields for SEF25",
|
|
315
|
+
refType: "sef25Ref",
|
|
316
|
+
paths: [
|
|
317
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/extension/properties/yesNo",
|
|
318
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/extension/oneOf/1/properties/details",
|
|
319
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/extension/oneOf/1/properties/planningPermission/properties/yesNo",
|
|
320
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/extension/oneOf/1/properties/buildingRegApproval/properties/yesNo",
|
|
321
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/loftConversion/properties/yesNo",
|
|
322
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/loftConversion/oneOf/1/properties/details",
|
|
323
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/loftConversion/oneOf/1/properties/planningPermission/properties/yesNo",
|
|
324
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/loftConversion/oneOf/1/properties/buildingRegApproval/properties/yesNo",
|
|
325
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/garageConversion/properties/yesNo",
|
|
326
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/garageConversion/oneOf/1/properties/details",
|
|
327
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/garageConversion/oneOf/1/properties/planningPermission/properties/yesNo",
|
|
328
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/garageConversion/oneOf/1/properties/buildingRegApproval/properties/yesNo",
|
|
329
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/removalOfInternalWalls/properties/yesNo",
|
|
330
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/removalOfInternalWalls/oneOf/1/properties/details",
|
|
331
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/removalOfInternalWalls/oneOf/1/properties/planningPermission/properties/yesNo",
|
|
332
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/removalOfInternalWalls/oneOf/1/properties/buildingRegApproval/properties/yesNo",
|
|
333
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/changeOfUse/properties/yesNo",
|
|
334
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/changeOfUse/oneOf/1/properties/details",
|
|
335
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/changeOfUse/oneOf/1/properties/planningPermission/properties/yesNo",
|
|
336
|
+
"/properties/propertyPack/properties/alterationsAndChanges/properties/changeOfUse/oneOf/1/properties/buildingRegApproval/properties/yesNo",
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
// TA6 edition 6 compatibility for shared MI/NTS validation
|
|
341
|
+
ta: {
|
|
342
|
+
name: "ta6Ed6Compatibility",
|
|
343
|
+
description:
|
|
344
|
+
"Compatibility enum ranges for validating shared PDTF state containing TA6 edition 6 answers",
|
|
345
|
+
refType: "ta6ed6CompatRef",
|
|
346
|
+
paths: [
|
|
347
|
+
"/properties/propertyPack/properties/parking",
|
|
348
|
+
"/properties/propertyPack/properties/heating",
|
|
349
|
+
"/properties/propertyPack/properties/rightsAndInformalArrangements",
|
|
350
|
+
"/properties/propertyPack/properties/legalBoundaries",
|
|
351
|
+
],
|
|
352
|
+
},
|
|
310
353
|
};
|
|
311
354
|
|
|
312
355
|
// Function to extract properties at specific paths with given ref type
|
|
@@ -45,6 +45,8 @@ const extractFields = [
|
|
|
45
45
|
"sef25",
|
|
46
46
|
];
|
|
47
47
|
|
|
48
|
+
const metadataFields = [...extractFields, "ta6ed6Compat"];
|
|
49
|
+
|
|
48
50
|
const flattenSkeleton = (schema) => {
|
|
49
51
|
if (!schema) return undefined;
|
|
50
52
|
|
|
@@ -258,26 +260,26 @@ extractFields.forEach((key) => {
|
|
|
258
260
|
|
|
259
261
|
const coreSchema = deleteProperties(combinedSchema, [
|
|
260
262
|
"discriminator",
|
|
261
|
-
...
|
|
262
|
-
...
|
|
263
|
-
...
|
|
264
|
-
...
|
|
265
|
-
...
|
|
266
|
-
...
|
|
267
|
-
...
|
|
268
|
-
...
|
|
269
|
-
...
|
|
270
|
-
...
|
|
271
|
-
...
|
|
272
|
-
...
|
|
273
|
-
...
|
|
274
|
-
...
|
|
275
|
-
...
|
|
276
|
-
...
|
|
277
|
-
...
|
|
278
|
-
...
|
|
279
|
-
...
|
|
280
|
-
...
|
|
263
|
+
...metadataFields.map((item) => `${item}Ref`),
|
|
264
|
+
...metadataFields.map((item) => `${item}Required`),
|
|
265
|
+
...metadataFields.map((item) => `${item}Title`),
|
|
266
|
+
...metadataFields.map((item) => `${item}Description`),
|
|
267
|
+
...metadataFields.map((item) => `${item}Enum`),
|
|
268
|
+
...metadataFields.map((item) => `${item}Type`),
|
|
269
|
+
...metadataFields.map((item) => `${item}MinLength`),
|
|
270
|
+
...metadataFields.map((item) => `${item}MaxLength`),
|
|
271
|
+
...metadataFields.map((item) => `${item}Pattern`),
|
|
272
|
+
...metadataFields.map((item) => `${item}Minimum`),
|
|
273
|
+
...metadataFields.map((item) => `${item}Maximum`),
|
|
274
|
+
...metadataFields.map((item) => `${item}MinItems`),
|
|
275
|
+
...metadataFields.map((item) => `${item}MaxItems`),
|
|
276
|
+
...metadataFields.map((item) => `${item}UniqueItems`),
|
|
277
|
+
...metadataFields.map((item) => `${item}AdditionalProperties`),
|
|
278
|
+
...metadataFields.map((item) => `${item}Format`),
|
|
279
|
+
...metadataFields.map((item) => `${item}MinProperties`),
|
|
280
|
+
...metadataFields.map((item) => `${item}MaxProperties`),
|
|
281
|
+
...metadataFields.map((item) => `${item}MultipleOf`),
|
|
282
|
+
...metadataFields.map((item) => `${item}Const`),
|
|
281
283
|
]);
|
|
282
284
|
|
|
283
285
|
fs.writeFileSync(
|