@planning-inspectorate/data-model 2.29.0 → 2.31.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/docs/README.md +4 -0
- package/docs/appeal-document.md +96 -93
- package/docs/appeal-s78-definitions-significantchangesaffectingapplication.md +14 -0
- package/docs/appeal-s78-definitions.md +3 -0
- package/docs/appeal-s78-if.md +35 -0
- package/docs/appeal-s78-then.md +31 -0
- package/docs/appeal-s78.md +360 -197
- package/package.json +1 -1
- package/pins_data_model/models/model_appeal_document.py +4 -1
- package/pins_data_model/models/model_appeal_event.py +1 -1
- package/pins_data_model/models/model_appeal_event_estimate.py +1 -1
- package/pins_data_model/models/model_appeal_has.py +1 -1
- package/pins_data_model/models/model_appeal_representation.py +1 -1
- package/pins_data_model/models/model_appeal_s78.py +154 -74
- package/pins_data_model/models/model_dart_response.py +1 -1
- package/pins_data_model/models/model_entraid.py +1 -1
- package/pins_data_model/models/model_folder.py +1 -1
- package/pins_data_model/models/model_gettimesheets_response.py +1 -1
- package/pins_data_model/models/model_listed_building.py +1 -1
- package/pins_data_model/models/model_listed_buildings.py +1 -1
- package/pins_data_model/models/model_nsip_document.py +1 -1
- package/pins_data_model/models/model_nsip_exam_timetable.py +1 -1
- package/pins_data_model/models/model_nsip_invoice.py +1 -1
- package/pins_data_model/models/model_nsip_meeting.py +1 -1
- package/pins_data_model/models/model_nsip_project.py +1 -1
- package/pins_data_model/models/model_nsip_project_update.py +1 -1
- package/pins_data_model/models/model_nsip_representation.py +1 -1
- package/pins_data_model/models/model_nsip_subscription.py +1 -1
- package/pins_data_model/models/model_pins_inspector.py +1 -1
- package/pins_data_model/models/model_pins_lpa.py +1 -1
- package/pins_data_model/models/model_s51_advice.py +1 -1
- package/pins_data_model/models/model_service_user.py +1 -1
- package/schemas/appeal-document.schema.json +3 -0
- package/schemas/appeal-s78.schema.json +72 -7
- package/schemas/commands/appellant-submission.schema.json +2 -1
- package/src/enums.cjs +26 -0
- package/src/enums.d.ts +24 -0
- package/src/enums.js +24 -0
- package/src/index.cjs +26 -0
- package/src/schemas.d.ts +54 -8
- package/tests/appeals/appeal-s78.test.js +21 -1
- package/tests/appeals/expedited-appeals.test.js +238 -0
|
@@ -0,0 +1,238 @@
|
|
|
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 = 'appeal-s78.schema.json';
|
|
8
|
+
|
|
9
|
+
const expeditedAppealPayload = {
|
|
10
|
+
caseId: 1001,
|
|
11
|
+
caseReference: '7000001',
|
|
12
|
+
submissionId: '7f45748c-2342-4b59-b934-ad6183e8d834',
|
|
13
|
+
caseStatus: 'validation',
|
|
14
|
+
caseType: 'S',
|
|
15
|
+
caseProcedure: 'writtenPart1', // New procedure type
|
|
16
|
+
lpaCode: 'Q9999',
|
|
17
|
+
caseOfficerId: 'officer-1',
|
|
18
|
+
inspectorId: 'inspector-1',
|
|
19
|
+
allocationLevel: 'A',
|
|
20
|
+
allocationBand: 1,
|
|
21
|
+
caseSpecialisms: ['Access'],
|
|
22
|
+
caseSubmittedDate: '2024-01-01T12:00:00.000Z',
|
|
23
|
+
caseCreatedDate: '2024-01-01T12:00:00.000Z',
|
|
24
|
+
caseUpdatedDate: '2024-01-01T12:00:00.000Z',
|
|
25
|
+
caseValidDate: '2024-01-02T12:00:00.000Z',
|
|
26
|
+
caseValidationDate: '2024-01-02T12:00:00.000Z',
|
|
27
|
+
caseValidationOutcome: 'valid',
|
|
28
|
+
caseValidationInvalidDetails: null,
|
|
29
|
+
caseValidationIncompleteDetails: null,
|
|
30
|
+
caseExtensionDate: null,
|
|
31
|
+
caseStartedDate: '2024-01-03T12:00:00.000Z',
|
|
32
|
+
casePublishedDate: null,
|
|
33
|
+
linkedCaseStatus: null,
|
|
34
|
+
leadCaseReference: null,
|
|
35
|
+
lpaQuestionnaireDueDate: '2024-01-10T12:00:00.000Z',
|
|
36
|
+
lpaQuestionnaireSubmittedDate: '2024-01-09T12:00:00.000Z',
|
|
37
|
+
lpaQuestionnaireCreatedDate: '2024-01-09T12:00:00.000Z',
|
|
38
|
+
lpaQuestionnairePublishedDate: '2024-01-10T12:00:00.000Z',
|
|
39
|
+
lpaQuestionnaireValidationOutcome: 'complete',
|
|
40
|
+
lpaQuestionnaireValidationOutcomeDate: '2024-01-10T12:00:00.000Z',
|
|
41
|
+
lpaQuestionnaireValidationDetails: null,
|
|
42
|
+
lpaStatement: 'LPA Statement content',
|
|
43
|
+
caseWithdrawnDate: null,
|
|
44
|
+
caseTransferredDate: null,
|
|
45
|
+
transferredCaseClosedDate: null,
|
|
46
|
+
caseDecisionOutcomeDate: null,
|
|
47
|
+
caseDecisionPublishedDate: null,
|
|
48
|
+
caseDecisionOutcome: null,
|
|
49
|
+
caseCompletedDate: null,
|
|
50
|
+
enforcementNotice: false,
|
|
51
|
+
applicationReference: 'APP/2024/0001',
|
|
52
|
+
applicationDate: '2023-12-01T12:00:00.000Z',
|
|
53
|
+
applicationDecision: 'refused',
|
|
54
|
+
applicationDecisionDate: '2023-12-20T12:00:00.000Z',
|
|
55
|
+
caseSubmissionDueDate: '2024-06-20T12:00:00.000Z',
|
|
56
|
+
siteAddressLine1: '123 Test Street',
|
|
57
|
+
siteAddressTown: 'Test Town',
|
|
58
|
+
siteAddressPostcode: 'TE1 1ST',
|
|
59
|
+
siteAccessDetails: ['Standard access'],
|
|
60
|
+
siteSafetyDetails: null,
|
|
61
|
+
siteAreaSquareMetres: 100,
|
|
62
|
+
floorSpaceSquareMetres: 50,
|
|
63
|
+
isCorrectAppealType: true,
|
|
64
|
+
isGreenBelt: false,
|
|
65
|
+
inConservationArea: false,
|
|
66
|
+
ownsAllLand: true,
|
|
67
|
+
ownsSomeLand: false,
|
|
68
|
+
knowsOtherOwners: 'No',
|
|
69
|
+
knowsAllOwners: 'Yes',
|
|
70
|
+
advertisedAppeal: false,
|
|
71
|
+
notificationMethod: ['notice'],
|
|
72
|
+
ownersInformed: true,
|
|
73
|
+
originalDevelopmentDescription: 'Description of development',
|
|
74
|
+
changedDevelopmentDescription: false,
|
|
75
|
+
newConditionDetails: null,
|
|
76
|
+
nearbyCaseReferences: [],
|
|
77
|
+
neighbouringSiteAddresses: [],
|
|
78
|
+
affectedListedBuildingNumbers: [],
|
|
79
|
+
changedListedBuildingNumbers: [],
|
|
80
|
+
appellantCostsAppliedFor: false,
|
|
81
|
+
lpaCostsAppliedFor: false,
|
|
82
|
+
agriculturalHolding: false,
|
|
83
|
+
tenantAgriculturalHolding: false,
|
|
84
|
+
otherTenantsAgriculturalHolding: false,
|
|
85
|
+
informedTenantsAgriculturalHolding: false,
|
|
86
|
+
appellantProcedurePreference: 'written',
|
|
87
|
+
appellantProcedurePreferenceDetails: null,
|
|
88
|
+
appellantProcedurePreferenceDuration: null,
|
|
89
|
+
appellantProcedurePreferenceWitnessCount: null,
|
|
90
|
+
statusPlanningObligation: 'Finalised',
|
|
91
|
+
affectsScheduledMonument: false,
|
|
92
|
+
hasProtectedSpecies: false,
|
|
93
|
+
isAonbNationalLandscape: false,
|
|
94
|
+
designatedSitesNames: [],
|
|
95
|
+
isGypsyOrTravellerSite: false,
|
|
96
|
+
isPublicRightOfWay: false,
|
|
97
|
+
eiaEnvironmentalImpactSchedule: 'schedule-2',
|
|
98
|
+
eiaDevelopmentDescription: 'infrastructure-projects',
|
|
99
|
+
eiaSensitiveAreaDetails: null,
|
|
100
|
+
eiaColumnTwoThreshold: false,
|
|
101
|
+
eiaScreeningOpinion: false,
|
|
102
|
+
eiaRequiresEnvironmentalStatement: false,
|
|
103
|
+
eiaCompletedEnvironmentalStatement: false,
|
|
104
|
+
hasStatutoryConsultees: false,
|
|
105
|
+
consultedBodiesDetails: null,
|
|
106
|
+
hasInfrastructureLevy: false,
|
|
107
|
+
isInfrastructureLevyFormallyAdopted: false,
|
|
108
|
+
infrastructureLevyAdoptedDate: null,
|
|
109
|
+
infrastructureLevyExpectedDate: null,
|
|
110
|
+
lpaProcedurePreference: 'written',
|
|
111
|
+
lpaProcedurePreferenceDetails: null,
|
|
112
|
+
lpaProcedurePreferenceDuration: null,
|
|
113
|
+
caseworkReason: null,
|
|
114
|
+
developmentType: 'minor-dwellings',
|
|
115
|
+
importantInformation: null,
|
|
116
|
+
jurisdiction: 'England',
|
|
117
|
+
redeterminedIndicator: 'No',
|
|
118
|
+
dateCostsReportDespatched: null,
|
|
119
|
+
dateNotRecoveredOrDerecovered: null,
|
|
120
|
+
dateRecovered: null,
|
|
121
|
+
originalCaseDecisionDate: null,
|
|
122
|
+
targetDate: '2024-04-01T12:00:00.000Z',
|
|
123
|
+
reasonForAppealAppellant: 'This is an expedited appeal because...', // New attribute
|
|
124
|
+
screeningOpinionIndicatesEiaRequired: false, // New attribute
|
|
125
|
+
significantChangesAffectingApplicationAppellant: [
|
|
126
|
+
// New multi-select
|
|
127
|
+
{ value: 'adopted-a-new-local-plan', comment: 'Plan adopted last month' },
|
|
128
|
+
{ value: 'other', comment: 'General policy shift' }
|
|
129
|
+
],
|
|
130
|
+
significantChangesAffectingApplicationLpa: null, // Optional
|
|
131
|
+
appellantCommentsSubmittedDate: null,
|
|
132
|
+
appellantStatementSubmittedDate: null,
|
|
133
|
+
appellantProofsSubmittedDate: null,
|
|
134
|
+
lpaCommentsSubmittedDate: '2024-01-15T12:00:00.000Z',
|
|
135
|
+
lpaProofsSubmittedDate: '2024-01-15T12:00:00.000Z',
|
|
136
|
+
lpaStatementSubmittedDate: '2024-01-15T12:00:00.000Z',
|
|
137
|
+
siteNoticesSentDate: '2024-01-05T12:00:00.000Z',
|
|
138
|
+
reasonForNeighbourVisits: 'To check impact on privacy',
|
|
139
|
+
numberOfResidencesNetChange: 1,
|
|
140
|
+
siteGridReferenceEasting: '357144',
|
|
141
|
+
siteGridReferenceNorthing: '400534',
|
|
142
|
+
siteViewableFromRoad: true,
|
|
143
|
+
siteWithinSSSI: false,
|
|
144
|
+
typeOfPlanningApplication: 'full-appeal'
|
|
145
|
+
// statementDueDate, interestedPartyRepsDueDate, finalCommentsDueDate, proofsOfEvidenceDueDate omitted (now optional)
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
describe('Expedited S78 Appeals', () => {
|
|
149
|
+
const schemas = loadAllSchemasSync();
|
|
150
|
+
const flatSchemas = Object.values(schemas).reduce((a, c) => ({ ...a, ...c }), {});
|
|
151
|
+
const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
|
|
152
|
+
addFormats(ajv);
|
|
153
|
+
|
|
154
|
+
it('should allow valid expedited appeal payload with new attributes and missing optional due dates', () => {
|
|
155
|
+
const test = structuredClone(expeditedAppealPayload);
|
|
156
|
+
const validationResult = ajv.validate(schema, test);
|
|
157
|
+
if (!validationResult) {
|
|
158
|
+
console.error(ajv.errors);
|
|
159
|
+
}
|
|
160
|
+
assert.strictEqual(validationResult, true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should allow writtenPart2 procedure type', () => {
|
|
164
|
+
const test = structuredClone(expeditedAppealPayload);
|
|
165
|
+
test.caseProcedure = 'writtenPart2';
|
|
166
|
+
const validationResult = ajv.validate(schema, test);
|
|
167
|
+
if (!validationResult) {
|
|
168
|
+
console.error(ajv.errors);
|
|
169
|
+
}
|
|
170
|
+
assert.strictEqual(validationResult, true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should allow null for new attributes', () => {
|
|
174
|
+
const test = structuredClone(expeditedAppealPayload);
|
|
175
|
+
test.reasonForAppealAppellant = null;
|
|
176
|
+
test.screeningOpinionIndicatesEiaRequired = null;
|
|
177
|
+
test.significantChangesAffectingApplicationAppellant = null;
|
|
178
|
+
test.significantChangesAffectingApplicationLpa = null;
|
|
179
|
+
const validationResult = ajv.validate(schema, test);
|
|
180
|
+
if (!validationResult) {
|
|
181
|
+
console.error(ajv.errors);
|
|
182
|
+
}
|
|
183
|
+
assert.strictEqual(validationResult, true);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should enforce maxLength for reasonForAppealAppellant', () => {
|
|
187
|
+
const test = structuredClone(expeditedAppealPayload);
|
|
188
|
+
test.reasonForAppealAppellant = 'a'.repeat(251);
|
|
189
|
+
const validationResult = ajv.validate(schema, test);
|
|
190
|
+
assert.strictEqual(validationResult, false);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should enforce enum for significantChangesAffectingApplication', () => {
|
|
194
|
+
const test = structuredClone(expeditedAppealPayload);
|
|
195
|
+
test.significantChangesAffectingApplicationAppellant = [{ value: 'invalid-enum-value', comment: 'some comment' }];
|
|
196
|
+
const validationResult = ajv.validate(schema, test);
|
|
197
|
+
assert.strictEqual(validationResult, false);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should allow new document types in appeal-document schema', () => {
|
|
201
|
+
const docSchema = 'appeal-document.schema.json';
|
|
202
|
+
const testDoc = {
|
|
203
|
+
documentId: 'doc-1',
|
|
204
|
+
caseId: 1001,
|
|
205
|
+
caseReference: '7000001',
|
|
206
|
+
version: 1,
|
|
207
|
+
filename: 'test.pdf',
|
|
208
|
+
originalFilename: 'test.pdf',
|
|
209
|
+
size: 1024,
|
|
210
|
+
mime: 'application/pdf',
|
|
211
|
+
documentURI: 'http://example.com/test.pdf',
|
|
212
|
+
publishedDocumentURI: null,
|
|
213
|
+
virusCheckStatus: 'scanned',
|
|
214
|
+
fileMD5: 'hash',
|
|
215
|
+
dateCreated: '2024-01-01T12:00:00.000Z',
|
|
216
|
+
dateReceived: '2024-01-01T12:00:00.000Z',
|
|
217
|
+
datePublished: null,
|
|
218
|
+
lastModified: '2024-01-01T12:00:00.000Z',
|
|
219
|
+
caseType: 'S',
|
|
220
|
+
redactedStatus: 'not_redacted',
|
|
221
|
+
documentType: 'otherSupportingInformationAppellant', // New type
|
|
222
|
+
sourceSystem: 'back-office-appeals',
|
|
223
|
+
origin: 'citizen',
|
|
224
|
+
owner: 'appellant',
|
|
225
|
+
author: 'appellant',
|
|
226
|
+
description: 'Other supporting info',
|
|
227
|
+
caseStage: 'appellant-case',
|
|
228
|
+
horizonFolderId: null
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const validationResult1 = ajv.validate(docSchema, testDoc);
|
|
232
|
+
assert.strictEqual(validationResult1, true);
|
|
233
|
+
|
|
234
|
+
testDoc.documentType = 'eiaEnvironmentalStatementAppellant'; // New type
|
|
235
|
+
const validationResult2 = ajv.validate(docSchema, testDoc);
|
|
236
|
+
assert.strictEqual(validationResult2, true);
|
|
237
|
+
});
|
|
238
|
+
});
|