@planning-inspectorate/data-model 2.10.0 → 2.10.2

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.
Files changed (35) hide show
  1. package/docs/appeal-s78.md +1 -0
  2. package/package.json +1 -1
  3. package/pins_data_model/models/model_appeal_document.py +1 -1
  4. package/pins_data_model/models/model_appeal_event.py +1 -1
  5. package/pins_data_model/models/model_appeal_event_estimate.py +1 -1
  6. package/pins_data_model/models/model_appeal_has.py +1 -1
  7. package/pins_data_model/models/model_appeal_representation.py +1 -1
  8. package/pins_data_model/models/model_appeal_s78.py +2 -1
  9. package/pins_data_model/models/model_dart_response.py +1 -1
  10. package/pins_data_model/models/model_entraid.py +1 -1
  11. package/pins_data_model/models/model_folder.py +1 -1
  12. package/pins_data_model/models/model_gettimesheets_response.py +1 -1
  13. package/pins_data_model/models/model_listed_building.py +1 -1
  14. package/pins_data_model/models/model_listed_buildings.py +1 -1
  15. package/pins_data_model/models/model_nsip_document.py +1 -1
  16. package/pins_data_model/models/model_nsip_exam_timetable.py +1 -1
  17. package/pins_data_model/models/model_nsip_invoice.py +1 -1
  18. package/pins_data_model/models/model_nsip_meeting.py +1 -1
  19. package/pins_data_model/models/model_nsip_project.py +1 -1
  20. package/pins_data_model/models/model_nsip_project_update.py +1 -1
  21. package/pins_data_model/models/model_nsip_representation.py +1 -1
  22. package/pins_data_model/models/model_nsip_subscription.py +1 -1
  23. package/pins_data_model/models/model_pins_inspector.py +1 -1
  24. package/pins_data_model/models/model_pins_lpa.py +1 -1
  25. package/pins_data_model/models/model_s51_advice.py +1 -1
  26. package/pins_data_model/models/model_service_user.py +1 -1
  27. package/schemas/appeal-s78.schema.json +1 -1
  28. package/schemas/commands/appeals-components/appeal-s78-submission.schema.json +10 -20
  29. package/schemas/commands/appeals-components/lpa-questionnaire-listed-building.schema.json +23 -0
  30. package/schemas/commands/appeals-components/lpa-questionnaire-s78.schema.json +7 -14
  31. package/schemas/commands/appeals-components/{site-designations-protections.schema.json → site-designations-protection.schema.json} +5 -10
  32. package/schemas/commands/lpa-questionnaire.schema.json +20 -5
  33. package/src/schemas.d.ts +61 -10
  34. package/tests/appeals/commands/s20-questionnaire.test.js +108 -0
  35. package/tests/appeals/commands/s78-questionnaire.test.js +29 -26
@@ -1474,6 +1474,7 @@ The outcome of the original LPA decision
1474
1474
  | `"granted"` | |
1475
1475
  | `"refused"` | |
1476
1476
  | `"not_received"` | |
1477
+ | `null` | |
1477
1478
 
1478
1479
  ## applicationDecisionDate
1479
1480
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/data-model",
3
- "version": "2.10.0",
3
+ "version": "2.10.2",
4
4
  "description": "JSON Schemas for the Planning Inspectorate's Data Model",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-document.schema.json
3
- # timestamp: 2026-01-15T10:15:03+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event-estimate.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-has.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-representation.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-s78.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -149,6 +149,7 @@ class ApplicationDecision(Enum):
149
149
  granted = "granted"
150
150
  refused = "refused"
151
151
  not_received = "not_received"
152
+ NoneType_None = None
152
153
 
153
154
 
154
155
  class KnowsOtherOwners(Enum):
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: dart-response.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: entraid.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: folder.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: gettimesheets-response.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-building.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-buildings.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-document.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-exam-timetable.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-invoice.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-meeting.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project-update.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-representation.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:10+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-subscription.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-inspector.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-lpa.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: s51-advice.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: service-user.schema.json
3
- # timestamp: 2026-01-15T10:15:02+00:00
3
+ # timestamp: 2026-01-20T11:57:09+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -478,7 +478,7 @@
478
478
  },
