@planning-inspectorate/data-model 2.4.0 → 2.5.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.
@@ -115,7 +115,7 @@ any of
115
115
  | [didAppellantSubmitCompletePhotosAndPlans](#didappellantsubmitcompletephotosandplans) | `boolean` | Optional | can be null | [Appeal HAS Case](appeal-has-properties-didappellantsubmitcompletephotosandplans.md "appeal-has.schema.json#/properties/didAppellantSubmitCompletePhotosAndPlans") |
116
116
  | [isSiteInAreaOfSpecialControlAdverts](#issiteinareaofspecialcontroladverts) | `boolean` | Optional | can be null | [Appeal HAS Case](appeal-has-properties-issiteinareaofspecialcontroladverts.md "appeal-has.schema.json#/properties/isSiteInAreaOfSpecialControlAdverts") |
117
117
  | [advertDetails](#advertdetails) | `array` | Optional | can be null | [Appeal HAS Case](appeal-has-properties-advertdetails.md "appeal-has.schema.json#/properties/advertDetails") |
118
- | [padsSapId](#padssapid) | `string` | Required | can be null | [Appeal HAS Case](appeal-has-properties-padssapid.md "appeal-has.schema.json#/properties/padsSapId") |
118
+ | [padsSapId](#padssapid) | `string` | Optional | can be null | [Appeal HAS Case](appeal-has-properties-padssapid.md "appeal-has.schema.json#/properties/padsSapId") |
119
119
  | Additional Properties | Any | Optional | can be null | |
120
120
 
121
121
  ## caseId
@@ -2630,7 +2630,7 @@ padsSapId for PADS inspectors
2630
2630
 
2631
2631
  `padsSapId`
2632
2632
 
2633
- * is required
2633
+ * is optional
2634
2634
 
2635
2635
  * Type: `string`
2636
2636
 
@@ -155,7 +155,7 @@ any of
155
155
  | [didAppellantSubmitCompletePhotosAndPlans](#didappellantsubmitcompletephotosandplans) | `boolean` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-didappellantsubmitcompletephotosandplans.md "appeal-s78.schema.json#/properties/didAppellantSubmitCompletePhotosAndPlans") |
156
156
  | [isSiteInAreaOfSpecialControlAdverts](#issiteinareaofspecialcontroladverts) | `boolean` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-issiteinareaofspecialcontroladverts.md "appeal-s78.schema.json#/properties/isSiteInAreaOfSpecialControlAdverts") |
157
157
  | [advertDetails](#advertdetails) | `array` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-advertdetails.md "appeal-s78.schema.json#/properties/advertDetails") |
158
- | [padsSapId](#padssapid) | `string` | Required | can be null | [Appeal S78 Case](appeal-s78-properties-padssapid.md "appeal-s78.schema.json#/properties/padsSapId") |
158
+ | [padsSapId](#padssapid) | `string` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-padssapid.md "appeal-s78.schema.json#/properties/padsSapId") |
159
159
  | [ownerOccupancyStatus](#owneroccupancystatus) | `string` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-owneroccupancystatus.md "appeal-s78.schema.json#/properties/ownerOccupancyStatus") |
160
160
  | [occupancyConditionsMet](#occupancyconditionsmet) | `boolean` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-occupancyconditionsmet.md "appeal-s78.schema.json#/properties/occupancyConditionsMet") |
161
161
  | [enforcementAppealGroundsDetails](#enforcementappealgroundsdetails) | `array` | Optional | can be null | [Appeal S78 Case](appeal-s78-properties-enforcementappealgroundsdetails.md "appeal-s78.schema.json#/properties/enforcementAppealGroundsDetails") |
@@ -3764,7 +3764,7 @@ padsSapId for PADS inspectors
3764
3764
 
3765
3765
  `padsSapId`
3766
3766
 
3767
- * is required
3767
+ * is optional
3768
3768
 
3769
3769
  * Type: `string`
3770
3770
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/data-model",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
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-has.schema.json
3
- # timestamp: 2025-12-09T12:06:41+00:00
3
+ # timestamp: 2025-12-15T10:41:08+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: 2025-12-09T15:01:57+00:00
3
+ # timestamp: 2025-12-15T10:41:08+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -72,8 +72,7 @@
72
72
  "neighbouringSiteAddresses",
73
73
  "affectedListedBuildingNumbers",
74
74
  "appellantCostsAppliedFor",
75
- "lpaCostsAppliedFor",
76
- "padsSapId"
75
+ "lpaCostsAppliedFor"
77
76
  ],
78
77
  "anyOf": [
79
78
  {
@@ -130,8 +130,7 @@
130
130
  "numberOfResidencesNetChange",
131
131
  "siteViewableFromRoad",
132
132
  "siteWithinSSSI",
133
- "typeOfPlanningApplication",
134
- "padsSapId"
133
+ "typeOfPlanningApplication"
135
134
  ],
136
135
  "anyOf": [
137
136
  {
@@ -0,0 +1,325 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "commands/appeals-components/appeal-enforcement-submission.schema.json",
4
+ "title": "Appellant enforcement submission properties",
5
+ "description": "Schema defining any properties for enforcement appeals",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "submissionId",
10
+ "caseProcedure",
11
+ "lpaCode",
12
+ "caseSubmittedDate",
13
+ "enforcementNotice",
14
+ "enforcementReference",
15
+ "caseSubmissionDueDate",
16
+ "enforcementIssueDate",
17
+ "enforcementEffectiveDate"
18
+ ],
19
+ "anyOf": [
20
+ {
21
+ "required": ["siteGridReferenceEasting", "siteGridReferenceNorthing"],
22
+ "properties": {
23
+ "siteGridReferenceEasting": { "type": "string" },
24
+ "siteGridReferenceNorthing": { "type": "string" }
25
+ }
26
+ },
27
+ {
28
+ "required": ["siteAddressLine1", "siteAddressTown", "siteAddressPostcode"],
29
+ "properties": {
30
+ "siteAddressLine1": { "type": "string" },
31
+ "siteAddressTown": { "type": "string" },
32
+ "siteAddressPostcode": { "type": "string" }
33
+ }
34
+ }
35
+ ],
36
+ "properties": {
37
+ "submissionId": {
38
+ "type": "string",
39
+ "format": "uuid",
40
+ "examples": ["7f45748c-2342-4b59-b934-ad6183e8d834"],
41
+ "description": "Case submission id - link to draft submission created by appellant"
42
+ },
43
+ "caseProcedure": {
44
+ "type": "string",
45
+ "enum": ["written", "hearing", "inquiry"],
46
+ "description": "The type of procedure for the appeal"
47
+ },
48
+ "lpaCode": {
49
+ "type": "string",
50
+ "examples": ["Q9999"],
51
+ "description": "A unique identifier for the Local Planning Authority",
52
+ "$comment": "Uses the internal PINS code identifying LPAs"
53
+ },
54
+ "caseSubmittedDate": {
55
+ "type": "string",
56
+ "format": "date-time",
57
+ "examples": ["2023-07-27T20:30:00.000Z"],
58
+ "description": "The date the appeal was submitted by the appellant"
59
+ },
60
+ "enforcementNotice": {
61
+ "type": ["boolean", "null"],
62
+ "description": "Indicates if an enforcement notice is the reason for the appeal"
63
+ },
64
+ "enforcementReference": {
65
+ "type": "string",
66
+ "examples": ["123456"],
67
+ "description": "The unique identifier of the enforcement notice"
68
+ },
69
+ "enforcementIssueDate": {
70
+ "type": "string",
71
+ "format": "date-time",
72
+ "examples": ["2023-07-27T20:30:00.000Z"],
73
+ "description": "The date the enforcement notice was issued"
74
+ },
75
+ "enforcementEffectiveDate": {
76
+ "type": "string",
77
+ "format": "date-time",
78
+ "examples": ["2023-07-27T20:30:00.000Z"],
79
+ "description": "The date the enforcement notice took effect"
80
+ },
81
+ "contactPlanningInspectorateDate": {
82
+ "type": ["string", "null"],
83
+ "format": "date-time",
84
+ "examples": ["2023-07-27T20:30:00.000Z"],
85
+ "description": "The date the enforcement notice took effect"
86
+ },
87
+ "interestInLand": {
88
+ "type": ["string", "null"],
89
+ "examples": ["owner, tenant, mortgage holder, other"],
90
+ "description": "The individual's interest in the appeal land"
91
+ },
92
+ "writtenOrVerbalPermission": {
93
+ "type": ["string", "null"],
94
+ "examples": ["yes, no"],
95
+ "description": "If required, whether the individual has permission to use the appeal land"
96
+ },
97
+ "descriptionOfAllegedBreach": {
98
+ "type": ["string", "null"],
99
+ "description": "A description of the alleged breach"
100
+ },
101
+ "applicationMadeAndFeePaid": {
102
+ "type": ["boolean", "null"],
103
+ "description": "Whether a planning application was made"
104
+ },
105
+ "applicationDevelopmentAllOrPart": {
106
+ "type": ["string", "null"],
107
+ "enum": ["all-of-the-development", "part-of-the-development", null],
108
+ "description": "Did the application relate to all or part of the development"
109
+ },
110
+ "originalDevelopmentDescription": {
111
+ "type": ["string", "null"],
112
+ "description": "A description of the proposed development"
113
+ },
114
+ "changedDevelopmentDescription": {
115
+ "type": ["boolean", "null"],
116
+ "description": "An updated description of the proposed development"
117
+ },
118
+ "applicationReference": {
119
+ "type": ["string", "null"],
120
+ "examples": ["43010/APP/1/290572"],
121
+ "description": "The unique identifier of the LPA application"
122
+ },
123
+ "applicationDate": {
124
+ "type": ["string", "null"],
125
+ "format": "date-time",
126
+ "examples": ["2023-07-27T20:30:00.000Z"],
127
+ "description": "The date of the original LPA application"
128
+ },
129
+ "applicationDecision": {
130
+ "type": ["string", "null"],
131
+ "enum": ["granted", "refused", "not_received", null],
132
+ "description": "The outcome of the original LPA decision"
133
+ },
134
+ "applicationDecisionDate": {
135
+ "type": ["string", "null"],
136
+ "format": "date-time",
137
+ "examples": ["2023-07-27T20:30:00.000Z"],
138
+ "description": "The date of the original LPA decision"
139
+ },
140
+ "applicationDecisionAppealed": {
141
+ "type": ["boolean", "null"],
142
+ "description": "Whether an appeal was made against the original LPA decision"
143
+ },
144
+ "appealDecisionDate": {
145
+ "type": ["string", "null"],
146
+ "format": "date-time",
147
+ "examples": ["2023-07-27T20:30:00.000Z"],
148
+ "description": "The date of the appeal against the original LPA decision"
149
+ },
150
+ "caseSubmissionDueDate": {
151
+ "type": ["string", "null"],
152
+ "format": "date-time",
153
+ "examples": ["2023-07-27T20:30:00.000Z"],
154
+ "description": "The statutory deadline for submitting an appeal from the original LPA decision date",
155
+ "$comment": "Calculated from the applicationDecisionDate"
156
+ },
157
+ "siteAddressLine1": {
158
+ "type": ["string", "null"],
159
+ "examples": ["96 The Avenue"],
160
+ "description": "First line of address for the appeal site"
161
+ },
162
+ "siteAddressLine2": {
163
+ "type": ["string", "null"],
164
+ "description": "Second line of address for the appeal site"
165
+ },
166
+ "siteAddressTown": {
167
+ "type": ["string", "null"],
168
+ "examples": ["Maidstone"],
169
+ "description": "Town / City of the site address"
170
+ },
171
+ "siteAddressCounty": {
172
+ "type": ["string", "null"],
173
+ "examples": ["Kent"],
174
+ "description": "County of the site address"
175
+ },
176
+ "siteAddressPostcode": {
177
+ "type": ["string", "null"],
178
+ "examples": ["MD21 5XY"],
179
+ "description": "Postal code of the site address"
180
+ },
181
+ "siteGridReferenceEasting": {
182
+ "type": ["string", "null"],
183
+ "pattern": "^\\d{6,7}$",
184
+ "examples": ["357144"]
185
+ },
186
+ "siteGridReferenceNorthing": {
187
+ "type": ["string", "null"],
188
+ "pattern": "^\\d{6,7}$",
189
+ "examples": ["400534"]
190
+ },
191
+ "contactAddressLine1": {
192
+ "type": ["string", "null"],
193
+ "examples": ["96 The Avenue"],
194
+ "description": "First line of address for the appeal site"
195
+ },
196
+ "contactAddressLine2": {
197
+ "type": ["string", "null"],
198
+ "description": "Second line of address for the appeal site"
199
+ },
200
+ "contactAddressTown": {
201
+ "type": ["string", "null"],
202
+ "examples": ["Maidstone"],
203
+ "description": "Town / City of the site address"
204
+ },
205
+ "contactAddressCounty": {
206
+ "type": ["string", "null"],
207
+ "examples": ["Kent"],
208
+ "description": "County of the site address"
209
+ },
210
+ "contactAddressPostcode": {
211
+ "type": ["string", "null"],
212
+ "examples": ["MD21 5XY"],
213
+ "description": "Postal code of the site address"
214
+ },
215
+ "contactGridReferenceEasting": {
216
+ "type": ["string", "null"],
217
+ "pattern": "^\\d{6,7}$",
218
+ "examples": ["357144"]
219
+ },
220
+ "contactGridReferenceNorthing": {
221
+ "type": ["string", "null"],
222
+ "pattern": "^\\d{6,7}$",
223
+ "examples": ["400534"]
224
+ },
225
+ "siteAccessDetails": {
226
+ "type": ["array", "null"],
227
+ "items": {
228
+ "type": "string",
229
+ "examples": ["The site is accessible through the small gate on the left of the property"]
230
+ },
231
+ "description": "Provided information on site accessibility"
232
+ },
233
+ "siteSafetyDetails": {
234
+ "type": ["array", "null"],
235
+ "items": {
236
+ "type": "string",
237
+ "examples": ["There is an aggressive dog in the property"]
238
+ },
239
+ "description": "Provided information on site health and safety"
240
+ },
241
+ "appellantCostsAppliedFor": {
242
+ "type": ["boolean", "null"],
243
+ "description": "Indicates if the appellant has applied for costs",
244
+ "$comment": "Also available on HAS cases"
245
+ },
246
+ "appellantProcedurePreference": {
247
+ "type": ["string", "null"],
248
+ "enum": ["written", "hearing", "inquiry", null],
249
+ "description": "The procedure preference indicated by the appellant"
250
+ },
251
+ "appellantProcedurePreferenceDetails": {
252
+ "type": ["string", "null"],
253
+ "description": "The procedure details preference indicated by the appellant"
254
+ },
255
+ "appellantProcedurePreferenceDuration": {
256
+ "type": ["number", "null"],
257
+ "description": "The duration of the procedure indicated by the appellant"
258
+ },
259
+ "appellantProcedurePreferenceWitnessCount": {
260
+ "type": ["number", "null"],
261
+ "description": "The number of witnesses appellant has suggested for inquiries preference"
262
+ },
263
+ "statusPlanningObligation": {
264
+ "type": ["string", "null"],
265
+ "description": "The planning obligation information, if available"
266
+ },
267
+ "nearbyCaseReferences": {
268
+ "type": ["array", "null"],
269
+ "items": {
270
+ "type": "string",
271
+ "examples": ["6000456", "8004567"]
272
+ },
273
+ "description": "A list of related case references known to the appellant and the LPA"
274
+ },
275
+ "namedIndividuals": {
276
+ "type": ["array", "null"],
277
+ "items": {
278
+ "type": "object",
279
+ "additionalProperties": true,
280
+ "properties": {
281
+ "firstName": {
282
+ "type": "string",
283
+ "description": "First name of the individual"
284
+ },
285
+ "lastName": {
286
+ "type": "string",
287
+ "description": "Last name of the individual"
288
+ },
289
+ "interestInLand": {
290
+ "type": "string",
291
+ "examples": ["owner, tenant, mortgage holder, other"],
292
+ "description": "Individual's interest in the appeal land"
293
+ },
294
+ "writtenOrVerbalPermission": {
295
+ "type": ["string", "null"],
296
+ "examples": ["yes"],
297
+ "description": "Whether the individual has permission to use the land"
298
+ }
299
+ },
300
+ "required": ["firstName", "lastName", "interestInLand", "writtenOrVerbalPermission"]
301
+ },
302
+ "description": "A list of individuals named on the enforcement notice"
303
+ },
304
+ "appealGrounds": {
305
+ "type": ["array", "null"],
306
+ "items": {
307
+ "type": "object",
308
+ "additionalProperties": true,
309
+ "properties": {
310
+ "groundRef": {
311
+ "type": "string",
312
+ "enum": ["a", "b", "c", "d", "e", "f", "g"],
313
+ "description": "Name of the appeal ground"
314
+ },
315
+ "factsForGround": {
316
+ "type": "string",
317
+ "description": "Facts supporting the appeal ground"
318
+ }
319
+ },
320
+ "required": ["groundRef", "factsForGround"]
321
+ },
322
+ "description": "A list of appeal grounds being pleaded"
323
+ }
324
+ }
325
+ }
@@ -49,6 +49,14 @@
49
49
  { "$ref": "appeals-components/appeal-advert.submission.schema.json" },
50
50
  { "$ref": "appeals-components/appellant-procedure-preference.schema.json" }
51
51
  ]
52
+ },
53
+ {
54
+ "$comment": "Enforcement notice",
55
+ "allOf": [
56
+ { "properties": { "caseType": { "enum": ["C"] } } },
57
+ { "$ref": "appeals-components/appeal-enforcement-submission.schema.json" },
58
+ { "$ref": "appeals-components/appellant-procedure-preference.schema.json" }
59
+ ]
52
60
  }
53
61
  ]
54
62
  },
@@ -116,6 +124,16 @@
116
124
  "plansDrawings",
117
125
  "newPlansDrawings",
118
126
  "otherNewDocuments",
127
+ "priorCorrespondenceWithPINS",
128
+ "enforcementNotice",
129
+ "enforcementNoticePlan",
130
+ "groundASupporting",
131
+ "groundBSupporting",
132
+ "groundCSupporting",
133
+ "groundDSupporting",
134
+ "groundESupporting",
135
+ "groundFSupporting",
136
+ "groundGSupporting",
119
137
  null,
120
138
  "environmentalAssessment"
121
139
  ],
package/src/schemas.d.ts CHANGED
@@ -802,7 +802,7 @@ export type AppealHASCase = (
802
802
  /**
803
803
  * padsSapId for PADS inspectors
804
804
  */
805
- padsSapId: string | null;
805
+ padsSapId?: string | null;
806
806
  [k: string]: unknown;
807
807
  };
808
808
 
@@ -1522,7 +1522,7 @@ export type AppealS78Case = (
1522
1522
  /**
1523
1523
  * padsSapId for PADS inspectors
1524
1524
  */
1525
- padsSapId: string | null;
1525
+ padsSapId?: string | null;
1526
1526
  /**
1527
1527
  * The nature of the appellant's interest in the appeal site
1528
1528
  */
@@ -3728,6 +3728,234 @@ export type AppellantProcedurePreferenceProperties = {
3728
3728
  */
3729
3729
  appellantProcedurePreferenceWitnessCount?: number | null;
3730
3730
  };
3731
+ /**
3732
+ * Schema defining any properties for enforcement appeals
3733
+ */
3734
+ export type AppellantEnforcementSubmissionProperties = (
3735
+ | {
3736
+ siteGridReferenceEasting: string;
3737
+ siteGridReferenceNorthing: string;
3738
+ }
3739
+ | {
3740
+ siteAddressLine1: string;
3741
+ siteAddressTown: string;
3742
+ siteAddressPostcode: string;
3743
+ }
3744
+ ) & {
3745
+ /**
3746
+ * Case submission id - link to draft submission created by appellant
3747
+ */
3748
+ submissionId: string;
3749
+ /**
3750
+ * The type of procedure for the appeal
3751
+ */
3752
+ caseProcedure: 'written' | 'hearing' | 'inquiry';
3753
+ /**
3754
+ * A unique identifier for the Local Planning Authority
3755
+ */
3756
+ lpaCode: string;
3757
+ /**
3758
+ * The date the appeal was submitted by the appellant
3759
+ */
3760
+ caseSubmittedDate: string;
3761
+ /**
3762
+ * Indicates if an enforcement notice is the reason for the appeal
3763
+ */
3764
+ enforcementNotice: boolean | null;
3765
+ /**
3766
+ * The unique identifier of the enforcement notice
3767
+ */
3768
+ enforcementReference: string;
3769
+ /**
3770
+ * The date the enforcement notice was issued
3771
+ */
3772
+ enforcementIssueDate: string;
3773
+ /**
3774
+ * The date the enforcement notice took effect
3775
+ */
3776
+ enforcementEffectiveDate: string;
3777
+ /**
3778
+ * The date the enforcement notice took effect
3779
+ */
3780
+ contactPlanningInspectorateDate?: string | null;
3781
+ /**
3782
+ * The individual's interest in the appeal land
3783
+ */
3784
+ interestInLand?: string | null;
3785
+ /**
3786
+ * If required, whether the individual has permission to use the appeal land
3787
+ */
3788
+ writtenOrVerbalPermission?: string | null;
3789
+ /**
3790
+ * A description of the alleged breach
3791
+ */
3792
+ descriptionOfAllegedBreach?: string | null;
3793
+ /**
3794
+ * Whether a planning application was made
3795
+ */
3796
+ applicationMadeAndFeePaid?: boolean | null;
3797
+ /**
3798
+ * Did the application relate to all or part of the development
3799
+ */
3800
+ applicationDevelopmentAllOrPart?: 'all-of-the-development' | 'part-of-the-development' | null;
3801
+ /**
3802
+ * A description of the proposed development
3803
+ */
3804
+ originalDevelopmentDescription?: string | null;
3805
+ /**
3806
+ * An updated description of the proposed development
3807
+ */
3808
+ changedDevelopmentDescription?: boolean | null;
3809
+ /**
3810
+ * The unique identifier of the LPA application
3811
+ */
3812
+ applicationReference?: string | null;
3813
+ /**
3814
+ * The date of the original LPA application
3815
+ */
3816
+ applicationDate?: string | null;
3817
+ /**
3818
+ * The outcome of the original LPA decision
3819
+ */
3820
+ applicationDecision?: 'granted' | 'refused' | 'not_received' | null;
3821
+ /**
3822
+ * The date of the original LPA decision
3823
+ */
3824
+ applicationDecisionDate?: string | null;
3825
+ /**
3826
+ * Whether an appeal was made against the original LPA decision
3827
+ */
3828
+ applicationDecisionAppealed?: boolean | null;
3829
+ /**
3830
+ * The date of the appeal against the original LPA decision
3831
+ */
3832
+ appealDecisionDate?: string | null;
3833
+ /**
3834
+ * The statutory deadline for submitting an appeal from the original LPA decision date
3835
+ */
3836
+ caseSubmissionDueDate: string | null;
3837
+ /**
3838
+ * First line of address for the appeal site
3839
+ */
3840
+ siteAddressLine1?: string | null;
3841
+ /**
3842
+ * Second line of address for the appeal site
3843
+ */
3844
+ siteAddressLine2?: string | null;
3845
+ /**
3846
+ * Town / City of the site address
3847
+ */
3848
+ siteAddressTown?: string | null;
3849
+ /**
3850
+ * County of the site address
3851
+ */
3852
+ siteAddressCounty?: string | null;
3853
+ /**
3854
+ * Postal code of the site address
3855
+ */
3856
+ siteAddressPostcode?: string | null;
3857
+ siteGridReferenceEasting?: string | null;
3858
+ siteGridReferenceNorthing?: string | null;
3859
+ /**
3860
+ * First line of address for the appeal site
3861
+ */
3862
+ contactAddressLine1?: string | null;
3863
+ /**
3864
+ * Second line of address for the appeal site
3865
+ */
3866
+ contactAddressLine2?: string | null;
3867
+ /**
3868
+ * Town / City of the site address
3869
+ */
3870
+ contactAddressTown?: string | null;
3871
+ /**
3872
+ * County of the site address
3873
+ */
3874
+ contactAddressCounty?: string | null;
3875
+ /**
3876
+ * Postal code of the site address
3877
+ */
3878
+ contactAddressPostcode?: string | null;
3879
+ contactGridReferenceEasting?: string | null;
3880
+ contactGridReferenceNorthing?: string | null;
3881
+ /**
3882
+ * Provided information on site accessibility
3883
+ */
3884
+ siteAccessDetails?: string[] | null;
3885
+ /**
3886
+ * Provided information on site health and safety
3887
+ */
3888
+ siteSafetyDetails?: string[] | null;
3889
+ /**
3890
+ * Indicates if the appellant has applied for costs
3891
+ */
3892
+ appellantCostsAppliedFor?: boolean | null;
3893
+ /**
3894
+ * The procedure preference indicated by the appellant
3895
+ */
3896
+ appellantProcedurePreference?: 'written' | 'hearing' | 'inquiry' | null;
3897
+ /**
3898
+ * The procedure details preference indicated by the appellant
3899
+ */
3900
+ appellantProcedurePreferenceDetails?: string | null;
3901
+ /**
3902
+ * The duration of the procedure indicated by the appellant
3903
+ */
3904
+ appellantProcedurePreferenceDuration?: number | null;
3905
+ /**
3906
+ * The number of witnesses appellant has suggested for inquiries preference
3907
+ */
3908
+ appellantProcedurePreferenceWitnessCount?: number | null;
3909
+ /**
3910
+ * The planning obligation information, if available
3911
+ */
3912
+ statusPlanningObligation?: string | null;
3913
+ /**
3914
+ * A list of related case references known to the appellant and the LPA
3915
+ */
3916
+ nearbyCaseReferences?: string[] | null;
3917
+ /**
3918
+ * A list of individuals named on the enforcement notice
3919
+ */
3920
+ namedIndividuals?:
3921
+ | {
3922
+ /**
3923
+ * First name of the individual
3924
+ */
3925
+ firstName: string;
3926
+ /**
3927
+ * Last name of the individual
3928
+ */
3929
+ lastName: string;
3930
+ /**
3931
+ * Individual's interest in the appeal land
3932
+ */
3933
+ interestInLand: string;
3934
+ /**
3935
+ * Whether the individual has permission to use the land
3936
+ */
3937
+ writtenOrVerbalPermission: string | null;
3938
+ [k: string]: unknown;
3939
+ }[]
3940
+ | null;
3941
+ /**
3942
+ * A list of appeal grounds being pleaded
3943
+ */
3944
+ appealGrounds?:
3945
+ | {
3946
+ /**
3947
+ * Name of the appeal ground
3948
+ */
3949
+ groundRef: 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g';
3950
+ /**
3951
+ * Facts supporting the appeal ground
3952
+ */
3953
+ factsForGround: string;
3954
+ [k: string]: unknown;
3955
+ }[]
3956
+ | null;
3957
+ [k: string]: unknown;
3958
+ };
3731
3959
 
3732
3960
  /**
3733
3961
  * Schema defining the data produced by the Front-Office when an appeal is requested
@@ -3754,6 +3982,10 @@ export interface AppellantSubmissionCommand {
3754
3982
  } & AppellantCommonSubmissionProperties &
3755
3983
  AppellantHASSubmissionProperties &
3756
3984
  AdvertSpecificProperties &
3985
+ AppellantProcedurePreferenceProperties)
3986
+ | ({
3987
+ caseType?: 'C';
3988
+ } & AppellantEnforcementSubmissionProperties &
3757
3989
  AppellantProcedurePreferenceProperties);
3758
3990
  documents: {
3759
3991
  /**
@@ -3804,6 +4036,16 @@ export interface AppellantSubmissionCommand {
3804
4036
  | 'plansDrawings'
3805
4037
  | 'newPlansDrawings'
3806
4038
  | 'otherNewDocuments'
4039
+ | 'priorCorrespondenceWithPINS'
4040
+ | 'enforcementNotice'
4041
+ | 'enforcementNoticePlan'
4042
+ | 'groundASupporting'
4043
+ | 'groundBSupporting'
4044
+ | 'groundCSupporting'
4045
+ | 'groundDSupporting'
4046
+ | 'groundESupporting'
4047
+ | 'groundFSupporting'
4048
+ | 'groundGSupporting'
3807
4049
  | null
3808
4050
  | 'environmentalAssessment';
3809
4051
  [k: string]: unknown;
@@ -0,0 +1,187 @@
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 = 'appellant-submission.schema.json';
8
+
9
+ const user = {
10
+ salutation: 'nisi aute tempor Ut',
11
+ firstName: null,
12
+ lastName: null,
13
+ emailAddress: null,
14
+ telephoneNumber: 'irure',
15
+ organisation: null,
16
+ serviceUserType: 'Appellant'
17
+ };
18
+
19
+ const document = {
20
+ documentId: 'Lorem',
21
+ filename: 'sunt',
22
+ originalFilename: 'dolore fugiat',
23
+ size: 663667,
24
+ mime: 'consequat veniam ipsum ullamco sint',
25
+ documentURI: 'reprehenderit',
26
+ dateCreated: '1904-04-10T14:53:32.0Z',
27
+ documentType: 'priorCorrespondenceWithPINS'
28
+ };
29
+
30
+ const exampleEnforcementSubmissionSchema = {
31
+ casedata: {
32
+ caseType: 'C',
33
+ submissionId: '4b78fc58-fcb0-70d6-5dea-b9b774f802d6',
34
+ caseProcedure: 'written',
35
+ lpaCode: 'cupidatat',
36
+ caseSubmittedDate: '1890-12-28T22:49:47.0Z',
37
+ enforcementNotice: true,
38
+ enforcementReference: '12345',
39
+ enforcementIssueDate: '1890-12-28T22:49:47.0Z',
40
+ enforcementEffectiveDate: '1890-12-28T22:49:47.0Z',
41
+ contactPlanningInspectorateDate: null,
42
+ interestInLand: 'owner',
43
+ writtenOrVerbalPermission: null,
44
+ descriptionOfAllegedBreach: 'a new chimney',
45
+ applicationMadeAndFeePaid: true,
46
+ applicationDevelopmentAllOrPart: 'all-of-the-development',
47
+ originalDevelopmentDescription: 'in amet',
48
+ changedDevelopmentDescription: true,
49
+ applicationReference: '43010/APP/1/290572',
50
+ applicationDate: '1890-12-28T22:49:47.0Z',
51
+ applicationDecision: 'granted',
52
+ applicationDecisionAppealed: false,
53
+ applicationDecisionDate: '1890-12-28T22:49:47.0Z',
54
+ appealDecisionDate: null,
55
+ caseSubmissionDueDate: null,
56
+ siteAddressLine1: 'occaecat aliquip non pariatur consequat',
57
+ siteAddressLine2: null,
58
+ siteAddressTown: 'et',
59
+ siteAddressCounty: 'velit',
60
+ siteAddressPostcode: 'nisi minim laboris sed',
61
+ siteGridReferenceEasting: '357144',
62
+ siteGridReferenceNorthing: '400534',
63
+ contactAddressLine1: null,
64
+ contactAddressLine2: null,
65
+ contactAddressTown: null,
66
+ contactAddressCounty: null,
67
+ contactAddressPostcode: null,
68
+ contactGridReferenceEasting: null,
69
+ contactGridReferenceNorthing: null,
70
+ siteAccessDetails: ['laborum', 'aute velit quis', 'dolor'],
71
+ siteSafetyDetails: ['velit aliqua', 'qui', 'tempor Duis ut dolore', 'anim Ut in ea', 'eiusmod laborum'],
72
+ appellantCostsAppliedFor: false,
73
+ appellantProcedurePreference: null,
74
+ appellantProcedurePreferenceDetails: 'eiusmod ex exercitation',
75
+ appellantProcedurePreferenceDuration: 10816414.941069126,
76
+ appellantProcedurePreferenceWitnessCount: -91300569.36444478,
77
+ statusPlanningObligation: 'proident aute',
78
+ nearbyCaseReferences: ['incididunt'],
79
+ namedIndividuals: [
80
+ {
81
+ firstName: 'Bob',
82
+ lastName: 'Bobberson',
83
+ interestInLand: 'owner',
84
+ writtenOrVerbalPermission: 'yes'
85
+ }
86
+ ],
87
+ appealGrounds: [
88
+ {
89
+ groundRef: 'a',
90
+ factsForGround: 'I like Christmas'
91
+ }
92
+ ]
93
+ },
94
+ documents: [document],
95
+ users: [user]
96
+ };
97
+
98
+ describe('enforcement submission command schema', () => {
99
+ const schemas = loadAllSchemasSync();
100
+ const flatSchemas = Object.values(schemas).reduce((a, c) => ({ ...a, ...c }), {});
101
+ const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
102
+ addFormats(ajv);
103
+
104
+ it('should allow valid schema for enforcement', () => {
105
+ const validationResult = ajv.validate(schema, exampleEnforcementSubmissionSchema);
106
+ assert.strictEqual(validationResult, true);
107
+ });
108
+
109
+ it('should enforce eastings-northings pattern', () => {
110
+ const invalidPatterns = [
111
+ { easting: 'abc123', northing: '400534', description: 'easting with letters' },
112
+ { easting: '1234567*', northing: '400534', description: 'easting too long' },
113
+ { easting: '12345', northing: '400534', description: 'easting too short' },
114
+ { easting: '357144', northing: 'xyz123', description: 'northing with letters' },
115
+ { easting: '357144', northing: '12345678', description: 'northing too long' },
116
+ { easting: '357144', northing: '12345', description: 'northing too short' },
117
+ { easting: '', northing: '400534', description: 'empty easting' },
118
+ { easting: '357144', northing: '', description: 'empty northing' }
119
+ ];
120
+
121
+ for (const pattern of invalidPatterns) {
122
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
123
+ test.casedata.siteGridReferenceEasting = pattern.easting;
124
+ test.casedata.siteGridReferenceNorthing = pattern.northing;
125
+
126
+ const validationResult = ajv.validate(schema, test);
127
+ assert.strictEqual(validationResult, false, `Expected validation to fail for ${pattern.description}`);
128
+ }
129
+ });
130
+
131
+ it('should allow only eastings-northings', () => {
132
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
133
+ test.casedata.siteAddressPostcode = undefined;
134
+ test.casedata.siteAddressLine1 = null;
135
+ test.casedata.siteAddressTown = null;
136
+ const validationResult = ajv.validate(schema, test);
137
+ assert.strictEqual(validationResult, true);
138
+ });
139
+
140
+ it('should allow only site address', () => {
141
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
142
+ test.casedata.siteGridReferenceEasting = undefined;
143
+ test.casedata.siteGridReferenceNorthing = null;
144
+ const validationResult = ajv.validate(schema, test);
145
+ assert.strictEqual(validationResult, true);
146
+ });
147
+
148
+ it('should enforce either site address or easting-northing', () => {
149
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
150
+ test.casedata.siteAddressPostcode = undefined;
151
+ test.casedata.siteAddressLine1 = null;
152
+ test.casedata.siteAddressTown = null;
153
+ test.casedata.siteGridReferenceEasting = undefined;
154
+ test.casedata.siteGridReferenceNorthing = null;
155
+ const validationResult = ajv.validate(schema, test);
156
+ assert.strictEqual(validationResult, false);
157
+ });
158
+
159
+ it('should reject missing root property', () => {
160
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
161
+ delete test.casedata.caseType;
162
+ const validationResult = ajv.validate(schema, test);
163
+ assert.strictEqual(validationResult, false);
164
+ assert.strictEqual(
165
+ ajv.errors.some((x) => x.message === "must have required property 'caseType'"),
166
+ true
167
+ );
168
+ });
169
+
170
+ it('should reject missing nested property', () => {
171
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
172
+ delete test.casedata.caseProcedure;
173
+ const validationResult = ajv.validate(schema, test);
174
+ assert.strictEqual(validationResult, false);
175
+ assert.strictEqual(
176
+ ajv.errors.some((x) => x.message === "must have required property 'caseProcedure'"),
177
+ true
178
+ );
179
+ });
180
+
181
+ it('should allow additional props', () => {
182
+ const test = structuredClone(exampleEnforcementSubmissionSchema);
183
+ test.test = 1;
184
+ const validationResult = ajv.validate(schema, test);
185
+ assert.strictEqual(validationResult, true);
186
+ });
187
+ });