@planning-inspectorate/data-model 2.40.0 → 2.42.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/.env.example +6 -0
- package/docs/nsip-invoice.md +19 -20
- package/load-test/appeals/README.md +35 -0
- package/load-test/appeals/generate-appeals-messages.js +456 -0
- package/load-test/appeals/run-load-script.js +58 -0
- package/load-test/service-bus-client.js +89 -0
- package/package.json +4 -1
- package/pins_data_model/models/model_nsip_invoice.py +8 -9
- package/schemas/nsip-invoice.schema.json +4 -16
- package/src/schemas.d.ts +9 -15
package/.env.example
ADDED
package/docs/nsip-invoice.md
CHANGED
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
| Property | Type | Required | Nullable | Defined by |
|
|
8
8
|
| :------------------------------------------------ | :-------- | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------ |
|
|
9
9
|
| [caseId](#caseid) | `integer` | Required | cannot be null | [NSIP Invoice](nsip-invoice-properties-caseid.md "nsip-invoice.schema.json#/properties/caseId") |
|
|
10
|
-
| [caseReference](#casereference) | `string` |
|
|
11
|
-
| [invoiceStage](#invoicestage) | `string` | Required |
|
|
10
|
+
| [caseReference](#casereference) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-casereference.md "nsip-invoice.schema.json#/properties/caseReference") |
|
|
11
|
+
| [invoiceStage](#invoicestage) | `string` | Required | cannot be null | [NSIP Invoice](nsip-invoice-properties-invoicestage.md "nsip-invoice.schema.json#/properties/invoiceStage") |
|
|
12
12
|
| [invoiceNumber](#invoicenumber) | `string` | Required | cannot be null | [NSIP Invoice](nsip-invoice-properties-invoicenumber.md "nsip-invoice.schema.json#/properties/invoiceNumber") |
|
|
13
|
-
| [amountDue](#amountdue) | `number` |
|
|
14
|
-
| [paymentDueDate](#paymentduedate) | `string` |
|
|
15
|
-
| [invoicedDate](#invoiceddate) | `string` |
|
|
16
|
-
| [paymentDate](#paymentdate) | `string` |
|
|
17
|
-
| [refundCreditNoteNumber](#refundcreditnotenumber) | `string` |
|
|
18
|
-
| [refundAmount](#refundamount) | `number` |
|
|
19
|
-
| [refundIssueDate](#refundissuedate) | `string` |
|
|
13
|
+
| [amountDue](#amountdue) | `number` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-amountdue.md "nsip-invoice.schema.json#/properties/amountDue") |
|
|
14
|
+
| [paymentDueDate](#paymentduedate) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-paymentduedate.md "nsip-invoice.schema.json#/properties/paymentDueDate") |
|
|
15
|
+
| [invoicedDate](#invoiceddate) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-invoiceddate.md "nsip-invoice.schema.json#/properties/invoicedDate") |
|
|
16
|
+
| [paymentDate](#paymentdate) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-paymentdate.md "nsip-invoice.schema.json#/properties/paymentDate") |
|
|
17
|
+
| [refundCreditNoteNumber](#refundcreditnotenumber) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-refundcreditnotenumber.md "nsip-invoice.schema.json#/properties/refundCreditNoteNumber") |
|
|
18
|
+
| [refundAmount](#refundamount) | `number` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-refundamount.md "nsip-invoice.schema.json#/properties/refundAmount") |
|
|
19
|
+
| [refundIssueDate](#refundissuedate) | `string` | Optional | can be null | [NSIP Invoice](nsip-invoice-properties-refundissuedate.md "nsip-invoice.schema.json#/properties/refundIssueDate") |
|
|
20
20
|
| Additional Properties | Any | Optional | can be null | |
|
|
21
21
|
|
|
22
22
|
## caseId
|
|
@@ -49,7 +49,7 @@ The unique identifier within the Back Office. This is not the same as the case r
|
|
|
49
49
|
|
|
50
50
|
`caseReference`
|
|
51
51
|
|
|
52
|
-
* is
|
|
52
|
+
* is optional
|
|
53
53
|
|
|
54
54
|
* Type: `string`
|
|
55
55
|
|
|
@@ -77,7 +77,7 @@ Invoice stage
|
|
|
77
77
|
|
|
78
78
|
* Type: `string`
|
|
79
79
|
|
|
80
|
-
*
|
|
80
|
+
* cannot be null
|
|
81
81
|
|
|
82
82
|
* defined in: [NSIP Invoice](nsip-invoice-properties-invoicestage.md "nsip-invoice.schema.json#/properties/invoiceStage")
|
|
83
83
|
|
|
@@ -96,7 +96,6 @@ Invoice stage
|
|
|
96
96
|
| `"pre_examination"` | |
|
|
97
97
|
| `"initial_examination"` | |
|
|
98
98
|
| `"final_examination"` | |
|
|
99
|
-
| `null` | |
|
|
100
99
|
|
|
101
100
|
### invoiceStage Examples
|
|
102
101
|
|
|
@@ -134,11 +133,11 @@ Amount due
|
|
|
134
133
|
|
|
135
134
|
`amountDue`
|
|
136
135
|
|
|
137
|
-
* is
|
|
136
|
+
* is optional
|
|
138
137
|
|
|
139
138
|
* Type: `number`
|
|
140
139
|
|
|
141
|
-
*
|
|
140
|
+
* can be null
|
|
142
141
|
|
|
143
142
|
* defined in: [NSIP Invoice](nsip-invoice-properties-amountdue.md "nsip-invoice.schema.json#/properties/amountDue")
|
|
144
143
|
|
|
@@ -158,7 +157,7 @@ Payment due date
|
|
|
158
157
|
|
|
159
158
|
`paymentDueDate`
|
|
160
159
|
|
|
161
|
-
* is
|
|
160
|
+
* is optional
|
|
162
161
|
|
|
163
162
|
* Type: `string`
|
|
164
163
|
|
|
@@ -186,7 +185,7 @@ Invoiced date
|
|
|
186
185
|
|
|
187
186
|
`invoicedDate`
|
|
188
187
|
|
|
189
|
-
* is
|
|
188
|
+
* is optional
|
|
190
189
|
|
|
191
190
|
* Type: `string`
|
|
192
191
|
|
|
@@ -214,7 +213,7 @@ Date of payment
|
|
|
214
213
|
|
|
215
214
|
`paymentDate`
|
|
216
215
|
|
|
217
|
-
* is
|
|
216
|
+
* is optional
|
|
218
217
|
|
|
219
218
|
* Type: `string`
|
|
220
219
|
|
|
@@ -242,7 +241,7 @@ Refund credit note number
|
|
|
242
241
|
|
|
243
242
|
`refundCreditNoteNumber`
|
|
244
243
|
|
|
245
|
-
* is
|
|
244
|
+
* is optional
|
|
246
245
|
|
|
247
246
|
* Type: `string`
|
|
248
247
|
|
|
@@ -266,7 +265,7 @@ Amount refunded
|
|
|
266
265
|
|
|
267
266
|
`refundAmount`
|
|
268
267
|
|
|
269
|
-
* is
|
|
268
|
+
* is optional
|
|
270
269
|
|
|
271
270
|
* Type: `number`
|
|
272
271
|
|
|
@@ -290,7 +289,7 @@ Refund issue date
|
|
|
290
289
|
|
|
291
290
|
`refundIssueDate`
|
|
292
291
|
|
|
293
|
-
* is
|
|
292
|
+
* is optional
|
|
294
293
|
|
|
295
294
|
* Type: `string`
|
|
296
295
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Appeals Load Test Script
|
|
2
|
+
|
|
3
|
+
This script is designed to allow load testing of service bus message consumers without the capacity of the sender being a limiting factor. The service bus client/strategy is hopefully resuable for other services in future
|
|
4
|
+
|
|
5
|
+
`load-test-appeals` sends a specified quantity of appeal case messages with service users and documents to service bus
|
|
6
|
+
|
|
7
|
+
This was written as a quick solution to provide ODW with the ability to run a load test without having to generate actual appeals within the Appeals Service, as generating them Appeals side proved too slow
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- node
|
|
12
|
+
- Azure VPN
|
|
13
|
+
- Azure CLI interface
|
|
14
|
+
- Your user has the ability to send messages manually on the given service bus topic
|
|
15
|
+
- Unsure if this can be run in WSL currently as az login seems to fail
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
`npm ci`
|
|
20
|
+
|
|
21
|
+
Create an .env file in the route of the project based on the example
|
|
22
|
+
|
|
23
|
+
- LOAD_TEST_SIMULATE_SENDING: (true|false) logs to console if true rather than sending, usful for checking data before sending
|
|
24
|
+
- LOAD_TEST_SERVICE_BUS_NAMESPACE: the fully qualified namespace of the service bus instance
|
|
25
|
+
- APPEALS_LOAD_TEST_CASE_TYPE: (HAS|S78) the case type topic to send on
|
|
26
|
+
- APPEALS_LOAD_TEST_CASE_COUNT: how many appeals to generate
|
|
27
|
+
- APPEALS_LOAD_TEST_DOCUMENTS_PER_CASE: how many documents to generate for each case
|
|
28
|
+
- APPEALS_LOAD_TEST_BATCH_SIZE: how many appeals are sent to service bus in one go, used to improve the speed in which messages can be sent to service bus
|
|
29
|
+
|
|
30
|
+
## Run
|
|
31
|
+
|
|
32
|
+
- set the env vars as needed, retrieve the correct Service Bus namespace from Azure
|
|
33
|
+
- connect to Azure VPN
|
|
34
|
+
- `az login` - login as yourself and connect to test env subscription
|
|
35
|
+
- `npm run load-test-appeals`
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import Ajv from 'ajv';
|
|
3
|
+
import addFormats from 'ajv-formats';
|
|
4
|
+
import { loadAllSchemasSync } from '../../src/index.js';
|
|
5
|
+
|
|
6
|
+
const HAS_SCHEMA = 'appeal-has.schema.json';
|
|
7
|
+
const S78_SCHEMA = 'appeal-s78.schema.json';
|
|
8
|
+
const DOCUMENT_SCHEMA = 'appeal-document.schema.json';
|
|
9
|
+
const SERVICE_USER_SCHEMA = 'service-user.schema.json';
|
|
10
|
+
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
|
|
13
|
+
const BASE_HAS_CASE = {
|
|
14
|
+
caseId: null,
|
|
15
|
+
caseReference: 'mollit dolore dolor non culpa',
|
|
16
|
+
submissionId: null,
|
|
17
|
+
caseStatus: 'ready_to_start',
|
|
18
|
+
caseType: 'ZP',
|
|
19
|
+
caseProcedure: 'inquiry',
|
|
20
|
+
lpaCode: 'nulla',
|
|
21
|
+
caseOfficerId: null,
|
|
22
|
+
inspectorId: 'mollit in ullamco Excepteur commodo',
|
|
23
|
+
allocationLevel: 'D',
|
|
24
|
+
allocationBand: 1,
|
|
25
|
+
caseSpecialisms: ['et culpa elit labore', 'minim Lorem'],
|
|
26
|
+
caseSubmittedDate: now,
|
|
27
|
+
caseCreatedDate: now,
|
|
28
|
+
caseUpdatedDate: now,
|
|
29
|
+
caseValidDate: now,
|
|
30
|
+
caseValidationDate: null,
|
|
31
|
+
caseValidationOutcome: 'invalid',
|
|
32
|
+
caseValidationInvalidDetails: null,
|
|
33
|
+
caseValidationIncompleteDetails: [
|
|
34
|
+
'occaecat dolor labore ut ipsum',
|
|
35
|
+
'eiusmod ullamco pariatur aliqua esse',
|
|
36
|
+
'eiusmod Ut officia',
|
|
37
|
+
'aute',
|
|
38
|
+
'do minim sint labore aliquip'
|
|
39
|
+
],
|
|
40
|
+
caseExtensionDate: null,
|
|
41
|
+
caseStartedDate: null,
|
|
42
|
+
casePublishedDate: null,
|
|
43
|
+
linkedCaseStatus: null,
|
|
44
|
+
leadCaseReference: null,
|
|
45
|
+
lpaQuestionnaireDueDate: null,
|
|
46
|
+
lpaQuestionnaireSubmittedDate: null,
|
|
47
|
+
lpaQuestionnaireCreatedDate: now,
|
|
48
|
+
lpaQuestionnairePublishedDate: now,
|
|
49
|
+
lpaQuestionnaireValidationOutcome: 'incomplete',
|
|
50
|
+
lpaQuestionnaireValidationOutcomeDate: now,
|
|
51
|
+
lpaQuestionnaireValidationDetails: ['dolore cillum in anim adipisicing'],
|
|
52
|
+
lpaStatement: 'Ut ipsum et',
|
|
53
|
+
caseWithdrawnDate: null,
|
|
54
|
+
caseTransferredDate: null,
|
|
55
|
+
transferredCaseClosedDate: null,
|
|
56
|
+
caseDecisionOutcomeDate: null,
|
|
57
|
+
caseDecisionPublishedDate: null,
|
|
58
|
+
caseDecisionOutcome: null,
|
|
59
|
+
caseCompletedDate: null,
|
|
60
|
+
enforcementNotice: null,
|
|
61
|
+
applicationReference: 'eu',
|
|
62
|
+
applicationDate: now,
|
|
63
|
+
applicationDecision: 'refused',
|
|
64
|
+
applicationDecisionDate: now,
|
|
65
|
+
applicationDecisionDueDate: null,
|
|
66
|
+
caseSubmissionDueDate: now,
|
|
67
|
+
siteAddressLine1: 'ullamco',
|
|
68
|
+
siteAddressLine2: null,
|
|
69
|
+
siteAddressTown: 'dolor reprehenderit ut',
|
|
70
|
+
siteAddressCounty: 'dolore eu exercitation enim',
|
|
71
|
+
siteAddressPostcode: 'est',
|
|
72
|
+
siteAccessDetails: [
|
|
73
|
+
'Ut',
|
|
74
|
+
'eiusmod magna aliqua ipsum',
|
|
75
|
+
'sit non ut amet aliqua',
|
|
76
|
+
'id consectetur cupidatat',
|
|
77
|
+
'incididunt ut Lorem eu'
|
|
78
|
+
],
|
|
79
|
+
siteSafetyDetails: null,
|
|
80
|
+
siteAreaSquareMetres: 123,
|
|
81
|
+
floorSpaceSquareMetres: null,
|
|
82
|
+
isCorrectAppealType: null,
|
|
83
|
+
isGreenBelt: true,
|
|
84
|
+
inConservationArea: null,
|
|
85
|
+
affectsScheduledMonument: true,
|
|
86
|
+
hasProtectedSpecies: true,
|
|
87
|
+
isAonbNationalLandscape: null,
|
|
88
|
+
designatedSitesNames: null,
|
|
89
|
+
hasInfrastructureLevy: false,
|
|
90
|
+
isInfrastructureLevyFormallyAdopted: false,
|
|
91
|
+
infrastructureLevyAdoptedDate: null,
|
|
92
|
+
infrastructureLevyExpectedDate: null,
|
|
93
|
+
lpaProcedurePreference: null,
|
|
94
|
+
lpaProcedurePreferenceDetails: null,
|
|
95
|
+
lpaProcedurePreferenceDuration: 1,
|
|
96
|
+
caseworkReason: 'elit reprehenderit',
|
|
97
|
+
importantInformation: null,
|
|
98
|
+
jurisdiction: null,
|
|
99
|
+
redeterminedIndicator: null,
|
|
100
|
+
dateCostsReportDespatched: null,
|
|
101
|
+
dateNotRecoveredOrDerecovered: null,
|
|
102
|
+
dateRecovered: null,
|
|
103
|
+
originalCaseDecisionDate: null,
|
|
104
|
+
targetDate: null,
|
|
105
|
+
ownsAllLand: true,
|
|
106
|
+
ownsSomeLand: null,
|
|
107
|
+
knowsOtherOwners: 'Some',
|
|
108
|
+
knowsAllOwners: null,
|
|
109
|
+
advertisedAppeal: null,
|
|
110
|
+
notificationMethod: null,
|
|
111
|
+
ownersInformed: null,
|
|
112
|
+
originalDevelopmentDescription: null,
|
|
113
|
+
changedDevelopmentDescription: null,
|
|
114
|
+
newConditionDetails: 'tempor anim adipisicing',
|
|
115
|
+
nearbyCaseReferences: null,
|
|
116
|
+
neighbouringSiteAddresses: [
|
|
117
|
+
{
|
|
118
|
+
neighbouringSiteAddressLine1: 'eu veniam',
|
|
119
|
+
neighbouringSiteAddressLine2: null,
|
|
120
|
+
neighbouringSiteAddressTown: 'pariatur dolore',
|
|
121
|
+
neighbouringSiteAddressCounty: 'fugiat minim anim aliqua aliquip',
|
|
122
|
+
neighbouringSiteAddressPostcode: 'ex anim',
|
|
123
|
+
neighbouringSiteAccessDetails: null,
|
|
124
|
+
neighbouringSiteSafetyDetails: 'veniam in sunt'
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
reasonForNeighbourVisits: null,
|
|
128
|
+
affectedListedBuildingNumbers: ['Excepteur', 'in consequat sit', 'esse sunt velit culpa', 'voluptate esse'],
|
|
129
|
+
appellantCostsAppliedFor: null,
|
|
130
|
+
lpaCostsAppliedFor: null,
|
|
131
|
+
typeOfPlanningApplication: 'householder-planning',
|
|
132
|
+
siteGridReferenceEasting: '357144',
|
|
133
|
+
siteGridReferenceNorthing: '400534',
|
|
134
|
+
hasLandownersPermission: null,
|
|
135
|
+
isSiteInAreaOfSpecialControlAdverts: false,
|
|
136
|
+
wasApplicationRefusedDueToHighwayOrTraffic: null,
|
|
137
|
+
didAppellantSubmitCompletePhotosAndPlans: null,
|
|
138
|
+
advertDetails: [
|
|
139
|
+
{
|
|
140
|
+
isAdvertInPosition: false,
|
|
141
|
+
isSiteOnHighwayLand: false,
|
|
142
|
+
advertType: 'Other'
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
padsSapId: null
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const BASE_S78_CASE = {
|
|
149
|
+
caseId: 96989394,
|
|
150
|
+
caseReference: 'exercitation dolore',
|
|
151
|
+
submissionId: null,
|
|
152
|
+
caseStatus: 'transferred',
|
|
153
|
+
caseType: 'D',
|
|
154
|
+
caseProcedure: 'inquiry',
|
|
155
|
+
lpaCode: 'pariatur cillum non',
|
|
156
|
+
caseOfficerId: null,
|
|
157
|
+
inspectorId: 'amet magna dolor in labore',
|
|
158
|
+
allocationLevel: null,
|
|
159
|
+
allocationBand: null,
|
|
160
|
+
caseSpecialisms: ['laboris est proident minim', 'qui sunt ut', 'consectetur Lorem do sunt aliquip'],
|
|
161
|
+
caseSubmittedDate: now,
|
|
162
|
+
caseCreatedDate: now,
|
|
163
|
+
caseUpdatedDate: now,
|
|
164
|
+
caseValidDate: null,
|
|
165
|
+
caseValidationDate: null,
|
|
166
|
+
caseValidationOutcome: null,
|
|
167
|
+
caseValidationInvalidDetails: null,
|
|
168
|
+
caseValidationIncompleteDetails: null,
|
|
169
|
+
caseExtensionDate: null,
|
|
170
|
+
caseStartedDate: now,
|
|
171
|
+
casePublishedDate: null,
|
|
172
|
+
linkedCaseStatus: 'lead',
|
|
173
|
+
leadCaseReference: null,
|
|
174
|
+
lpaQuestionnaireDueDate: null,
|
|
175
|
+
lpaQuestionnaireSubmittedDate: null,
|
|
176
|
+
lpaQuestionnaireCreatedDate: null,
|
|
177
|
+
lpaQuestionnairePublishedDate: null,
|
|
178
|
+
lpaQuestionnaireValidationOutcome: null,
|
|
179
|
+
lpaQuestionnaireValidationOutcomeDate: null,
|
|
180
|
+
lpaQuestionnaireValidationDetails: ['cupidatat dolore in', 'officia in nostrud non', 'eu amet velit labore aute'],
|
|
181
|
+
lpaStatement: 'ipsum consectetur labore pariatur',
|
|
182
|
+
listOfDocumentsBeforeDecision: null,
|
|
183
|
+
caseWithdrawnDate: null,
|
|
184
|
+
caseTransferredDate: null,
|
|
185
|
+
transferredCaseClosedDate: null,
|
|
186
|
+
caseDecisionOutcomeDate: null,
|
|
187
|
+
caseDecisionPublishedDate: null,
|
|
188
|
+
caseDecisionOutcome: null,
|
|
189
|
+
caseCompletedDate: null,
|
|
190
|
+
enforcementNotice: null,
|
|
191
|
+
applicationReference: 'consequat ullamco in aute amet',
|
|
192
|
+
applicationDate: now,
|
|
193
|
+
applicationDecision: 'not_received',
|
|
194
|
+
applicationDecisionDate: null,
|
|
195
|
+
applicationDecisionDueDate: now,
|
|
196
|
+
caseSubmissionDueDate: null,
|
|
197
|
+
siteAddressLine1: 'dolore mollit',
|
|
198
|
+
siteAddressLine2: null,
|
|
199
|
+
siteAddressTown: 'in',
|
|
200
|
+
siteAddressCounty: 'ut incididunt velit in',
|
|
201
|
+
siteAddressPostcode: 'ut',
|
|
202
|
+
siteAccessDetails: ['ullamco', 'elit in', 'velit officia pariatur mollit', 'nostrud ex culpa'],
|
|
203
|
+
siteSafetyDetails: null,
|
|
204
|
+
siteAreaSquareMetres: 69381079.34849802,
|
|
205
|
+
floorSpaceSquareMetres: null,
|
|
206
|
+
isCorrectAppealType: null,
|
|
207
|
+
isGreenBelt: false,
|
|
208
|
+
inConservationArea: null,
|
|
209
|
+
ownsAllLand: null,
|
|
210
|
+
ownsSomeLand: null,
|
|
211
|
+
knowsOtherOwners: null,
|
|
212
|
+
knowsAllOwners: null,
|
|
213
|
+
advertisedAppeal: true,
|
|
214
|
+
notificationMethod: ['notice'],
|
|
215
|
+
ownersInformed: null,
|
|
216
|
+
originalDevelopmentDescription: 'est',
|
|
217
|
+
changedDevelopmentDescription: null,
|
|
218
|
+
newConditionDetails: null,
|
|
219
|
+
nearbyCaseReferences: ['veniam voluptate incididunt Excepteur'],
|
|
220
|
+
neighbouringSiteAddresses: [
|
|
221
|
+
{
|
|
222
|
+
neighbouringSiteAddressLine1: 'laborum',
|
|
223
|
+
neighbouringSiteAddressLine2: null,
|
|
224
|
+
neighbouringSiteAddressTown: 'aute ipsum dolore',
|
|
225
|
+
neighbouringSiteAddressCounty: null,
|
|
226
|
+
neighbouringSiteAddressPostcode: 'dolor ipsum anim fugiat',
|
|
227
|
+
neighbouringSiteAccessDetails: 'reprehenderit mollit Duis exercitation',
|
|
228
|
+
neighbouringSiteSafetyDetails: null
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
reasonForNeighbourVisits: null,
|
|
232
|
+
affectedListedBuildingNumbers: ['tempor adipisicing in'],
|
|
233
|
+
changedListedBuildingNumbers: null,
|
|
234
|
+
preserveGrantLoan: true,
|
|
235
|
+
consultHistoricEngland: false,
|
|
236
|
+
appellantCostsAppliedFor: null,
|
|
237
|
+
lpaCostsAppliedFor: null,
|
|
238
|
+
agriculturalHolding: null,
|
|
239
|
+
tenantAgriculturalHolding: null,
|
|
240
|
+
otherTenantsAgriculturalHolding: null,
|
|
241
|
+
informedTenantsAgriculturalHolding: true,
|
|
242
|
+
appellantProcedurePreference: 'hearing',
|
|
243
|
+
appellantProcedurePreferenceDetails: null,
|
|
244
|
+
appellantProcedurePreferenceDuration: null,
|
|
245
|
+
appellantProcedurePreferenceWitnessCount: null,
|
|
246
|
+
statusPlanningObligation: 'cillum proident fugiat ex quis',
|
|
247
|
+
affectsScheduledMonument: true,
|
|
248
|
+
hasProtectedSpecies: true,
|
|
249
|
+
isAonbNationalLandscape: null,
|
|
250
|
+
designatedSitesNames: null,
|
|
251
|
+
isGypsyOrTravellerSite: false,
|
|
252
|
+
isPublicRightOfWay: true,
|
|
253
|
+
eiaEnvironmentalImpactSchedule: 'schedule-1',
|
|
254
|
+
eiaDevelopmentDescription: 'agriculture-aquaculture',
|
|
255
|
+
eiaSensitiveAreaDetails: 'dolore non anim ut deserunt',
|
|
256
|
+
eiaColumnTwoThreshold: null,
|
|
257
|
+
eiaScreeningOpinion: true,
|
|
258
|
+
eiaRequiresEnvironmentalStatement: null,
|
|
259
|
+
eiaCompletedEnvironmentalStatement: null,
|
|
260
|
+
hasStatutoryConsultees: true,
|
|
261
|
+
consultedBodiesDetails: null,
|
|
262
|
+
hasInfrastructureLevy: null,
|
|
263
|
+
isInfrastructureLevyFormallyAdopted: null,
|
|
264
|
+
infrastructureLevyAdoptedDate: null,
|
|
265
|
+
infrastructureLevyExpectedDate: null,
|
|
266
|
+
lpaProcedurePreference: null,
|
|
267
|
+
lpaProcedurePreferenceDetails: null,
|
|
268
|
+
lpaProcedurePreferenceDuration: 12,
|
|
269
|
+
caseworkReason: 'sit ipsum',
|
|
270
|
+
importantInformation: null,
|
|
271
|
+
jurisdiction: null,
|
|
272
|
+
redeterminedIndicator: null,
|
|
273
|
+
dateCostsReportDespatched: null,
|
|
274
|
+
dateNotRecoveredOrDerecovered: null,
|
|
275
|
+
dateRecovered: null,
|
|
276
|
+
originalCaseDecisionDate: null,
|
|
277
|
+
targetDate: null,
|
|
278
|
+
appellantCommentsSubmittedDate: null,
|
|
279
|
+
appellantStatementSubmittedDate: null,
|
|
280
|
+
appellantProofsSubmittedDate: null,
|
|
281
|
+
finalCommentsDueDate: null,
|
|
282
|
+
interestedPartyRepsDueDate: null,
|
|
283
|
+
lpaCommentsSubmittedDate: null,
|
|
284
|
+
lpaProofsSubmittedDate: null,
|
|
285
|
+
lpaStatementSubmittedDate: null,
|
|
286
|
+
proofsOfEvidenceDueDate: null,
|
|
287
|
+
siteNoticesSentDate: null,
|
|
288
|
+
statementDueDate: null,
|
|
289
|
+
caseManagementConferenceDate: null,
|
|
290
|
+
numberOfResidencesNetChange: 59800248.893144846,
|
|
291
|
+
siteGridReferenceEasting: '357144',
|
|
292
|
+
siteGridReferenceNorthing: '400534',
|
|
293
|
+
siteViewableFromRoad: null,
|
|
294
|
+
siteWithinSSSI: null,
|
|
295
|
+
typeOfPlanningApplication: 'full-appeal',
|
|
296
|
+
developmentType: 'other-minor',
|
|
297
|
+
statementOfCommonGroundDueDate: null,
|
|
298
|
+
planningObligationDueDate: null,
|
|
299
|
+
advertDetails: [
|
|
300
|
+
{
|
|
301
|
+
isAdvertInPosition: false,
|
|
302
|
+
isSiteOnHighwayLand: false,
|
|
303
|
+
advertType: 'Other'
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
padsSapId: null,
|
|
307
|
+
applicationMadeUnderActSection: 'existing-development',
|
|
308
|
+
siteUseAtTimeOfApplication: 'Site use at time of application',
|
|
309
|
+
appealUnderActSection: 'proposed-use-of-a-development',
|
|
310
|
+
lpaConsiderAppealInvalid: true,
|
|
311
|
+
lpaAppealInvalidReasons: 'Reason(s) why LPA consider the appeal invalid'
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
function randomCaseReference(prefix, index) {
|
|
315
|
+
const suffix = String(index + 1).padStart(7, '0');
|
|
316
|
+
return `${prefix}${suffix}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function randomHex(length) {
|
|
320
|
+
return crypto
|
|
321
|
+
.randomBytes(Math.ceil(length / 2))
|
|
322
|
+
.toString('hex')
|
|
323
|
+
.slice(0, length);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function createCase(caseType, index) {
|
|
327
|
+
const template = caseType === 'HAS' ? BASE_HAS_CASE : BASE_S78_CASE;
|
|
328
|
+
const entity = structuredClone(template);
|
|
329
|
+
const referencePrefix = caseType === 'HAS' ? 'HAS' : 'S78';
|
|
330
|
+
|
|
331
|
+
entity.caseId = 500000 + index;
|
|
332
|
+
entity.caseReference = randomCaseReference(referencePrefix, index);
|
|
333
|
+
entity.caseCreatedDate = now;
|
|
334
|
+
entity.caseUpdatedDate = now;
|
|
335
|
+
entity.caseSubmittedDate = now;
|
|
336
|
+
entity.applicationReference = `LOAD-${referencePrefix}-${index + 1}`;
|
|
337
|
+
entity.lpaCode = 'LOAD';
|
|
338
|
+
|
|
339
|
+
return entity;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function createServiceUser(casePayload, serviceUserType, index) {
|
|
343
|
+
const typeName = serviceUserType === 'Agent' ? 'Agent' : 'Appellant';
|
|
344
|
+
return {
|
|
345
|
+
id: crypto.randomUUID(),
|
|
346
|
+
salutation: 'Ms',
|
|
347
|
+
firstName: `Load-Test`,
|
|
348
|
+
lastName: `${typeName}${index + 1}`,
|
|
349
|
+
addressLine1: '1 Test Street',
|
|
350
|
+
addressLine2: null,
|
|
351
|
+
addressTown: 'Testville',
|
|
352
|
+
addressCounty: 'Testshire',
|
|
353
|
+
postcode: 'TE1 1ST',
|
|
354
|
+
addressCountry: 'United Kingdom',
|
|
355
|
+
organisation: 'Load Test Org',
|
|
356
|
+
organisationType: 'Private Company',
|
|
357
|
+
role: typeName,
|
|
358
|
+
telephoneNumber: '+447123456789',
|
|
359
|
+
otherPhoneNumber: null,
|
|
360
|
+
faxNumber: null,
|
|
361
|
+
emailAddress: `${typeName.toLowerCase()}${index + 1}@example.com`,
|
|
362
|
+
webAddress: 'https://example.com',
|
|
363
|
+
serviceUserType: typeName,
|
|
364
|
+
caseReference: casePayload.caseReference,
|
|
365
|
+
sourceSystem: 'back-office-appeals',
|
|
366
|
+
sourceSuid: crypto.randomUUID()
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function createDocument(casePayload, caseIndex, documentIndex) {
|
|
371
|
+
const documentId = crypto.randomUUID();
|
|
372
|
+
const filename = `LOAD-${casePayload.caseReference}-doc-${documentIndex + 1}.pdf`;
|
|
373
|
+
const caseType = typeof casePayload.caseType === 'string' ? casePayload.caseType : null;
|
|
374
|
+
const documentUri = `https://example.local/appeal/${casePayload.caseReference}/${documentId}/v1/${filename}`;
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
documentId,
|
|
378
|
+
caseId: casePayload.caseId,
|
|
379
|
+
caseReference: casePayload.caseReference,
|
|
380
|
+
version: 1,
|
|
381
|
+
filename,
|
|
382
|
+
originalFilename: filename,
|
|
383
|
+
size: 1000 + documentIndex,
|
|
384
|
+
mime: 'application/pdf',
|
|
385
|
+
documentURI: documentUri,
|
|
386
|
+
publishedDocumentURI: documentUri,
|
|
387
|
+
virusCheckStatus: 'scanned',
|
|
388
|
+
fileMD5: randomHex(32),
|
|
389
|
+
dateCreated: now,
|
|
390
|
+
dateReceived: now,
|
|
391
|
+
datePublished: now,
|
|
392
|
+
lastModified: now,
|
|
393
|
+
caseType,
|
|
394
|
+
redactedStatus: 'no_redaction_required',
|
|
395
|
+
documentType: 'plansDrawings',
|
|
396
|
+
sourceSystem: 'back-office-appeals',
|
|
397
|
+
origin: 'pins',
|
|
398
|
+
owner: 'Load Test Team',
|
|
399
|
+
author: 'Load Test Team',
|
|
400
|
+
description: `Load test document ${documentIndex + 1}`,
|
|
401
|
+
caseStage: 'appellant-case',
|
|
402
|
+
horizonFolderId: null
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function buildAjvValidator() {
|
|
407
|
+
const schemas = loadAllSchemasSync();
|
|
408
|
+
const flatSchemas = Object.values(schemas).reduce((acc, current) => ({ ...acc, ...current }), {});
|
|
409
|
+
const ajv = new Ajv({ schemas: flatSchemas, allErrors: true });
|
|
410
|
+
addFormats(ajv);
|
|
411
|
+
return ajv;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function validateOrThrow(ajv, schemaName, payload, label) {
|
|
415
|
+
const valid = ajv.validate(schemaName, payload);
|
|
416
|
+
if (!valid) {
|
|
417
|
+
const errors = ajv.errorsText(ajv.errors, { separator: '\n' });
|
|
418
|
+
throw new Error(`${label} failed schema ${schemaName}:\n${errors}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @param {'HAS'|'S78'} [caseType]
|
|
424
|
+
* @param {number} [documentsPerCase]
|
|
425
|
+
* @param {number} [caseIndex]
|
|
426
|
+
* @returns {{case: any, serviceUsers: any[], documents: any[]}}
|
|
427
|
+
*/
|
|
428
|
+
export const generateAppealsLoadTestMessages = (caseType = 'HAS', documentsPerCase = 2, caseIndex = 1) => {
|
|
429
|
+
const caseSchema = caseType === 'HAS' ? HAS_SCHEMA : S78_SCHEMA;
|
|
430
|
+
const ajv = buildAjvValidator();
|
|
431
|
+
|
|
432
|
+
const casePayload = createCase(caseType, caseIndex);
|
|
433
|
+
validateOrThrow(ajv, caseSchema, casePayload, `case[${caseIndex}]`);
|
|
434
|
+
|
|
435
|
+
const serviceUsers = [
|
|
436
|
+
createServiceUser(casePayload, 'Agent', caseIndex),
|
|
437
|
+
createServiceUser(casePayload, 'Appellant', caseIndex)
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
for (const serviceUser of serviceUsers) {
|
|
441
|
+
validateOrThrow(ajv, SERVICE_USER_SCHEMA, serviceUser, `${caseIndex} serviceUser[${serviceUser.role}]`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const documents = [];
|
|
445
|
+
for (let docIndex = 0; docIndex < documentsPerCase; docIndex += 1) {
|
|
446
|
+
const document = createDocument(casePayload, caseIndex, docIndex);
|
|
447
|
+
validateOrThrow(ajv, DOCUMENT_SCHEMA, document, `document[${caseIndex}][${docIndex}]`);
|
|
448
|
+
documents.push(document);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
case: casePayload,
|
|
453
|
+
serviceUsers,
|
|
454
|
+
documents
|
|
455
|
+
};
|
|
456
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { generateAppealsLoadTestMessages } from './generate-appeals-messages.js';
|
|
2
|
+
import { ServiceBusEventClient } from '../service-bus-client.js';
|
|
3
|
+
import { MESSAGE_EVENT_TYPE } from '../../src/enums.js';
|
|
4
|
+
|
|
5
|
+
/** @type {'HAS'|'S78'} */ //@ts-ignore
|
|
6
|
+
const caseType = process.env.APPEALS_LOAD_TEST_CASE_TYPE || 'HAS'; // 'HAS' or 'S78'
|
|
7
|
+
const caseCount = parseInt(process.env.APPEALS_LOAD_TEST_CASE_COUNT || '1', 10);
|
|
8
|
+
const documentsPerCase = parseInt(process.env.APPEALS_LOAD_TEST_DOCUMENTS_PER_CASE || '2', 10);
|
|
9
|
+
const batchSize = parseInt(process.env.APPEALS_LOAD_TEST_BATCH_SIZE || '50', 10);
|
|
10
|
+
|
|
11
|
+
if (batchSize < 1) {
|
|
12
|
+
throw new Error('Batch size must be at least 1');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const caseTypeTopicMap = {
|
|
16
|
+
HAS: 'appeal-has',
|
|
17
|
+
S78: 'appeal-s78'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if (!caseTypeTopicMap[caseType]) {
|
|
21
|
+
throw new Error(`Unsupported case type: ${caseType}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const client = new ServiceBusEventClient();
|
|
25
|
+
|
|
26
|
+
let casesCreated = 0;
|
|
27
|
+
let documentsCreated = 0;
|
|
28
|
+
let serviceUsersCreated = 0;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
for (let batchIndex = 0; batchIndex < caseCount; batchIndex += batchSize) {
|
|
32
|
+
const batch = [];
|
|
33
|
+
const batchEnd = Math.min(batchIndex + batchSize, caseCount);
|
|
34
|
+
|
|
35
|
+
for (let index = batchIndex; index < batchEnd; index += 1) {
|
|
36
|
+
batch.push(generateAppealsLoadTestMessages(caseType, documentsPerCase, index));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const batchCases = batch.map((m) => m.case);
|
|
40
|
+
const batchServiceUsers = batch.flatMap((m) => m.serviceUsers);
|
|
41
|
+
const batchDocuments = batch.flatMap((m) => m.documents);
|
|
42
|
+
|
|
43
|
+
await Promise.all([
|
|
44
|
+
client.sendEvents(caseTypeTopicMap[caseType], batchCases, MESSAGE_EVENT_TYPE.CREATE),
|
|
45
|
+
client.sendEvents('appeal-service-user', batchServiceUsers, MESSAGE_EVENT_TYPE.CREATE),
|
|
46
|
+
client.sendEvents('appeal-document', batchDocuments, MESSAGE_EVENT_TYPE.CREATE)
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
casesCreated += batch.length;
|
|
50
|
+
documentsCreated += batchDocuments.length;
|
|
51
|
+
serviceUsersCreated += batchServiceUsers.length;
|
|
52
|
+
}
|
|
53
|
+
} finally {
|
|
54
|
+
await client.close();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log('Finished');
|
|
58
|
+
console.log({ casesCreated, documentsCreated, serviceUsersCreated });
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { ServiceBusClient } from '@azure/service-bus';
|
|
2
|
+
import { DefaultAzureCredential } from '@azure/identity';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {object} ServiceBusEventClientOptions
|
|
6
|
+
* @property {string | undefined} [connectionString]
|
|
7
|
+
* @property {import('@azure/core-auth').TokenCredential | undefined} [credential]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Event client used to broadcast messages to service bus topics.
|
|
12
|
+
* @class
|
|
13
|
+
*/
|
|
14
|
+
export class ServiceBusEventClient {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.client = new ServiceBusClient(process.env.LOAD_TEST_SERVICE_BUS_NAMESPACE, new DefaultAzureCredential());
|
|
17
|
+
this.simulateSending = process.env.LOAD_TEST_SIMULATE_SENDING === 'true'; // true logs to console, false sends to service bus
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} topic
|
|
22
|
+
* @returns Instance of the service bus sender from the service bus client
|
|
23
|
+
*/
|
|
24
|
+
#createSender = (topic) => {
|
|
25
|
+
return this.client.createSender(topic);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns unix timestamp representing the trace id
|
|
30
|
+
* @returns {number}
|
|
31
|
+
*/
|
|
32
|
+
#createTraceId = () => {
|
|
33
|
+
return Date.now();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {object[]} events
|
|
38
|
+
* @param {number} traceId
|
|
39
|
+
* @param {import('../utils/event-type').EventType} type
|
|
40
|
+
* @returns {import('@azure/service-bus').ServiceBusMessage []}
|
|
41
|
+
*/
|
|
42
|
+
#transformMessagesToSend = (events, traceId, type) => {
|
|
43
|
+
return events.map((body) => ({
|
|
44
|
+
body,
|
|
45
|
+
contentType: 'application/json',
|
|
46
|
+
applicationProperties: {
|
|
47
|
+
version: '0.1',
|
|
48
|
+
traceId,
|
|
49
|
+
type
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} topic
|
|
56
|
+
* @param {any[]} events
|
|
57
|
+
* @param {import('../utils/event-type').EventType} eventType
|
|
58
|
+
*/
|
|
59
|
+
sendEvents = async (topic, events, eventType) => {
|
|
60
|
+
const sender = this.#createSender(topic);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const traceId = this.#createTraceId();
|
|
64
|
+
|
|
65
|
+
console.log(`Publishing ${events.length} events to topic ${topic} with trace id ${traceId}`);
|
|
66
|
+
|
|
67
|
+
const messages = this.#transformMessagesToSend(events, traceId, eventType);
|
|
68
|
+
|
|
69
|
+
if (this.simulateSending) {
|
|
70
|
+
console.log(`Simulating sending messages: ${JSON.stringify(messages)}`);
|
|
71
|
+
} else {
|
|
72
|
+
await sender.sendMessages(messages);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return events;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(`Error sending messages to topic ${topic}:`, error);
|
|
78
|
+
throw error;
|
|
79
|
+
} finally {
|
|
80
|
+
if (!sender.isClosed) {
|
|
81
|
+
await sender.close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
close = async () => {
|
|
87
|
+
await this.client.close();
|
|
88
|
+
};
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planning-inspectorate/data-model",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.42.0",
|
|
4
4
|
"description": "JSON Schemas for the Planning Inspectorate's Data Model",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"gen-types": "node src/lib/gen-types.js",
|
|
17
17
|
"gen-cjs": "npx rollup ./src/index.js --file ./src/index.cjs --format cjs && npx rollup ./src/enums.js --file ./src/enums.cjs --format cjs",
|
|
18
18
|
"lint": "npx eslint .",
|
|
19
|
+
"load-test-appeals": "node --env-file=.env load-test/appeals/run-load-script.js",
|
|
19
20
|
"format": "prettier --write .",
|
|
20
21
|
"format-check": "prettier --check .",
|
|
21
22
|
"prepare": "node .husky/install.mjs"
|
|
@@ -30,6 +31,8 @@
|
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@adobe/jsonschema2md": "^8.0.3",
|
|
34
|
+
"@azure/identity": "^4.13.0",
|
|
35
|
+
"@azure/service-bus": "^7.9.1",
|
|
33
36
|
"@commitlint/cli": "^21.0.0",
|
|
34
37
|
"@commitlint/config-conventional": "^21.0.0",
|
|
35
38
|
"@eslint/js": "^10.0.1",
|
|
@@ -18,7 +18,6 @@ class InvoiceStage(Enum):
|
|
|
18
18
|
pre_examination = "pre_examination"
|
|
19
19
|
initial_examination = "initial_examination"
|
|
20
20
|
final_examination = "final_examination"
|
|
21
|
-
NoneType_None = None
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class NsipInvoice(BaseModel):
|
|
@@ -33,7 +32,7 @@ class NsipInvoice(BaseModel):
|
|
|
33
32
|
"""
|
|
34
33
|
The unique identifier within the Back Office. This is not the same as the case reference
|
|
35
34
|
"""
|
|
36
|
-
caseReference: str | None = Field(
|
|
35
|
+
caseReference: str | None = Field(None, examples=["BC0510001"])
|
|
37
36
|
invoiceStage: InvoiceStage = Field(..., examples=["pre_acceptance"])
|
|
38
37
|
"""
|
|
39
38
|
Invoice stage
|
|
@@ -42,38 +41,38 @@ class NsipInvoice(BaseModel):
|
|
|
42
41
|
"""
|
|
43
42
|
Invoice number
|
|
44
43
|
"""
|
|
45
|
-
amountDue: float = Field(
|
|
44
|
+
amountDue: float | None = Field(None, examples=[500])
|
|
46
45
|
"""
|
|
47
46
|
Amount due
|
|
48
47
|
"""
|
|
49
48
|
paymentDueDate: AwareDatetime | None = Field(
|
|
50
|
-
|
|
49
|
+
None, examples=["2023-07-27T20:30:00.000Z"]
|
|
51
50
|
)
|
|
52
51
|
"""
|
|
53
52
|
Payment due date
|
|
54
53
|
"""
|
|
55
54
|
invoicedDate: AwareDatetime | None = Field(
|
|
56
|
-
|
|
55
|
+
None, examples=["2023-07-27T20:30:00.000Z"]
|
|
57
56
|
)
|
|
58
57
|
"""
|
|
59
58
|
Invoiced date
|
|
60
59
|
"""
|
|
61
60
|
paymentDate: AwareDatetime | None = Field(
|
|
62
|
-
|
|
61
|
+
None, examples=["2023-07-27T20:30:00.000Z"]
|
|
63
62
|
)
|
|
64
63
|
"""
|
|
65
64
|
Date of payment
|
|
66
65
|
"""
|
|
67
|
-
refundCreditNoteNumber: str | None = Field(
|
|
66
|
+
refundCreditNoteNumber: str | None = Field(None, examples=["1800004419"])
|
|
68
67
|
"""
|
|
69
68
|
Refund credit note number
|
|
70
69
|
"""
|
|
71
|
-
refundAmount: float | None = Field(
|
|
70
|
+
refundAmount: float | None = Field(None, examples=[200])
|
|
72
71
|
"""
|
|
73
72
|
Amount refunded
|
|
74
73
|
"""
|
|
75
74
|
refundIssueDate: AwareDatetime | None = Field(
|
|
76
|
-
|
|
75
|
+
None, examples=["2023-07-27T20:30:00.000Z"]
|
|
77
76
|
)
|
|
78
77
|
"""
|
|
79
78
|
Refund issue date
|
|
@@ -5,19 +5,7 @@
|
|
|
5
5
|
"description": "NSIP Standalone Invoice Schema for ODW Curated layer",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": true,
|
|
8
|
-
"required": [
|
|
9
|
-
"caseId",
|
|
10
|
-
"caseReference",
|
|
11
|
-
"invoiceStage",
|
|
12
|
-
"invoiceNumber",
|
|
13
|
-
"amountDue",
|
|
14
|
-
"paymentDueDate",
|
|
15
|
-
"invoicedDate",
|
|
16
|
-
"paymentDate",
|
|
17
|
-
"refundCreditNoteNumber",
|
|
18
|
-
"refundAmount",
|
|
19
|
-
"refundIssueDate"
|
|
20
|
-
],
|
|
8
|
+
"required": ["caseId", "invoiceStage", "invoiceNumber"],
|
|
21
9
|
"properties": {
|
|
22
10
|
"caseId": {
|
|
23
11
|
"type": "integer",
|
|
@@ -29,9 +17,9 @@
|
|
|
29
17
|
"examples": ["BC0510001"]
|
|
30
18
|
},
|
|
31
19
|
"invoiceStage": {
|
|
32
|
-
"type":
|
|
20
|
+
"type": "string",
|
|
33
21
|
"description": "Invoice stage",
|
|
34
|
-
"enum": ["pre_acceptance", "acceptance", "pre_examination", "initial_examination", "final_examination"
|
|
22
|
+
"enum": ["pre_acceptance", "acceptance", "pre_examination", "initial_examination", "final_examination"],
|
|
35
23
|
"examples": ["pre_acceptance"]
|
|
36
24
|
},
|
|
37
25
|
"invoiceNumber": {
|
|
@@ -40,7 +28,7 @@
|
|
|
40
28
|
"examples": ["1800004419"]
|
|
41
29
|
},
|
|
42
30
|
"amountDue": {
|
|
43
|
-
"type": "number",
|
|
31
|
+
"type": ["number", "null"],
|
|
44
32
|
"description": "Amount due",
|
|
45
33
|
"examples": [500]
|
|
46
34
|
},
|
package/src/schemas.d.ts
CHANGED
|
@@ -2411,17 +2411,11 @@ export interface NSIPInvoice {
|
|
|
2411
2411
|
* The unique identifier within the Back Office. This is not the same as the case reference
|
|
2412
2412
|
*/
|
|
2413
2413
|
caseId: number;
|
|
2414
|
-
caseReference
|
|
2414
|
+
caseReference?: string | null;
|
|
2415
2415
|
/**
|
|
2416
2416
|
* Invoice stage
|
|
2417
2417
|
*/
|
|
2418
|
-
invoiceStage:
|
|
2419
|
-
| 'pre_acceptance'
|
|
2420
|
-
| 'acceptance'
|
|
2421
|
-
| 'pre_examination'
|
|
2422
|
-
| 'initial_examination'
|
|
2423
|
-
| 'final_examination'
|
|
2424
|
-
| null;
|
|
2418
|
+
invoiceStage: 'pre_acceptance' | 'acceptance' | 'pre_examination' | 'initial_examination' | 'final_examination';
|
|
2425
2419
|
/**
|
|
2426
2420
|
* Invoice number
|
|
2427
2421
|
*/
|
|
@@ -2429,31 +2423,31 @@ export interface NSIPInvoice {
|
|
|
2429
2423
|
/**
|
|
2430
2424
|
* Amount due
|
|
2431
2425
|
*/
|
|
2432
|
-
amountDue
|
|
2426
|
+
amountDue?: number | null;
|
|
2433
2427
|
/**
|
|
2434
2428
|
* Payment due date
|
|
2435
2429
|
*/
|
|
2436
|
-
paymentDueDate
|
|
2430
|
+
paymentDueDate?: string | null;
|
|
2437
2431
|
/**
|
|
2438
2432
|
* Invoiced date
|
|
2439
2433
|
*/
|
|
2440
|
-
invoicedDate
|
|
2434
|
+
invoicedDate?: string | null;
|
|
2441
2435
|
/**
|
|
2442
2436
|
* Date of payment
|
|
2443
2437
|
*/
|
|
2444
|
-
paymentDate
|
|
2438
|
+
paymentDate?: string | null;
|
|
2445
2439
|
/**
|
|
2446
2440
|
* Refund credit note number
|
|
2447
2441
|
*/
|
|
2448
|
-
refundCreditNoteNumber
|
|
2442
|
+
refundCreditNoteNumber?: string | null;
|
|
2449
2443
|
/**
|
|
2450
2444
|
* Amount refunded
|
|
2451
2445
|
*/
|
|
2452
|
-
refundAmount
|
|
2446
|
+
refundAmount?: number | null;
|
|
2453
2447
|
/**
|
|
2454
2448
|
* Refund issue date
|
|
2455
2449
|
*/
|
|
2456
|
-
refundIssueDate
|
|
2450
|
+
refundIssueDate?: string | null;
|
|
2457
2451
|
[k: string]: unknown;
|
|
2458
2452
|
}
|
|
2459
2453
|
|