479
479
  "applicationDecision": {
480
480
  "type": ["string", "null"],
481
- "enum": ["granted", "refused", "not_received"],
481
+ "enum": ["granted", "refused", "not_received", null],
482
482
  "description": "The outcome of the original LPA decision",
483
483
  "$comment": "Also available on HAS cases"
484
484
  },
@@ -23,54 +23,44 @@
23
23
  "properties": {
24
24
  "agriculturalHolding": {
25
25
  "type": ["boolean", "null"],
26
- "description": "Indicates if the site is an agricultural holding",
27
- "$comment": "Only available in S78 cases"
26
+ "description": "Indicates if the site is an agricultural holding"
28
27
  },
29
28
  "tenantAgriculturalHolding": {
30
29
  "type": ["boolean", "null"],
31
- "description": "Indicates if the site is a tenant agricultural holding",
32
- "$comment": "Only available in S78 cases"
30
+ "description": "Indicates if the site is a tenant agricultural holding"
33
31
  },
34
32
  "otherTenantsAgriculturalHolding": {
35
33
  "type": ["boolean", "null"],
36
- "description": "Indicates if the site is an agricultural holding with other tenants",
37
- "$comment": "Only available in S78 cases"
34
+ "description": "Indicates if the site is an agricultural holding with other tenants"
38
35
  },
39
36
  "informedTenantsAgriculturalHolding": {
40
37
  "type": ["boolean", "null"],
41
- "description": "If the site is an agricultural holding, indicates tenants are informed",
42
- "$comment": "Only available in S78 cases"
38
+ "description": "If the site is an agricultural holding, indicates tenants are informed"
43
39
  },
44
40
  "appellantProcedurePreference": {
45
41
  "type": ["string", "null"],
46
42
  "enum": ["written", "hearing", "inquiry", null],
47
- "description": "The procedure preference indicated by the appellant",
48
- "$comment": "Only available in S78 cases"
43
+ "description": "The procedure preference indicated by the appellant"
49
44
  },
50
45
  "appellantProcedurePreferenceDetails": {
51
46
  "type": ["string", "null"],
52
- "description": "The procedure details preference indicated by the appellant",
53
- "$comment": "Only available in S78 cases"
47
+ "description": "The procedure details preference indicated by the appellant"
54
48
  },
55
49
  "appellantProcedurePreferenceDuration": {
56
50
  "type": ["number", "null"],
57
- "description": "The duration of the procedure indicated by the appellant",
58
- "$comment": "Only available in S78 cases"
51
+ "description": "The duration of the procedure indicated by the appellant"
59
52
  },
60
53
  "appellantProcedurePreferenceWitnessCount": {
61
54
  "type": ["number", "null"],
62
- "description": "The number of witnesses in inquiries",
63
- "$comment": "Only available in S78 cases"
55
+ "description": "The number of witnesses in inquiries"
64
56
  },
65
57
  "planningObligation": {
66
58
  "type": ["boolean", "null"],
67
- "description": "Indicates the existence of a planning obligation",
68
- "$comment": "Only available in S78 cases"
59
+ "description": "Indicates the existence of a planning obligation"
69
60
  },
70
61
  "statusPlanningObligation": {
71
62
  "type": ["string", "null"],
72
- "description": "The planning obligation information, if available",
73
- "$comment": "Only available in S78 cases"
63
+ "description": "The planning obligation information, if available"
74
64
  },
75
65
  "developmentType": {
76
66
  "type": ["string", "null"],
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "commands/appeals-components/lpa-questionnaire-listed-building.schema.json",
4
+ "title": "LPAQ listed building submission properties",
5
+ "description": "Schema defining any listed building specific properties for LPAQ submissions",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": ["preserveGrantLoan", "consultHistoricEngland"],
9
+ "allOf": [
10
+ {
11
+ "properties": {
12
+ "preserveGrantLoan": {
13
+ "type": ["boolean", "null"],
14
+ "description": "A grant or loan has been made to preserve the listed building"
15
+ },
16
+ "consultHistoricEngland": {
17
+ "type": ["boolean", "null"],
18
+ "description": "Are Historic England to be consulted?"
19
+ }
20
+ }
21
+ }
22
+ ]
23
+ }
@@ -19,23 +19,19 @@
19
19
  "properties": {
20
20
  "affectsScheduledMonument": {
21
21
  "type": ["boolean", "null"],
22
- "description": "Indicates that a scheduled monument is affected",
23
- "$comment": "Only available in S78 cases"
22
+ "description": "Indicates that a scheduled monument is affected"
24
23
  },
25
24
  "hasTreePreservationOrder": {
26
25
  "type": ["boolean", "null"],
27
- "description": "Indicates a TPO (tree preservation order)",
28
- "$comment": "Only available in S78 cases"
26
+ "description": "Indicates a TPO (tree preservation order)"
29
27
  },
30
28
  "isPublicRightOfWay": {
31
29
  "type": ["boolean", "null"],
32
- "description": "Indicates a PROW (public right of way)",
33
- "$comment": "Only available in S78 cases"
30
+ "description": "Indicates a PROW (public right of way)"
34
31
  },
35
32
  "hasStatutoryConsultees": {
36
33
  "type": ["boolean", "null"],
37
- "description": "Indicates statutory consultees",
38
- "$comment": "Only available in S78 cases"
34
+ "description": "Indicates statutory consultees"
39
35
  },
40
36
  "consultedBodiesDetails": {
41
37
  "type": ["string", "null"],
@@ -43,18 +39,15 @@
43
39
  },
44
40
  "hasConsultationResponses": {
45
41
  "type": ["boolean", "null"],
46
- "description": "Indicates consultation responses",
47
- "$comment": "Only available in S78 cases"
42
+ "description": "Indicates consultation responses"
48
43
  },
49
44
  "hasEmergingPlan": {
50
45
  "type": ["boolean", "null"],
51
- "description": "Indicates emerging plans",
52
- "$comment": "Only available in S78 cases"
46
+ "description": "Indicates emerging plans"
53
47
  },
54
48
  "hasSupplementaryPlanningDocs": {
55
49
  "type": ["boolean", "null"],
56
- "description": "Indicates supplementary planning documents",
57
- "$comment": "Only available in S78 cases"
50
+ "description": "Indicates supplementary planning documents"
58
51
  }
59
52
  }
60
53
  }
