@pdtf/schemas 3.2.1-9 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +127 -3
- 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/nts.json +8 -2
- 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/pdtf-transaction.json +149 -3
- package/src/schemas/v3/skeleton.json +27 -2
- package/src/tests/v3/transactionSchema.test.js +244 -0
package/index.js
CHANGED
|
@@ -61,14 +61,49 @@ const transactionSchemas = {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const combineMerge = (target, source, options) => {
|
|
64
|
+
// Special handling for 'required' arrays - combine them uniquely
|
|
65
|
+
if (target.some((item) => typeof item === "string")) {
|
|
66
|
+
return [...new Set([...target, ...source])];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// For other arrays, concatenate while preserving uniqueness for primitive values
|
|
70
|
+
if (!options.isMergeableObject(target[0])) {
|
|
71
|
+
return [...new Set([...target, ...source])];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Special handling for oneOf arrays - merge matching schemas based on discriminator
|
|
75
|
+
if (target[0]?.properties && source[0]?.properties) {
|
|
76
|
+
// Find a common discriminator property (a property with an enum)
|
|
77
|
+
const findDiscriminator = (schema) => {
|
|
78
|
+
const props = schema.properties;
|
|
79
|
+
return Object.keys(props).find((key) => Array.isArray(props[key]?.enum));
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const discriminator = findDiscriminator(target[0]);
|
|
83
|
+
if (discriminator) {
|
|
84
|
+
return target.map((targetSchema) => {
|
|
85
|
+
const targetEnum = targetSchema.properties?.[discriminator]?.enum || [];
|
|
86
|
+
const matchingSourceSchema = source.find((sourceSchema) => {
|
|
87
|
+
const sourceEnum =
|
|
88
|
+
sourceSchema.properties?.[discriminator]?.enum || [];
|
|
89
|
+
// Check if there's any overlap between the enum values
|
|
90
|
+
return sourceEnum.some((value) => targetEnum.includes(value));
|
|
91
|
+
});
|
|
92
|
+
if (matchingSourceSchema) {
|
|
93
|
+
return merge(targetSchema, matchingSourceSchema, options);
|
|
94
|
+
}
|
|
95
|
+
return targetSchema;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// For arrays of objects, merge by index and append remaining items
|
|
64
101
|
const destination = target.slice();
|
|
65
102
|
source.forEach((item, index) => {
|
|
66
103
|
if (typeof destination[index] === "undefined") {
|
|
67
104
|
destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
|
|
68
105
|
} else if (options.isMergeableObject(item)) {
|
|
69
106
|
destination[index] = merge(target[index], item, options);
|
|
70
|
-
} else if (target.indexOf(item) === -1) {
|
|
71
|
-
destination.push(item);
|
|
72
107
|
}
|
|
73
108
|
});
|
|
74
109
|
return destination;
|
|
@@ -84,7 +119,12 @@ const getTransactionSchema = (
|
|
|
84
119
|
if (!overlays || overlays.length < 1) return sourceSchema;
|
|
85
120
|
let mergedSchema = sourceSchema;
|
|
86
121
|
overlays.forEach((overlay) => {
|
|
87
|
-
|
|
122
|
+
// Handle both string keys and direct overlay objects
|
|
123
|
+
const overlaySchema =
|
|
124
|
+
typeof overlay === "string"
|
|
125
|
+
? overlaysMap[schemaId][overlay] || {}
|
|
126
|
+
: overlay;
|
|
127
|
+
|
|
88
128
|
mergedSchema = merge(mergedSchema, overlaySchema, {
|
|
89
129
|
customMerge: (key) => {
|
|
90
130
|
if (key === "enum") {
|
|
@@ -97,6 +137,23 @@ const getTransactionSchema = (
|
|
|
97
137
|
return mergedSchema;
|
|
98
138
|
};
|
|
99
139
|
|
|
140
|
+
// Add helper function to generate cache key
|
|
141
|
+
const generateOverlayKey = (overlays) => {
|
|
142
|
+
if (!overlays) return "";
|
|
143
|
+
return overlays
|
|
144
|
+
.map((overlay) => {
|
|
145
|
+
if (typeof overlay === "string") return overlay;
|
|
146
|
+
// Generate a simple hash of the object for caching
|
|
147
|
+
return JSON.stringify(overlay)
|
|
148
|
+
.split("")
|
|
149
|
+
.reduce((hash, char) => {
|
|
150
|
+
return ((hash << 5) - hash + char.charCodeAt(0)) | 0;
|
|
151
|
+
}, 0)
|
|
152
|
+
.toString(36);
|
|
153
|
+
})
|
|
154
|
+
.join(".");
|
|
155
|
+
};
|
|
156
|
+
|
|
100
157
|
const getValidator = (schemaId, overlays) => {
|
|
101
158
|
return getSubschemaValidator("", schemaId, overlays);
|
|
102
159
|
};
|
|
@@ -135,7 +192,7 @@ const isPathValid = (path, schemaId, overlays) => {
|
|
|
135
192
|
|
|
136
193
|
const getSubschemaValidator = (path, schemaId, overlays) => {
|
|
137
194
|
const subSchema = getSubschema(path, schemaId, overlays);
|
|
138
|
-
const overlayKey = (overlays
|
|
195
|
+
const overlayKey = generateOverlayKey(overlays);
|
|
139
196
|
// see if we can retrieve the schema by path, schemaId and overlays
|
|
140
197
|
const cacheKey = `${path}-${schemaId}-${overlayKey}`;
|
|
141
198
|
let validator = ajv.getSchema(cacheKey);
|
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://trust.propdata.org.uk/schemas/v3/overlays/custom.json",
|
|
4
|
+
"properties": {
|
|
5
|
+
"propertyPack": {
|
|
6
|
+
"customRef": "JKW",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": ["specialistIssues"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"specialistIssues": {
|
|
11
|
+
"customRef": "JKW.7",
|
|
12
|
+
"required": ["japaneseKnotweed"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"japaneseKnotweed": {
|
|
15
|
+
"customRef": "JKW.7.8",
|
|
16
|
+
"discriminator": {
|
|
17
|
+
"propertyName": "yesNo"
|
|
18
|
+
},
|
|
19
|
+
"oneOf": [
|
|
20
|
+
{
|
|
21
|
+
"properties": {
|
|
22
|
+
"yesNo": {
|
|
23
|
+
"enum": ["No", "Not known"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"properties": {
|
|
29
|
+
"yesNo": {
|
|
30
|
+
"enum": ["Yes"]
|
|
31
|
+
},
|
|
32
|
+
"managementPlanInPlace": {
|
|
33
|
+
"customRef": "JKW.7.8.2"
|
|
34
|
+
},
|
|
35
|
+
"attachments": {
|
|
36
|
+
"customRef": "JKW.7.8.3"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["managementPlanInPlace", "attachments"]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"required": ["yesNo"],
|
|
43
|
+
"title": "Is the property affected by Japanese knotweed?",
|
|
44
|
+
"properties": {
|
|
45
|
+
"yesNo": {
|
|
46
|
+
"customRef": "JKW.7.8.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://trust.propdata.org.uk/schemas/v3/overlays/ta7.json",
|
|
4
|
+
"properties": {
|
|
5
|
+
"propertyPack": {
|
|
6
|
+
"macRef": "MAC",
|
|
7
|
+
"required": ["ownership"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"ownership": {
|
|
10
|
+
"macRef": "MAC.1",
|
|
11
|
+
"required": ["ownershipsToBeTransferred"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"ownershipsToBeTransferred": {
|
|
14
|
+
"macRef": "MAC.1",
|
|
15
|
+
"items": {
|
|
16
|
+
"discriminator": {
|
|
17
|
+
"propertyName": "ownershipType"
|
|
18
|
+
},
|
|
19
|
+
"oneOf": [
|
|
20
|
+
{
|
|
21
|
+
"properties": {
|
|
22
|
+
"ownershipType": {
|
|
23
|
+
"enum": ["Leasehold"]
|
|
24
|
+
},
|
|
25
|
+
"leaseholdInformation": {
|
|
26
|
+
"required": ["contactDetails"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"contactDetails": {
|
|
29
|
+
"macRef": "MAC.4.1",
|
|
30
|
+
"required": ["contacts"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"contacts": {
|
|
33
|
+
"macRef": "MAC.4.1",
|
|
34
|
+
"required": ["managingAgent"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"managingAgent": {
|
|
37
|
+
"macRef": "MAC.4.1c",
|
|
38
|
+
"required": ["contact"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"contact": {
|
|
41
|
+
"macRef": "MAC.4.1c",
|
|
42
|
+
"required": ["nameOrOrganisation"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"nameOrOrganisation": {
|
|
45
|
+
"macRef": "MAC.4.1c.1"
|
|
46
|
+
},
|
|
47
|
+
"address": {
|
|
48
|
+
"macRef": "MAC.4.1c.0",
|
|
49
|
+
"properties": {
|
|
50
|
+
"line1": {
|
|
51
|
+
"macRef": "MAC.4.1c.2"
|
|
52
|
+
},
|
|
53
|
+
"line2": {
|
|
54
|
+
"macRef": "MAC.4.1c.3"
|
|
55
|
+
},
|
|
56
|
+
"line3": {
|
|
57
|
+
"macRef": "MAC.4.1c.4"
|
|
58
|
+
},
|
|
59
|
+
"town": {
|
|
60
|
+
"macRef": "MAC.4.1c.5"
|
|
61
|
+
},
|
|
62
|
+
"postcode": {
|
|
63
|
+
"macRef": "MAC.4.1c.6"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"telephone": {
|
|
68
|
+
"macRef": "MAC.4.1c.7"
|
|
69
|
+
},
|
|
70
|
+
"emailAddress": {
|
|
71
|
+
"macRef": "MAC.4.1c.8"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -976,5 +976,59 @@
|
|
|
976
976
|
"contentTypes": ["Environmental Risks"]
|
|
977
977
|
}
|
|
978
978
|
]
|
|
979
|
-
}
|
|
979
|
+
},
|
|
980
|
+
"contracts": [
|
|
981
|
+
{
|
|
982
|
+
"contract": {
|
|
983
|
+
"template": {
|
|
984
|
+
"name": "Example Agency Agreement",
|
|
985
|
+
"url": "https://example.com/example-agency-agreement",
|
|
986
|
+
"externalIds": {
|
|
987
|
+
"Moverly": "file123"
|
|
988
|
+
},
|
|
989
|
+
"requiredSignatories": [
|
|
990
|
+
{
|
|
991
|
+
"role": "Seller",
|
|
992
|
+
"requiresAll": true
|
|
993
|
+
}
|
|
994
|
+
]
|
|
995
|
+
},
|
|
996
|
+
"terms": {
|
|
997
|
+
"propertyAddress": "123 Maple Street, London, SW1A 1AA",
|
|
998
|
+
"sellersNames": "Peter and Clarissa Hetherington-Smythe",
|
|
999
|
+
"sellersContactAddress": "47 Park Road, Harpenden, AL3 5AF",
|
|
1000
|
+
"sellersTelephoneNumber": "07700 900123",
|
|
1001
|
+
"sellersEmail": "peter.hetherington-smyth@email.com",
|
|
1002
|
+
"typeOfAgency": "Sole Agency",
|
|
1003
|
+
"feeAsPercent": "1.5%",
|
|
1004
|
+
"fixedFeeExVat": "£2,500",
|
|
1005
|
+
"estimatedFeeIncludingVat": "£3,000",
|
|
1006
|
+
"includeAdditionalChargeEpc": true,
|
|
1007
|
+
"additionalChargeForEpcExVat": "75",
|
|
1008
|
+
"includeAdditionalChargeForEpcAndFloorplan": true,
|
|
1009
|
+
"additionalChargeForEpcAndFloorplanExVat": "150",
|
|
1010
|
+
"includeAdditionalChargeForEpcFloorplanAndPhotos": true,
|
|
1011
|
+
"additionalChargeForEpcFloorplanAndPhotosExVat": "150",
|
|
1012
|
+
"includeAdditionalChargeForPremiumPhotos ": true,
|
|
1013
|
+
"additionalChargeForPremiumPhotosExVat": "150",
|
|
1014
|
+
"includeAdditionalChargeForBrochure": true,
|
|
1015
|
+
"additionalChargeForBrochureExVat": "150",
|
|
1016
|
+
"includeAdditionalChargeForBespokeAdvertising": true,
|
|
1017
|
+
"additionalChargeForBespokeAdvertisingExVat": "150",
|
|
1018
|
+
"initialMarketingPrice": "£450,000"
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
"signatures": [
|
|
1022
|
+
{
|
|
1023
|
+
"name": "Peter Ronald Hetherington-Smythe",
|
|
1024
|
+
"signedOn": "2024-11-29T16:15:02.735Z",
|
|
1025
|
+
"externalIds": {
|
|
1026
|
+
"Moverly": "123434"
|
|
1027
|
+
},
|
|
1028
|
+
"role": "Seller",
|
|
1029
|
+
"contractHash": "IYnBjUI8TauXFop6WCvnPA=="
|
|
1030
|
+
}
|
|
1031
|
+
]
|
|
1032
|
+
}
|
|
1033
|
+
]
|
|
980
1034
|
}
|
|
@@ -185,6 +185,9 @@
|
|
|
185
185
|
},
|
|
186
186
|
"externalIds": {
|
|
187
187
|
"type": "object"
|
|
188
|
+
},
|
|
189
|
+
"isRemoved": {
|
|
190
|
+
"type": "boolean"
|
|
188
191
|
}
|
|
189
192
|
},
|
|
190
193
|
"discriminator": {
|
|
@@ -235,6 +238,7 @@
|
|
|
235
238
|
"Personal Representative for a Deceased Owner",
|
|
236
239
|
"Under Power of Attorney",
|
|
237
240
|
"Mortgagee in Possession",
|
|
241
|
+
"Assistant",
|
|
238
242
|
"Other"
|
|
239
243
|
]
|
|
240
244
|
}
|
|
@@ -271,6 +275,7 @@
|
|
|
271
275
|
"enum": [
|
|
272
276
|
"Personal Representative for a Deceased Owner",
|
|
273
277
|
"Under Power of Attorney",
|
|
278
|
+
"Assistant",
|
|
274
279
|
"Other"
|
|
275
280
|
]
|
|
276
281
|
},
|
|
@@ -1372,7 +1377,9 @@
|
|
|
1372
1377
|
"Trunk road/motorway",
|
|
1373
1378
|
"Airport",
|
|
1374
1379
|
"Helipad",
|
|
1375
|
-
"Other"
|
|
1380
|
+
"Other",
|
|
1381
|
+
"Tramway",
|
|
1382
|
+
"Underground/Subway"
|
|
1376
1383
|
]
|
|
1377
1384
|
},
|
|
1378
1385
|
"latitude": {
|
|
@@ -3949,6 +3956,7 @@
|
|
|
3949
3956
|
}
|
|
3950
3957
|
}
|
|
3951
3958
|
},
|
|
3959
|
+
"ntsRequired": ["managedFreeholdOrCommonholdInformation"],
|
|
3952
3960
|
"fme1Required": ["managedFreeholdOrCommonholdInformation"],
|
|
3953
3961
|
"baspi4Required": [
|
|
3954
3962
|
"managedFreeholdOrCommonholdInformation"
|
|
@@ -8949,6 +8957,7 @@
|
|
|
8949
8957
|
}
|
|
8950
8958
|
}
|
|
8951
8959
|
},
|
|
8960
|
+
"ntsRequired": ["leaseholdInformation"],
|
|
8952
8961
|
"lpe1Required": ["leaseholdInformation"],
|
|
8953
8962
|
"baspi4Required": ["leaseholdInformation"],
|
|
8954
8963
|
"baspi5Required": ["leaseholdInformation"],
|
|
@@ -26332,6 +26341,32 @@
|
|
|
26332
26341
|
"sr24Ref": "1.2",
|
|
26333
26342
|
"title": "I/we consent to the sharing of the information contained in the Sale Ready pack with the my/our legal representative and the buyer(s) and their legal representatives",
|
|
26334
26343
|
"type": "boolean"
|
|
26344
|
+
},
|
|
26345
|
+
"sellerSignatures": {
|
|
26346
|
+
"sr24Ref": "1.3",
|
|
26347
|
+
"title": "I confirm that all information provided is accurate to the best of my knowledge and if I become aware of any change to/changes which alter the information supplied/provided prior to exchange of contracts for the sale of the property, I will immediately notify the person marketing the property as well as my property lawyer.",
|
|
26348
|
+
"type": "array",
|
|
26349
|
+
"items": {
|
|
26350
|
+
"type": "object",
|
|
26351
|
+
"required": ["name", "signedOn"],
|
|
26352
|
+
"properties": {
|
|
26353
|
+
"name": {
|
|
26354
|
+
"sr24Ref": "1.3.1",
|
|
26355
|
+
"title": "Name of signatory",
|
|
26356
|
+
"type": "string",
|
|
26357
|
+
"minLength": 1
|
|
26358
|
+
},
|
|
26359
|
+
"signedOn": {
|
|
26360
|
+
"sr24Ref": "1.3.2",
|
|
26361
|
+
"title": "Date and time of signature in ISO 8601 format",
|
|
26362
|
+
"type": "string",
|
|
26363
|
+
"format": "date-time"
|
|
26364
|
+
},
|
|
26365
|
+
"externalIds": {
|
|
26366
|
+
"type": "object"
|
|
26367
|
+
}
|
|
26368
|
+
}
|
|
26369
|
+
}
|
|
26335
26370
|
}
|
|
26336
26371
|
}
|
|
26337
26372
|
},
|
|
@@ -28903,9 +28938,9 @@
|
|
|
28903
28938
|
}
|
|
28904
28939
|
]
|
|
28905
28940
|
},
|
|
28906
|
-
"
|
|
28941
|
+
"floodAndCoastalErosionRiskManagement": {
|
|
28907
28942
|
"con29RRef": "3.7(g)",
|
|
28908
|
-
"title": "flood and
|
|
28943
|
+
"title": "flood and coastal erosion risk management",
|
|
28909
28944
|
"type": "object",
|
|
28910
28945
|
"properties": {
|
|
28911
28946
|
"yesNo": {
|
|
@@ -39285,6 +39320,95 @@
|
|
|
39285
39320
|
}
|
|
39286
39321
|
}
|
|
39287
39322
|
}
|
|
39323
|
+
},
|
|
39324
|
+
"contracts": {
|
|
39325
|
+
"type": "array",
|
|
39326
|
+
"items": {
|
|
39327
|
+
"type": "object",
|
|
39328
|
+
"properties": {
|
|
39329
|
+
"contract": {
|
|
39330
|
+
"type": "object",
|
|
39331
|
+
"required": ["template", "terms"],
|
|
39332
|
+
"properties": {
|
|
39333
|
+
"template": {
|
|
39334
|
+
"type": "object",
|
|
39335
|
+
"required": ["name", "url", "requiredSignatories"],
|
|
39336
|
+
"properties": {
|
|
39337
|
+
"name": { "type": "string" },
|
|
39338
|
+
"url": { "type": "string", "format": "uri" },
|
|
39339
|
+
"externalIds": {
|
|
39340
|
+
"type": "object"
|
|
39341
|
+
},
|
|
39342
|
+
"requiredSignatories": {
|
|
39343
|
+
"type": "array",
|
|
39344
|
+
"items": {
|
|
39345
|
+
"type": "object",
|
|
39346
|
+
"properties": {
|
|
39347
|
+
"role": {
|
|
39348
|
+
"type": "string",
|
|
39349
|
+
"enum": [
|
|
39350
|
+
"Seller",
|
|
39351
|
+
"Seller's Conveyancer",
|
|
39352
|
+
"Prospective Buyer",
|
|
39353
|
+
"Buyer",
|
|
39354
|
+
"Buyer's Conveyancer",
|
|
39355
|
+
"Estate Agent",
|
|
39356
|
+
"Buyer's Agent",
|
|
39357
|
+
"Surveyor",
|
|
39358
|
+
"Mortgage Broker",
|
|
39359
|
+
"Lender",
|
|
39360
|
+
"Landlord",
|
|
39361
|
+
"Tenant"
|
|
39362
|
+
]
|
|
39363
|
+
},
|
|
39364
|
+
"requiresAll": { "type": "boolean" }
|
|
39365
|
+
}
|
|
39366
|
+
}
|
|
39367
|
+
}
|
|
39368
|
+
}
|
|
39369
|
+
},
|
|
39370
|
+
"terms": {
|
|
39371
|
+
"type": "object"
|
|
39372
|
+
}
|
|
39373
|
+
}
|
|
39374
|
+
},
|
|
39375
|
+
"signatures": {
|
|
39376
|
+
"type": "array",
|
|
39377
|
+
"items": {
|
|
39378
|
+
"type": "object",
|
|
39379
|
+
"required": ["name", "signedOn", "role", "contractHash"],
|
|
39380
|
+
"properties": {
|
|
39381
|
+
"name": { "type": "string" },
|
|
39382
|
+
"signedOn": { "type": "string", "format": "date-time" },
|
|
39383
|
+
"externalIds": {
|
|
39384
|
+
"type": "object"
|
|
39385
|
+
}
|
|
39386
|
+
},
|
|
39387
|
+
"role": {
|
|
39388
|
+
"type": "string",
|
|
39389
|
+
"enum": [
|
|
39390
|
+
"Seller",
|
|
39391
|
+
"Seller's Conveyancer",
|
|
39392
|
+
"Prospective Buyer",
|
|
39393
|
+
"Buyer",
|
|
39394
|
+
"Buyer's Conveyancer",
|
|
39395
|
+
"Estate Agent",
|
|
39396
|
+
"Buyer's Agent",
|
|
39397
|
+
"Surveyor",
|
|
39398
|
+
"Mortgage Broker",
|
|
39399
|
+
"Lender",
|
|
39400
|
+
"Landlord",
|
|
39401
|
+
"Tenant"
|
|
39402
|
+
]
|
|
39403
|
+
},
|
|
39404
|
+
"contractHash": {
|
|
39405
|
+
"title": "An MD5 hash of the stable-stringified contract object being signed",
|
|
39406
|
+
"type": "string"
|
|
39407
|
+
}
|
|
39408
|
+
}
|
|
39409
|
+
}
|
|
39410
|
+
}
|
|
39411
|
+
}
|
|
39288
39412
|
}
|
|
39289
39413
|
}
|
|
39290
39414
|
}
|
|
@@ -275,7 +275,10 @@
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
|
-
}
|
|
278
|
+
},
|
|
279
|
+
"required": [
|
|
280
|
+
"managedFreeholdOrCommonholdInformation"
|
|
281
|
+
]
|
|
279
282
|
},
|
|
280
283
|
{
|
|
281
284
|
"properties": {
|
|
@@ -524,7 +527,10 @@
|
|
|
524
527
|
}
|
|
525
528
|
}
|
|
526
529
|
}
|
|
527
|
-
}
|
|
530
|
+
},
|
|
531
|
+
"required": [
|
|
532
|
+
"leaseholdInformation"
|
|
533
|
+
]
|
|
528
534
|
},
|
|
529
535
|
{
|
|
530
536
|
"properties": {
|
|
@@ -16,6 +16,19 @@
|
|
|
16
16
|
},
|
|
17
17
|
"authorisationToShare": {
|
|
18
18
|
"sr24Ref": "1.2"
|
|
19
|
+
},
|
|
20
|
+
"sellerSignatures": {
|
|
21
|
+
"sr24Ref": "1.3",
|
|
22
|
+
"items": {
|
|
23
|
+
"properties": {
|
|
24
|
+
"name": {
|
|
25
|
+
"sr24Ref": "1.3.1"
|
|
26
|
+
},
|
|
27
|
+
"signedOn": {
|
|
28
|
+
"sr24Ref": "1.3.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
}
|
|
20
33
|
}
|
|
21
34
|
}
|
|
@@ -182,6 +182,9 @@
|
|
|
182
182
|
},
|
|
183
183
|
"externalIds": {
|
|
184
184
|
"type": "object"
|
|
185
|
+
},
|
|
186
|
+
"isRemoved": {
|
|
187
|
+
"type": "boolean"
|
|
185
188
|
}
|
|
186
189
|
},
|
|
187
190
|
"oneOf": [
|
|
@@ -223,6 +226,7 @@
|
|
|
223
226
|
"Personal Representative for a Deceased Owner",
|
|
224
227
|
"Under Power of Attorney",
|
|
225
228
|
"Mortgagee in Possession",
|
|
229
|
+
"Assistant",
|
|
226
230
|
"Other"
|
|
227
231
|
]
|
|
228
232
|
}
|
|
@@ -257,6 +261,7 @@
|
|
|
257
261
|
"enum": [
|
|
258
262
|
"Personal Representative for a Deceased Owner",
|
|
259
263
|
"Under Power of Attorney",
|
|
264
|
+
"Assistant",
|
|
260
265
|
"Other"
|
|
261
266
|
]
|
|
262
267
|
},
|
|
@@ -1090,7 +1095,9 @@
|
|
|
1090
1095
|
"Trunk road/motorway",
|
|
1091
1096
|
"Airport",
|
|
1092
1097
|
"Helipad",
|
|
1093
|
-
"Other"
|
|
1098
|
+
"Other",
|
|
1099
|
+
"Tramway",
|
|
1100
|
+
"Underground/Subway"
|
|
1094
1101
|
]
|
|
1095
1102
|
},
|
|
1096
1103
|
"latitude": {
|
|
@@ -21689,6 +21696,32 @@
|
|
|
21689
21696
|
"authorisationToShare": {
|
|
21690
21697
|
"title": "I/we consent to the sharing of the information contained in the Sale Ready pack with the my/our legal representative and the buyer(s) and their legal representatives",
|
|
21691
21698
|
"type": "boolean"
|
|
21699
|
+
},
|
|
21700
|
+
"sellerSignatures": {
|
|
21701
|
+
"title": "I confirm that all information provided is accurate to the best of my knowledge and if I become aware of any change to/changes which alter the information supplied/provided prior to exchange of contracts for the sale of the property, I will immediately notify the person marketing the property as well as my property lawyer.",
|
|
21702
|
+
"type": "array",
|
|
21703
|
+
"items": {
|
|
21704
|
+
"type": "object",
|
|
21705
|
+
"required": [
|
|
21706
|
+
"name",
|
|
21707
|
+
"signedOn"
|
|
21708
|
+
],
|
|
21709
|
+
"properties": {
|
|
21710
|
+
"name": {
|
|
21711
|
+
"title": "Name of signatory",
|
|
21712
|
+
"type": "string",
|
|
21713
|
+
"minLength": 1
|
|
21714
|
+
},
|
|
21715
|
+
"signedOn": {
|
|
21716
|
+
"title": "Date and time of signature in ISO 8601 format",
|
|
21717
|
+
"type": "string",
|
|
21718
|
+
"format": "date-time"
|
|
21719
|
+
},
|
|
21720
|
+
"externalIds": {
|
|
21721
|
+
"type": "object"
|
|
21722
|
+
}
|
|
21723
|
+
}
|
|
21724
|
+
}
|
|
21692
21725
|
}
|
|
21693
21726
|
}
|
|
21694
21727
|
},
|
|
@@ -24233,8 +24266,8 @@
|
|
|
24233
24266
|
}
|
|
24234
24267
|
]
|
|
24235
24268
|
},
|
|
24236
|
-
"
|
|
24237
|
-
"title": "flood and
|
|
24269
|
+
"floodAndCoastalErosionRiskManagement": {
|
|
24270
|
+
"title": "flood and coastal erosion risk management",
|
|
24238
24271
|
"type": "object",
|
|
24239
24272
|
"properties": {
|
|
24240
24273
|
"yesNo": {
|
|
@@ -34089,6 +34122,119 @@
|
|
|
34089
34122
|
}
|
|
34090
34123
|
}
|
|
34091
34124
|
}
|
|
34125
|
+
},
|
|
34126
|
+
"contracts": {
|
|
34127
|
+
"type": "array",
|
|
34128
|
+
"items": {
|
|
34129
|
+
"type": "object",
|
|
34130
|
+
"properties": {
|
|
34131
|
+
"contract": {
|
|
34132
|
+
"type": "object",
|
|
34133
|
+
"required": [
|
|
34134
|
+
"template",
|
|
34135
|
+
"terms"
|
|
34136
|
+
],
|
|
34137
|
+
"properties": {
|
|
34138
|
+
"template": {
|
|
34139
|
+
"type": "object",
|
|
34140
|
+
"required": [
|
|
34141
|
+
"name",
|
|
34142
|
+
"url",
|
|
34143
|
+
"requiredSignatories"
|
|
34144
|
+
],
|
|
34145
|
+
"properties": {
|
|
34146
|
+
"name": {
|
|
34147
|
+
"type": "string"
|
|
34148
|
+
},
|
|
34149
|
+
"url": {
|
|
34150
|
+
"type": "string",
|
|
34151
|
+
"format": "uri"
|
|
34152
|
+
},
|
|
34153
|
+
"externalIds": {
|
|
34154
|
+
"type": "object"
|
|
34155
|
+
},
|
|
34156
|
+
"requiredSignatories": {
|
|
34157
|
+
"type": "array",
|
|
34158
|
+
"items": {
|
|
34159
|
+
"type": "object",
|
|
34160
|
+
"properties": {
|
|
34161
|
+
"role": {
|
|
34162
|
+
"type": "string",
|
|
34163
|
+
"enum": [
|
|
34164
|
+
"Seller",
|
|
34165
|
+
"Seller's Conveyancer",
|
|
34166
|
+
"Prospective Buyer",
|
|
34167
|
+
"Buyer",
|
|
34168
|
+
"Buyer's Conveyancer",
|
|
34169
|
+
"Estate Agent",
|
|
34170
|
+
"Buyer's Agent",
|
|
34171
|
+
"Surveyor",
|
|
34172
|
+
"Mortgage Broker",
|
|
34173
|
+
"Lender",
|
|
34174
|
+
"Landlord",
|
|
34175
|
+
"Tenant"
|
|
34176
|
+
]
|
|
34177
|
+
},
|
|
34178
|
+
"requiresAll": {
|
|
34179
|
+
"type": "boolean"
|
|
34180
|
+
}
|
|
34181
|
+
}
|
|
34182
|
+
}
|
|
34183
|
+
}
|
|
34184
|
+
}
|
|
34185
|
+
},
|
|
34186
|
+
"terms": {
|
|
34187
|
+
"type": "object"
|
|
34188
|
+
}
|
|
34189
|
+
}
|
|
34190
|
+
},
|
|
34191
|
+
"signatures": {
|
|
34192
|
+
"type": "array",
|
|
34193
|
+
"items": {
|
|
34194
|
+
"type": "object",
|
|
34195
|
+
"required": [
|
|
34196
|
+
"name",
|
|
34197
|
+
"signedOn",
|
|
34198
|
+
"role",
|
|
34199
|
+
"contractHash"
|
|
34200
|
+
],
|
|
34201
|
+
"properties": {
|
|
34202
|
+
"name": {
|
|
34203
|
+
"type": "string"
|
|
34204
|
+
},
|
|
34205
|
+
"signedOn": {
|
|
34206
|
+
"type": "string",
|
|
34207
|
+
"format": "date-time"
|
|
34208
|
+
},
|
|
34209
|
+
"externalIds": {
|
|
34210
|
+
"type": "object"
|
|
34211
|
+
}
|
|
34212
|
+
},
|
|
34213
|
+
"role": {
|
|
34214
|
+
"type": "string",
|
|
34215
|
+
"enum": [
|
|
34216
|
+
"Seller",
|
|
34217
|
+
"Seller's Conveyancer",
|
|
34218
|
+
"Prospective Buyer",
|
|
34219
|
+
"Buyer",
|
|
34220
|
+
"Buyer's Conveyancer",
|
|
34221
|
+
"Estate Agent",
|
|
34222
|
+
"Buyer's Agent",
|
|
34223
|
+
"Surveyor",
|
|
34224
|
+
"Mortgage Broker",
|
|
34225
|
+
"Lender",
|
|
34226
|
+
"Landlord",
|
|
34227
|
+
"Tenant"
|
|
34228
|
+
]
|
|
34229
|
+
},
|
|
34230
|
+
"contractHash": {
|
|
34231
|
+
"title": "An MD5 hash of the stable-stringified contract object being signed",
|
|
34232
|
+
"type": "string"
|
|
34233
|
+
}
|
|
34234
|
+
}
|
|
34235
|
+
}
|
|
34236
|
+
}
|
|
34237
|
+
}
|
|
34092
34238
|
}
|
|
34093
34239
|
}
|
|
34094
34240
|
}
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"organisationReference": {},
|
|
31
31
|
"role": {},
|
|
32
32
|
"externalIds": {},
|
|
33
|
+
"isRemoved": {},
|
|
33
34
|
"sellersCapacity": {
|
|
34
35
|
"capacity": {},
|
|
35
36
|
"sellersCapacityDetails": {},
|
|
@@ -2816,7 +2817,12 @@
|
|
|
2816
2817
|
},
|
|
2817
2818
|
"saleReadyDeclarations": {
|
|
2818
2819
|
"authorisedToActOnBehalfOfAllSellers": {},
|
|
2819
|
-
"authorisationToShare": {}
|
|
2820
|
+
"authorisationToShare": {},
|
|
2821
|
+
"sellerSignatures": {
|
|
2822
|
+
"name": {},
|
|
2823
|
+
"signedOn": {},
|
|
2824
|
+
"externalIds": {}
|
|
2825
|
+
}
|
|
2820
2826
|
},
|
|
2821
2827
|
"localSearches": {
|
|
2822
2828
|
"localLandCharges": {
|
|
@@ -3181,7 +3187,7 @@
|
|
|
3181
3187
|
"yesNo": {},
|
|
3182
3188
|
"details": {}
|
|
3183
3189
|
},
|
|
3184
|
-
"
|
|
3190
|
+
"floodAndCoastalErosionRiskManagement": {
|
|
3185
3191
|
"yesNo": {},
|
|
3186
3192
|
"details": {}
|
|
3187
3193
|
}
|
|
@@ -4089,5 +4095,24 @@
|
|
|
4089
4095
|
"pricingMethodology": {}
|
|
4090
4096
|
}
|
|
4091
4097
|
}
|
|
4098
|
+
},
|
|
4099
|
+
"contracts": {
|
|
4100
|
+
"contract": {
|
|
4101
|
+
"template": {
|
|
4102
|
+
"name": {},
|
|
4103
|
+
"url": {},
|
|
4104
|
+
"externalIds": {},
|
|
4105
|
+
"requiredSignatories": {
|
|
4106
|
+
"role": {},
|
|
4107
|
+
"requiresAll": {}
|
|
4108
|
+
}
|
|
4109
|
+
},
|
|
4110
|
+
"terms": {}
|
|
4111
|
+
},
|
|
4112
|
+
"signatures": {
|
|
4113
|
+
"name": {},
|
|
4114
|
+
"signedOn": {},
|
|
4115
|
+
"externalIds": {}
|
|
4116
|
+
}
|
|
4092
4117
|
}
|
|
4093
4118
|
}
|
|
@@ -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
|
+
});
|