@pdtf/schemas 3.2.1-9 → 3.4.0-1
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 +61 -4
- package/package.json +1 -1
- package/src/examples/v3/exampleCustomOverlay.json +55 -0
- package/src/examples/v3/exampleCustomOverlay2.json +93 -0
- package/src/examples/v3/exampleTransaction.json +55 -1
- package/src/schemas/v3/combined.json +1125 -342
- package/src/schemas/v3/overlays/baspi4.json +1 -0
- package/src/schemas/v3/overlays/baspi5.json +1 -0
- package/src/schemas/v3/overlays/con29R.json +1 -1
- package/src/schemas/v3/overlays/lpe1.json +6 -1
- package/src/schemas/v3/overlays/nts.json +8 -2
- package/src/schemas/v3/overlays/{nts-sef.json → nts2.json} +585 -222
- package/src/schemas/v3/overlays/ntsl2.json +1965 -0
- package/src/schemas/v3/overlays/piq.json +1 -0
- package/src/schemas/v3/overlays/rds.json +1 -0
- package/src/schemas/v3/overlays/sr24.json +13 -0
- package/src/schemas/v3/overlays/ta6.json +1 -0
- package/src/schemas/v3/pdtf-transaction.json +2032 -6089
- package/src/schemas/v3/skeleton.json +58 -3
- package/src/tests/v3/transactionSchema.test.js +244 -0
- package/src/utils/extractOverlay.js +2 -1
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"organisationReference": {},
|
|
31
31
|
"role": {},
|
|
32
32
|
"externalIds": {},
|
|
33
|
+
"participantStatus": {},
|
|
33
34
|
"sellersCapacity": {
|
|
34
35
|
"capacity": {},
|
|
35
36
|
"sellersCapacityDetails": {},
|
|
@@ -214,6 +215,11 @@
|
|
|
214
215
|
"titleNumber": {},
|
|
215
216
|
"titlePropertyDescription": {},
|
|
216
217
|
"ownershipType": {},
|
|
218
|
+
"estateRentcharges": {
|
|
219
|
+
"yesNo": {},
|
|
220
|
+
"details": {},
|
|
221
|
+
"amount": {}
|
|
222
|
+
},
|
|
217
223
|
"wholeFreeholdForSale": {
|
|
218
224
|
"yesNo": {},
|
|
219
225
|
"details": {}
|
|
@@ -1066,6 +1072,7 @@
|
|
|
1066
1072
|
"typeOfConstruction": {
|
|
1067
1073
|
"isStandardForm": {
|
|
1068
1074
|
"yesNo": {},
|
|
1075
|
+
"constructionType": {},
|
|
1069
1076
|
"details": {}
|
|
1070
1077
|
},
|
|
1071
1078
|
"buildingSafety": {
|
|
@@ -2337,6 +2344,10 @@
|
|
|
2337
2344
|
"hasBeenFlooded": {},
|
|
2338
2345
|
"typeOfFlooding": {},
|
|
2339
2346
|
"details": {}
|
|
2347
|
+
},
|
|
2348
|
+
"floodDefences": {
|
|
2349
|
+
"hasFloodDefences": {},
|
|
2350
|
+
"details": {}
|
|
2340
2351
|
}
|
|
2341
2352
|
},
|
|
2342
2353
|
"radon": {
|
|
@@ -2816,7 +2827,12 @@
|
|
|
2816
2827
|
},
|
|
2817
2828
|
"saleReadyDeclarations": {
|
|
2818
2829
|
"authorisedToActOnBehalfOfAllSellers": {},
|
|
2819
|
-
"authorisationToShare": {}
|
|
2830
|
+
"authorisationToShare": {},
|
|
2831
|
+
"sellerSignatures": {
|
|
2832
|
+
"name": {},
|
|
2833
|
+
"signedOn": {},
|
|
2834
|
+
"externalIds": {}
|
|
2835
|
+
}
|
|
2820
2836
|
},
|
|
2821
2837
|
"localSearches": {
|
|
2822
2838
|
"localLandCharges": {
|
|
@@ -3181,7 +3197,7 @@
|
|
|
3181
3197
|
"yesNo": {},
|
|
3182
3198
|
"details": {}
|
|
3183
3199
|
},
|
|
3184
|
-
"
|
|
3200
|
+
"floodAndCoastalErosionRiskManagement": {
|
|
3185
3201
|
"yesNo": {},
|
|
3186
3202
|
"details": {}
|
|
3187
3203
|
}
|
|
@@ -4089,5 +4105,44 @@
|
|
|
4089
4105
|
"pricingMethodology": {}
|
|
4090
4106
|
}
|
|
4091
4107
|
}
|
|
4108
|
+
},
|
|
4109
|
+
"contracts": {
|
|
4110
|
+
"contract": {
|
|
4111
|
+
"template": {
|
|
4112
|
+
"name": {},
|
|
4113
|
+
"url": {},
|
|
4114
|
+
"externalIds": {},
|
|
4115
|
+
"requiredSignatories": {
|
|
4116
|
+
"role": {},
|
|
4117
|
+
"requiresAll": {}
|
|
4118
|
+
}
|
|
4119
|
+
},
|
|
4120
|
+
"terms": {}
|
|
4121
|
+
},
|
|
4122
|
+
"signatures": {
|
|
4123
|
+
"name": {},
|
|
4124
|
+
"signedOn": {},
|
|
4125
|
+
"externalIds": {}
|
|
4126
|
+
}
|
|
4127
|
+
},
|
|
4128
|
+
"chain": {
|
|
4129
|
+
"onwardPurchase": {
|
|
4130
|
+
"transactionId": {},
|
|
4131
|
+
"externalIds": {},
|
|
4132
|
+
"address": {
|
|
4133
|
+
"buildingNumber": {},
|
|
4134
|
+
"buildingName": {},
|
|
4135
|
+
"subBuilding": {},
|
|
4136
|
+
"street": {},
|
|
4137
|
+
"line1": {},
|
|
4138
|
+
"line2": {},
|
|
4139
|
+
"line 3": {},
|
|
4140
|
+
"town": {},
|
|
4141
|
+
"postcode": {},
|
|
4142
|
+
"homeNation": {}
|
|
4143
|
+
},
|
|
4144
|
+
"uprn": {},
|
|
4145
|
+
"sellingAgent": {}
|
|
4146
|
+
}
|
|
4092
4147
|
}
|
|
4093
|
-
}
|
|
4148
|
+
}
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
getValidator,
|
|
9
9
|
getSubschemaValidator,
|
|
10
10
|
getTitleAtPath,
|
|
11
|
+
overlaysMap,
|
|
11
12
|
} = require("../../../index.js");
|
|
12
13
|
|
|
13
14
|
const exampleTransaction = require("../../examples/v3/exampleTransaction.json");
|
|
@@ -90,6 +91,18 @@ test("sample with missing dependent required fields is invalid", () => {
|
|
|
90
91
|
expect(isValid).toBe(false);
|
|
91
92
|
});
|
|
92
93
|
|
|
94
|
+
test("sample with missing dependent required fields with NTS overlay is invalid", () => {
|
|
95
|
+
// Previously a required field was missing from the NTS overlay
|
|
96
|
+
const clonedExampleTransaction = JSON.parse(
|
|
97
|
+
JSON.stringify(exampleTransaction)
|
|
98
|
+
);
|
|
99
|
+
clonedExampleTransaction.propertyPack.ownership.ownershipsToBeTransferred[0].leaseholdInformation =
|
|
100
|
+
undefined;
|
|
101
|
+
const validator = getValidator(schemaId, ["nts2023"]);
|
|
102
|
+
const isValid = validator(clonedExampleTransaction);
|
|
103
|
+
expect(isValid).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
93
106
|
test("correctly gets a top level subschema", () => {
|
|
94
107
|
const subschema = getSubschema("/propertyPack");
|
|
95
108
|
expect(subschema.title).toBe("Property Pack");
|
|
@@ -396,3 +409,234 @@ test("correctly gets titles across schemas, arrays and non-existient title prope
|
|
|
396
409
|
)
|
|
397
410
|
).toBe("Please provide their full names and ages.");
|
|
398
411
|
});
|
|
412
|
+
test("validates a valid contract", () => {
|
|
413
|
+
const path = "/contracts";
|
|
414
|
+
const data = jp.get(exampleTransaction, path);
|
|
415
|
+
const validator = getSubschemaValidator(path, exampleTransaction.$schema);
|
|
416
|
+
const isValid = validator(data);
|
|
417
|
+
expect(isValid).toBe(true);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("overlay as key and as object produce identical results", () => {
|
|
421
|
+
// Get the NTS overlay object directly from the overlays map
|
|
422
|
+
const ntsOverlayObject = { ...overlaysMap[schemaId]["nts2023"] };
|
|
423
|
+
// Remove the $id to prevent schema registration conflicts
|
|
424
|
+
ntsOverlayObject.$id =
|
|
425
|
+
"https://trust.propdata.org.uk/schemas/v3/overlays/nts2024.json";
|
|
426
|
+
|
|
427
|
+
// Get schema using the key
|
|
428
|
+
const schemaWithKey = getTransactionSchema(schemaId, ["nts2023"]);
|
|
429
|
+
|
|
430
|
+
// Get schema using the object
|
|
431
|
+
const schemaWithObject = getTransactionSchema(schemaId, [ntsOverlayObject]);
|
|
432
|
+
|
|
433
|
+
// Create copies without $id for comparison
|
|
434
|
+
const schemaWithKeyNoId = { ...schemaWithKey };
|
|
435
|
+
const schemaWithObjectNoId = { ...schemaWithObject };
|
|
436
|
+
delete schemaWithKeyNoId.$id;
|
|
437
|
+
delete schemaWithObjectNoId.$id;
|
|
438
|
+
|
|
439
|
+
// Deep compare the results (excluding $id)
|
|
440
|
+
expect(schemaWithObjectNoId).toEqual(schemaWithKeyNoId);
|
|
441
|
+
|
|
442
|
+
// Test that validation produces the same errors
|
|
443
|
+
const validatorWithKey = getValidator(schemaId, ["nts2023"]);
|
|
444
|
+
const validatorWithObject = getValidator(schemaId, [ntsOverlayObject]);
|
|
445
|
+
|
|
446
|
+
validatorWithKey(exampleTransaction);
|
|
447
|
+
validatorWithObject(exampleTransaction);
|
|
448
|
+
|
|
449
|
+
// Compare validation errors
|
|
450
|
+
expect(validatorWithObject.errors).toEqual(validatorWithKey.errors);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("correctly merges custom Japanese Knotweed overlay with base schema", () => {
|
|
454
|
+
// Load the custom overlay
|
|
455
|
+
const customOverlay = require("../../examples/v3/exampleCustomOverlay.json");
|
|
456
|
+
|
|
457
|
+
// Get base schema and schema with overlay
|
|
458
|
+
const baseSchema = getTransactionSchema(schemaId);
|
|
459
|
+
const schemaWithOverlay = getTransactionSchema(schemaId, [customOverlay]);
|
|
460
|
+
|
|
461
|
+
// Verify that other properties in propertyPack are preserved
|
|
462
|
+
expect(
|
|
463
|
+
Object.keys(schemaWithOverlay.properties.propertyPack.properties)
|
|
464
|
+
).toEqual(Object.keys(baseSchema.properties.propertyPack.properties));
|
|
465
|
+
|
|
466
|
+
// Verify that other properties in specialistIssues are preserved
|
|
467
|
+
const baseSpecialistIssues =
|
|
468
|
+
baseSchema.properties.propertyPack.properties.specialistIssues;
|
|
469
|
+
const overlaidSpecialistIssues =
|
|
470
|
+
schemaWithOverlay.properties.propertyPack.properties.specialistIssues;
|
|
471
|
+
expect(Object.keys(overlaidSpecialistIssues.properties)).toEqual(
|
|
472
|
+
Object.keys(baseSpecialistIssues.properties)
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// Verify that Japanese Knotweed section is updated
|
|
476
|
+
const japaneseKnotweed = overlaidSpecialistIssues.properties.japaneseKnotweed;
|
|
477
|
+
expect(japaneseKnotweed.title).toBe(
|
|
478
|
+
"Is the property affected by Japanese knotweed?"
|
|
479
|
+
);
|
|
480
|
+
expect(japaneseKnotweed.required).toEqual(["yesNo"]);
|
|
481
|
+
|
|
482
|
+
// Verify the oneOf conditions are updated
|
|
483
|
+
expect(japaneseKnotweed.oneOf).toHaveLength(2);
|
|
484
|
+
expect(japaneseKnotweed.oneOf[0].properties.yesNo.enum).toEqual([
|
|
485
|
+
"No",
|
|
486
|
+
"Not known",
|
|
487
|
+
]);
|
|
488
|
+
expect(japaneseKnotweed.oneOf[1].properties.yesNo.enum).toEqual(["Yes"]);
|
|
489
|
+
expect(japaneseKnotweed.oneOf[1].required).toEqual([
|
|
490
|
+
"managementPlanInPlace",
|
|
491
|
+
"attachments",
|
|
492
|
+
]);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test("correctly merges multiple overlays using both key and object references", () => {
|
|
496
|
+
// Load the custom overlay
|
|
497
|
+
const customOverlay = require("../../examples/v3/exampleCustomOverlay.json");
|
|
498
|
+
|
|
499
|
+
// Get schema with both overlays
|
|
500
|
+
const schemaWithOverlays = getTransactionSchema(schemaId, [
|
|
501
|
+
"nts2023",
|
|
502
|
+
customOverlay,
|
|
503
|
+
]);
|
|
504
|
+
|
|
505
|
+
// Get individual overlay schemas for comparison
|
|
506
|
+
const ntsSchema = getTransactionSchema(schemaId, ["nts2023"]);
|
|
507
|
+
const customSchema = getTransactionSchema(schemaId, [customOverlay]);
|
|
508
|
+
|
|
509
|
+
// Verify propertyPack structure is preserved
|
|
510
|
+
const propertyPack = schemaWithOverlays.properties.propertyPack;
|
|
511
|
+
expect(Object.keys(propertyPack.properties)).toEqual(
|
|
512
|
+
expect.arrayContaining(
|
|
513
|
+
Object.keys(ntsSchema.properties.propertyPack.properties)
|
|
514
|
+
)
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
// Verify Japanese Knotweed changes from custom overlay are present
|
|
518
|
+
const japaneseKnotweed =
|
|
519
|
+
propertyPack.properties.specialistIssues.properties.japaneseKnotweed;
|
|
520
|
+
expect(japaneseKnotweed.title).toBe(
|
|
521
|
+
"Is the property affected by Japanese knotweed?"
|
|
522
|
+
);
|
|
523
|
+
expect(japaneseKnotweed.oneOf[1].required).toEqual([
|
|
524
|
+
"managementPlanInPlace",
|
|
525
|
+
"attachments",
|
|
526
|
+
]);
|
|
527
|
+
|
|
528
|
+
// Verify customRef properties are correctly merged
|
|
529
|
+
expect(propertyPack.customRef).toBe("JKW");
|
|
530
|
+
expect(propertyPack.properties.specialistIssues.customRef).toBe("JKW.7");
|
|
531
|
+
expect(japaneseKnotweed.customRef).toBe("JKW.7.8");
|
|
532
|
+
expect(
|
|
533
|
+
japaneseKnotweed.oneOf[1].properties.managementPlanInPlace.customRef
|
|
534
|
+
).toBe("JKW.7.8.2");
|
|
535
|
+
expect(japaneseKnotweed.oneOf[1].properties.attachments.customRef).toBe(
|
|
536
|
+
"JKW.7.8.3"
|
|
537
|
+
);
|
|
538
|
+
expect(japaneseKnotweed.properties.yesNo.customRef).toBe("JKW.7.8.1");
|
|
539
|
+
|
|
540
|
+
// Verify NTS specific changes are present (e.g., simplified enums)
|
|
541
|
+
const mainsElectricity =
|
|
542
|
+
propertyPack.properties.electricity.properties.mainsElectricity;
|
|
543
|
+
expect(mainsElectricity.properties.yesNo.enum).toEqual(["Yes", "No"]);
|
|
544
|
+
|
|
545
|
+
// Verify required fields from both overlays are merged
|
|
546
|
+
expect(propertyPack.required).toEqual(
|
|
547
|
+
expect.arrayContaining([
|
|
548
|
+
...ntsSchema.properties.propertyPack.required,
|
|
549
|
+
...customSchema.properties.propertyPack.required,
|
|
550
|
+
])
|
|
551
|
+
);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test("correctly merges NTS and both custom overlays with proper required fields", () => {
|
|
555
|
+
// Load both custom overlays
|
|
556
|
+
const japaneseKnoweedOverlay = require("../../examples/v3/exampleCustomOverlay.json");
|
|
557
|
+
const managingAgentOverlay = require("../../examples/v3/exampleCustomOverlay2.json");
|
|
558
|
+
|
|
559
|
+
// Get schema with all overlays
|
|
560
|
+
const schemaWithOverlays = getTransactionSchema(schemaId, [
|
|
561
|
+
"nts2023",
|
|
562
|
+
japaneseKnoweedOverlay,
|
|
563
|
+
managingAgentOverlay,
|
|
564
|
+
]);
|
|
565
|
+
|
|
566
|
+
// Navigate to the leasehold section in the oneOf array
|
|
567
|
+
const leaseholdSchema =
|
|
568
|
+
schemaWithOverlays.properties.propertyPack.properties.ownership.properties.ownershipsToBeTransferred.items.oneOf.find(
|
|
569
|
+
(schema) => schema.properties.ownershipType.enum[0] === "Leasehold"
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
// Verify the leasehold information structure
|
|
573
|
+
const leaseholdInfo = leaseholdSchema.properties.leaseholdInformation;
|
|
574
|
+
|
|
575
|
+
// Check required fields - should include both NTS and custom overlay requirements
|
|
576
|
+
expect(leaseholdInfo.required).toContain("contactDetails");
|
|
577
|
+
expect(leaseholdInfo.required).toContain("leaseTerm"); // from NTS
|
|
578
|
+
expect(leaseholdInfo.required).toContain("groundRent"); // from NTS
|
|
579
|
+
|
|
580
|
+
// Verify managing agent contact structure and refs
|
|
581
|
+
const managingAgent =
|
|
582
|
+
leaseholdInfo.properties.contactDetails.properties.contacts.properties
|
|
583
|
+
.managingAgent;
|
|
584
|
+
expect(managingAgent.macRef).toBe("MAC.4.1c");
|
|
585
|
+
|
|
586
|
+
// Verify contact details structure
|
|
587
|
+
const contact = managingAgent.properties.contact;
|
|
588
|
+
expect(contact.properties.nameOrOrganisation.macRef).toBe("MAC.4.1c.1");
|
|
589
|
+
expect(contact.properties.address.macRef).toBe("MAC.4.1c.0");
|
|
590
|
+
expect(contact.properties.telephone.macRef).toBe("MAC.4.1c.7");
|
|
591
|
+
expect(contact.properties.emailAddress.macRef).toBe("MAC.4.1c.8");
|
|
592
|
+
|
|
593
|
+
// Verify Japanese Knotweed section is still present and correct
|
|
594
|
+
const japaneseKnotweed =
|
|
595
|
+
schemaWithOverlays.properties.propertyPack.properties.specialistIssues
|
|
596
|
+
.properties.japaneseKnotweed;
|
|
597
|
+
expect(japaneseKnotweed.customRef).toBe("JKW.7.8");
|
|
598
|
+
|
|
599
|
+
// Verify NTS changes are still present
|
|
600
|
+
const mainsElectricity =
|
|
601
|
+
schemaWithOverlays.properties.propertyPack.properties.electricity.properties
|
|
602
|
+
.mainsElectricity;
|
|
603
|
+
expect(mainsElectricity.properties.yesNo.enum).toEqual(["Yes", "No"]);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
test("ta7 overlay as key and as object produce identical results at leasehold info level", () => {
|
|
607
|
+
// Get the TA7 overlay object directly from the overlays map
|
|
608
|
+
const ta7OverlayObject = { ...overlaysMap[schemaId]["ta7ed3"] };
|
|
609
|
+
// Remove the $id to prevent schema registration conflicts
|
|
610
|
+
ta7OverlayObject.$id =
|
|
611
|
+
"https://trust.propdata.org.uk/schemas/v3/overlays/ta7ed3-copy.json";
|
|
612
|
+
|
|
613
|
+
// Get schemas using both methods
|
|
614
|
+
const schemaWithKey = getTransactionSchema(schemaId, ["ta7ed3"]);
|
|
615
|
+
const schemaWithObject = getTransactionSchema(schemaId, [ta7OverlayObject]);
|
|
616
|
+
|
|
617
|
+
// Navigate to the leasehold information section in both schemas
|
|
618
|
+
const getLeaseholdInfo = (schema) => {
|
|
619
|
+
const ownershipsToBeTransferred =
|
|
620
|
+
schema.properties.propertyPack.properties.ownership.properties
|
|
621
|
+
.ownershipsToBeTransferred;
|
|
622
|
+
const leaseholdSchema = ownershipsToBeTransferred.items.oneOf.find(
|
|
623
|
+
(schema) => schema.properties.ownershipType.enum[0] === "Leasehold"
|
|
624
|
+
);
|
|
625
|
+
return leaseholdSchema.properties.leaseholdInformation;
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
const leaseholdInfoWithKey = getLeaseholdInfo(schemaWithKey);
|
|
629
|
+
const leaseholdInfoWithObject = getLeaseholdInfo(schemaWithObject);
|
|
630
|
+
|
|
631
|
+
// Deep compare the leasehold information sections
|
|
632
|
+
expect(leaseholdInfoWithObject).toEqual(leaseholdInfoWithKey);
|
|
633
|
+
|
|
634
|
+
// Verify specific TA7 requirements are present in both
|
|
635
|
+
expect(leaseholdInfoWithKey.required).toContain("contactDetails");
|
|
636
|
+
expect(leaseholdInfoWithObject.required).toContain("contactDetails");
|
|
637
|
+
|
|
638
|
+
// Verify TA7 refs are present and identical
|
|
639
|
+
const contactDetails = leaseholdInfoWithKey.properties.contactDetails;
|
|
640
|
+
expect(contactDetails.ta7Ref).toBe("4.1");
|
|
641
|
+
expect(leaseholdInfoWithObject.properties.contactDetails.ta7Ref).toBe("4.1");
|
|
642
|
+
});
|