@@ -17,18 +17,15 @@
17
17
  "properties": {
18
18
  "affectsScheduledMonument": {
19
19
  "type": ["boolean", "null"],
20
- "description": "Indicates that a scheduled monument is affected",
21
- "$comment": "Only available in S78 cases"
20
+ "description": "Indicates that a scheduled monument is affected"
22
21
  },
23
22
  "hasProtectedSpecies": {
24
23
  "type": ["boolean", "null"],
25
- "description": "Indicates the existence of protected species",
26
- "$comment": "Only available in S78 cases"
24
+ "description": "Indicates the existence of protected species"
27
25
  },
28
26
  "isAonbNationalLandscape": {
29
27
  "type": ["boolean", "null"],
30
- "description": "Indicates an area of outstanding beauty (National Landscape)",
31
- "$comment": "Only available in S78 cases"
28
+ "description": "Indicates an area of outstanding beauty (National Landscape)"
32
29
  },
33
30
  "designatedSitesNames": {
34
31
  "type": ["array", "null"],
@@ -36,13 +33,11 @@
36
33
  "type": "string",
37
34
  "examples": ["SSSI", "cSAC", "SAC", "pSPA", "SPA", "user entered value"]
38
35
  },
39
- "description": "The designated site names",
40
- "$comment": "Only available in S78 cases"
36
+ "description": "The designated site names"
41
37
  },
42
38
  "isGypsyOrTravellerSite": {
43
39
  "type": ["boolean", "null"],
44
- "description": "Indicates a traveller site",
45
- "$comment": "Only available in S78 cases"
40
+ "description": "Indicates a traveller site"
46
41
  }
47
42
  }
48
43
  }
@@ -21,9 +21,9 @@
21
21
  ]
22
22
  },
23
23
  {
24
- "$comment": "S78 + S20",
24
+ "$comment": "S78",
25
25
  "allOf": [
26
- { "properties": { "caseType": { "enum": ["W", "Y"] } } },
26
+ { "properties": { "caseType": { "enum": ["W"] } } },
27
27
  { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
28
28
  { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
29
29
  { "$ref": "appeals-components/lpa-questionnaire-s78.schema.json" },
@@ -34,14 +34,29 @@
34
34
  { "$ref": "appeals-components/site-designations-protection.schema.json" }
35
35
  ]
36
36
  },
37
+ {
38
+ "$comment": "S20",
39
+ "allOf": [
40
+ { "properties": { "caseType": { "enum": ["Y"] } } },
41
+ { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
42
+ { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
43
+ { "$ref": "appeals-components/lpa-questionnaire-s78.schema.json" },
44
+ { "$ref": "appeals-components/lpa-procedure-preference.schema.json" },
45
+ { "$ref": "appeals-components/changed-listed-building-number.schema.json" },
46
+ { "$ref": "appeals-components/infrastructure-levy.schema.json" },
47
+ { "$ref": "appeals-components/environmental-impact-assessment.schema.json" },
48
+ { "$ref": "appeals-components/site-designations-protection.schema.json" },
49
+ { "$ref": "appeals-components/lpa-questionnaire-listed-building.schema.json" }
50
+ ]
51
+ },
37
52
  {
38
53
  "$comment": "CAS Adverts",
39
54
  "allOf": [
40
55
  { "properties": { "caseType": { "enum": ["ZA"] } } },
41
56
  { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
42
57
  { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
43
- { "$ref": "appeals-components/lpa-procedure-preference.schema.json" },
44
- { "$ref": "appeals-components/lpa-questionnaire-advert.schema.json" }
58
+ { "$ref": "appeals-components/lpa-questionnaire-advert.schema.json" },
59
+ { "$ref": "appeals-components/lpa-procedure-preference.schema.json" }
45
60
  ]
46
61
  },
47
62
  {
@@ -119,6 +134,7 @@
119
134
  "documentType": {
120
135
  "type": ["string", "null"],
121
136
  "enum": [
137
+ null,
122
138
  "whoNotified",
123
139
  "whoNotifiedSiteNotice",
124
140
  "whoNotifiedLetterToNeighbours",
@@ -135,7 +151,6 @@
135
151
  "supplementaryPlanning",
136
152
  "emergingPlan",
137
153
  "appealNotification",
138
- null,
139
154
  "otherRelevantPolicies",
140
155
  "treePreservationPlan",
141
156
  "consultationResponses",
package/src/schemas.d.ts CHANGED
@@ -145,6 +145,9 @@ export interface AppealDocument {
145
145
  | 'environmentalAssessment'
146
146
  | 'appealNotification'
147
147
  | 'historicEnglandConsultation'
148
+ | 'conservationDocuments'
149
+ | 'planShowingExtentOfOrder'
150
+ | 'definitiveMapAndStatementExtract'
148
151
  | 'stopNotice'
149
152
  | 'article4Direction'
150
153
  | 'enforcementList'
@@ -153,6 +156,18 @@ export interface AppealDocument {
153
156
  | 'lpaEnforcementNotice'
154
157
  | 'lpaEnforcementNoticePlan'
155
158
  | 'planningContraventionNotice'
159
+ | 'priorCorrespondenceWithPINS'
160
+ | 'enforcementNotice'
161
+ | 'enforcementNoticePlan'
162
+ | 'groundASupporting'
163
+ | 'groundBSupporting'
164
+ | 'groundCSupporting'
165
+ | 'groundDSupporting'
166
+ | 'groundESupporting'
167
+ | 'groundFSupporting'
168
+ | 'groundGSupporting'
169
+ | 'groundAFeeReceipt'
170
+ | 'delegatedReport'
156
171
  | null;
157
172
  /**
158
173
  * The system mastering the metadata for the current document
@@ -1107,15 +1122,15 @@ export type AppealS78Case = (
1107
1122
  /**
1108
1123
  * The unique identifier of the LPA application
1109
1124
  */
1110
- applicationReference: string;
1125
+ applicationReference: string | null;
1111
1126
  /**
1112
1127
  * The date of the original LPA application
1113
1128
  */
1114
- applicationDate: string;
1129
+ applicationDate: string | null;
1115
1130
  /**
1116
1131
  * The outcome of the original LPA decision
1117
1132
  */
1118
- applicationDecision: 'granted' | 'refused' | 'not_received';
1133
+ applicationDecision: 'granted' | 'refused' | 'not_received' | null;
1119
1134
  /**
1120
1135
  * The date of the original LPA decision
1121
1136
  */
@@ -1531,6 +1546,18 @@ export type AppealS78Case = (
1531
1546
  * padsSapId for PADS inspectors
1532
1547
  */
1533
1548
  padsSapId?: string | null;
1549
+ /**
1550
+ * The reference number of the enforcement notice
1551
+ */
1552
+ enforcementNoticeReference?: string | null;
1553
+ /**
1554
+ * A description of the alleged breach
1555
+ */
1556
+ descriptionOfAllegedBreach?: string | null;
1557
+ /**
1558
+ * The date of the appellant contacted PINS prior to submitting an appeal
1559
+ */
1560
+ dateAppellantContactedPins?: string | null;
1534
1561
  /**
1535
1562
  * The nature of the appellant's interest in the appeal site
1536
1563
  */
@@ -4058,7 +4085,7 @@ export interface AppellantSubmissionCommand {
4058
4085
  | 'groundISupporting'
4059
4086
  | 'groundJSupporting'
4060
4087
  | 'groundKSupporting'
4061
- | 'applicationFeeReceipt'
4088
+ | 'groundAFeeReceipt'
4062
4089
  | null
4063
4090
  | 'environmentalAssessment';
4064
4091
  [k: string]: unknown;
@@ -4394,6 +4421,19 @@ export type SiteDesignationsAndProtectionSpecificProperties = {
4394
4421
  */
4395
4422
  isGypsyOrTravellerSite?: boolean | null;
4396
4423
  };
4424
+ /**
4425
+ * Schema defining any listed building specific properties for LPAQ submissions
4426
+ */
4427
+ export type LPAQListedBuildingSubmissionProperties = {
4428
+ /**
4429
+ * A grant or loan has been made to preserve the listed building
4430
+ */
4431
+ preserveGrantLoan?: boolean | null;
4432
+ /**
4433
+ * Are Historic England to be consulted?
4434
+ */
4435
+ consultHistoricEngland?: boolean | null;
4436
+ };
4397
4437
  /**
4398
4438
  * Schema defining any advert specific properties for LPAQ submissions
4399
4439
  */
@@ -4477,7 +4517,7 @@ export interface LPAQuestionnaireCommand {
4477
4517
  } & LPAQCommonSubmissionProperties &
4478
4518
  LPAQHASSubmissionProperties)
4479
4519
  | ({
4480
- caseType?: 'W' | 'Y';
4520
+ caseType?: 'W';
4481
4521
  } & LPAQCommonSubmissionProperties &
4482
4522
  LPAQHASSubmissionProperties &
4483
4523
  LPAQS78SubmissionProperties &
@@ -4487,11 +4527,22 @@ export interface LPAQuestionnaireCommand {
4487
4527
  EnvironmentalImpactAssessmentProperties &
4488
4528
  SiteDesignationsAndProtectionSpecificProperties)
4489
4529
  | ({
4490
- caseType?: 'ZA';
4530
+ caseType?: 'Y';
4491
4531
  } & LPAQCommonSubmissionProperties &
4492
4532
  LPAQHASSubmissionProperties &
4533
+ LPAQS78SubmissionProperties &
4493
4534
  LPAProcedurePreferenceProperties &
4494
- LPAQCasAdvertSubmissionProperties)
4535
+ ChangedListedBuildingNumbersProperties &
4536
+ InfrastructureLevyProperties &
4537
+ EnvironmentalImpactAssessmentProperties &
4538
+ SiteDesignationsAndProtectionSpecificProperties &
4539
+ LPAQListedBuildingSubmissionProperties)
4540
+ | ({
4541
+ caseType?: 'ZA';
4542
+ } & LPAQCommonSubmissionProperties &
4543
+ LPAQHASSubmissionProperties &
4544
+ LPAQCasAdvertSubmissionProperties &
4545
+ LPAProcedurePreferenceProperties)
4495
4546
  | ({
4496
4547
  caseType?: 'H';
4497
4548
  } & LPAQCommonSubmissionProperties &
@@ -4542,6 +4593,7 @@ export interface LPAQuestionnaireCommand {
4542
4593
  * The type of document, used for exchange, migrations and consumption from the appeal back-office system
4543
4594
  */
4544
4595
  documentType:
4596
+ | null
4545
4597
  | 'whoNotified'
4546
4598
  | 'whoNotifiedSiteNotice'
4547
4599
  | 'whoNotifiedLetterToNeighbours'
@@ -4558,7 +4610,6 @@ export interface LPAQuestionnaireCommand {
4558
4610
  | 'supplementaryPlanning'
4559
4611
  | 'emergingPlan'
4560
4612
  | 'appealNotification'
4561
- | null
4562
4613
  | 'otherRelevantPolicies'
4563
4614
  | 'treePreservationPlan'
4564
4615
  | 'consultationResponses'
@@ -4574,8 +4625,8 @@ export interface LPAQuestionnaireCommand {
4574
4625
  | 'enforcementList'
4575
4626
  | 'localDevelopmentOrder'
4576
4627
  | 'planningPermission'
4577
- | 'enforcementNotice'
4578
- | 'enforcementNoticePlan'
4628
+ | 'lpaEnforcementNotice'
4629
+ | 'lpaEnforcementNoticePlan'
4579
4630
  | 'planningContraventionNotice';
4580
4631
  [k: string]: unknown;
4581
4632
  }[];
@@ -0,0 +1,108 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import Ajv from 'ajv';
4
+ import addFormats from 'ajv-formats';
5
+ import { loadAllSchemasSync } from '../../../src/index.js';
6
+
7
+ const schema = 'lpa-questionnaire.schema.json';
8
+
9
+ const document = {
10
+ documentId: 'deserunt sint aliqua adipisicing dolore',
11
+ filename: 'ut dolore',
12
+ originalFilename: 'Lorem amet fugiat adipisicing Excepteur',
13
+ size: -90476934,
14
+ mime: 'magna labore exercitation ut',
15
+ documentURI: 'ut reprehenderit',
16
+ dateCreated: '1933-04-03T03:24:38.0Z',
17
+ documentType: 'plansDrawings'
18
+ };
19
+
20
+ const exampleS20QuestionnaireSchema = {
21
+ casedata: {
22
+ caseType: 'Y',
23
+ caseReference: 'est ea elit in nulla',
24
+ lpaQuestionnaireSubmittedDate: '2026-01-01T12:00:00.000Z',
25
+ siteAccessDetails: ['velit adipisicing fugiat dolor ullamco'],
26
+ siteSafetyDetails: ['ut', 'dolore ut sint Duis', 'magna consequat quis laboris officia'],
27
+ nearbyCaseReferences: ['nulla', 'incididunt eu nostrud eiusmod', 'exercitation', 'Ut nostrud', 'eiusmod cillum eu'],
28
+ neighbouringSiteAddresses: [
29
+ {
30
+ neighbouringSiteAddressLine1: '123 High Street',
31
+ neighbouringSiteAddressLine2: 'Second line of address of the site',
32
+ neighbouringSiteAddressTown: 'Maidstone',
33
+ neighbouringSiteAddressCounty: 'Kent',
34
+ neighbouringSiteAddressPostcode: 'MD21 5XY',
35
+ neighbouringSiteAccessDetails: 'Ongoing road works at this address',
36
+ neighbouringSiteSafetyDetails: 'Ongoing road works at this address'
37
+ }
38
+ ],
39
+ isCorrectAppealType: true,
40
+ affectedListedBuildingNumbers: ['12345', '67890'],
41
+ isGreenBelt: true,
42
+ inConservationArea: true,
43
+ newConditionDetails: 'Test',
44
+ notificationMethod: ['irure', 'cillum tempor incididunt labore in'],
45
+ lpaCostsAppliedFor: true,
46
+ lpaStatement: 'ex laborum cupidatat',
47
+ changedListedBuildingNumbers: ['54321', '09876'],
48
+ affectsScheduledMonument: true,
49
+ hasProtectedSpecies: true,
50
+ isAonbNationalLandscape: true,
51
+ designatedSitesNames: ['quis nulla eu officia exercitation'],
52
+ hasTreePreservationOrder: true,
53
+ isGypsyOrTravellerSite: true,
54
+ isPublicRightOfWay: true,
55
+ eiaEnvironmentalImpactSchedule: 'schedule-2',
56
+ eiaDevelopmentDescription: 'production-processing-of-metals',
57
+ eiaSensitiveAreaDetails: 'test',
58
+ eiaColumnTwoThreshold: true,
59
+ eiaScreeningOpinion: true,
60
+ eiaRequiresEnvironmentalStatement: true,
61
+ eiaCompletedEnvironmentalStatement: true,
62
+ hasStatutoryConsultees: true,
63
+ consultedBodiesDetails: 'in in labore irure nostrud',
64
+ hasConsultationResponses: true,
65
+ hasEmergingPlan: true,
66
+ hasSupplementaryPlanningDocs: true,
67
+ hasInfrastructureLevy: true,
68
+ isInfrastructureLevyFormallyAdopted: true,
69
+ infrastructureLevyAdoptedDate: '1913-06-14T10:40:33.0Z',
70
+ infrastructureLevyExpectedDate: '1912-06-14T10:40:33.0Z',
71
+ lpaProcedurePreference: 'written',
72
+ lpaProcedurePreferenceDetails: 'in reprehenderit',
73
+ lpaProcedurePreferenceDuration: -34480053.16630782,
74
+ preserveGrantLoan: true,
75
+ consultHistoricEngland: true
76
+ },
77
+ documents: [document]
78
+ };
79
+
80
+ describe('S20 questionnaire command schema', () => {
81
+ const schemas = loadAllSchemasSync();
82
+ const flatSchemas = Object.values(schemas).reduce((a, c) => ({ ...a, ...c }), {});
83
+ const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
84
+ addFormats(ajv);
85
+
86
+ it('should allow valid schema for S20', () => {
87
+ const validationResult = ajv.validate(schema, exampleS20QuestionnaireSchema);
88
+ assert.strictEqual(validationResult, true);
89
+ });
90
+
91
+ it('should reject missing caseReference', () => {
92
+ const test = structuredClone(exampleS20QuestionnaireSchema);
93
+ delete test.casedata.caseReference;
94
+ const validationResult = ajv.validate(schema, test);
95
+ assert.strictEqual(validationResult, false);
96
+ assert.strictEqual(
97
+ ajv.errors.some((x) => x.message === "must have required property 'caseReference'"),
98
+ true
99
+ );
100
+ });
101
+
102
+ it('should allow additional props', () => {
103
+ const test = structuredClone(exampleS20QuestionnaireSchema);
104
+ test.test = 1;
105
+ const validationResult = ajv.validate(schema, test);
106
+ assert.strictEqual(validationResult, true);
107
+ });
108
+ });
@@ -21,44 +21,54 @@ const exampleS78QuestionnaireSchema = {
21
21
  casedata: {
22
22
  caseType: 'W',
23
23
  caseReference: 'est ea elit in nulla',
24
- lpaQuestionnaireSubmittedDate: null,
24
+ lpaQuestionnaireSubmittedDate: '2026-01-01T12:00:00.000Z',
25
25
  siteAccessDetails: ['velit adipisicing fugiat dolor ullamco'],
26
26
  siteSafetyDetails: ['ut', 'dolore ut sint Duis', 'magna consequat quis laboris officia'],
27
27
  nearbyCaseReferences: ['nulla', 'incididunt eu nostrud eiusmod', 'exercitation', 'Ut nostrud', 'eiusmod cillum eu'],
28
- neighbouringSiteAddresses: null,
28
+ neighbouringSiteAddresses: [
29
+ {
30
+ neighbouringSiteAddressLine1: '123 High Street',
31
+ neighbouringSiteAddressLine2: 'Second line of address of the site',
32
+ neighbouringSiteAddressTown: 'Maidstone',
33
+ neighbouringSiteAddressCounty: 'Kent',
34
+ neighbouringSiteAddressPostcode: 'MD21 5XY',
35
+ neighbouringSiteAccessDetails: 'Ongoing road works at this address',
36
+ neighbouringSiteSafetyDetails: 'Ongoing road works at this address'
37
+ }
38
+ ],
29
39
  isCorrectAppealType: true,
30
- affectedListedBuildingNumbers: null,
31
- isGreenBelt: null,
32
- inConservationArea: null,
33
- newConditionDetails: null,
40
+ affectedListedBuildingNumbers: ['12345', '67890'],
41
+ isGreenBelt: true,
42
+ inConservationArea: true,
43
+ newConditionDetails: 'Test',
34
44
  notificationMethod: ['irure', 'cillum tempor incididunt labore in'],
35
45
  lpaCostsAppliedFor: true,
36
46
  lpaStatement: 'ex laborum cupidatat',
37
- changedListedBuildingNumbers: null,
47
+ changedListedBuildingNumbers: ['54321', '09876'],
38
48
  affectsScheduledMonument: true,
39
- hasProtectedSpecies: false,
49
+ hasProtectedSpecies: true,
40
50
  isAonbNationalLandscape: true,
41
51
  designatedSitesNames: ['quis nulla eu officia exercitation'],
42
- hasTreePreservationOrder: null,
52
+ hasTreePreservationOrder: true,
43
53
  isGypsyOrTravellerSite: true,
44
- isPublicRightOfWay: null,
54
+ isPublicRightOfWay: true,
45
55
  eiaEnvironmentalImpactSchedule: 'schedule-2',
46
56
  eiaDevelopmentDescription: 'production-processing-of-metals',
47
- eiaSensitiveAreaDetails: null,
48
- eiaColumnTwoThreshold: false,
49
- eiaScreeningOpinion: false,
50
- eiaRequiresEnvironmentalStatement: null,
57
+ eiaSensitiveAreaDetails: 'test',
58
+ eiaColumnTwoThreshold: true,
59
+ eiaScreeningOpinion: true,
60
+ eiaRequiresEnvironmentalStatement: true,
51
61
  eiaCompletedEnvironmentalStatement: true,
52
62
  hasStatutoryConsultees: true,
53
63
  consultedBodiesDetails: 'in in labore irure nostrud',
54
- hasConsultationResponses: null,
55
- hasEmergingPlan: null,
56
- hasSupplementaryPlanningDocs: null,
64
+ hasConsultationResponses: true,
65
+ hasEmergingPlan: true,
66
+ hasSupplementaryPlanningDocs: true,
57
67
  hasInfrastructureLevy: true,
58
- isInfrastructureLevyFormallyAdopted: false,
68
+ isInfrastructureLevyFormallyAdopted: true,
59
69
  infrastructureLevyAdoptedDate: '1913-06-14T10:40:33.0Z',
60
70
  infrastructureLevyExpectedDate: '1912-06-14T10:40:33.0Z',
61
- lpaProcedurePreference: null,
71
+ lpaProcedurePreference: 'written',
62
72
  lpaProcedurePreferenceDetails: 'in reprehenderit',
63
73
  lpaProcedurePreferenceDuration: -34480053.16630782
64
74
  },
@@ -76,13 +86,6 @@ describe('S78 questionnaire command schema', () => {
76
86
  assert.strictEqual(validationResult, true);
77
87
  });
78
88
 
79
- it('should allow valid schema for S20', () => {
80
- const test = structuredClone(exampleS78QuestionnaireSchema);
81
- test.casedata.caseType = 'Y';
82
- const validationResult = ajv.validate(schema, test);
83
- assert.strictEqual(validationResult, true);
84
- });
85
-
86
89
  it('should reject missing caseReference', () => {
87
90
  const test = structuredClone(exampleS78QuestionnaireSchema);
88
91
  delete test.casedata.caseReference;