From 2a8c48a18445588dcfbbdaa02ab2b52cebc2a658 Mon Sep 17 00:00:00 2001
From: Caitlin Roach
Date: Tue, 16 Jun 2026 16:14:41 +0100
Subject: [PATCH 1/4] Add empty state for no pharmacies
---
app/routes/prototype-admin.js | 14 +++
.../includes/_preset-scenarios-table.html | 8 ++
app/views/pharmacies/index.html | 116 +++++++++---------
3 files changed, 81 insertions(+), 57 deletions(-)
diff --git a/app/routes/prototype-admin.js b/app/routes/prototype-admin.js
index b2401748..2bd6777f 100644
--- a/app/routes/prototype-admin.js
+++ b/app/routes/prototype-admin.js
@@ -564,6 +564,20 @@ module.exports = (router) => {
res.redirect('/home')
})
+ // ----------------------------------------------------------------
+ // Preset: Pharmacy HQ, no data (Amanda White, P15951)
+ // ----------------------------------------------------------------
+
+ router.get('/prototype-setup/preset/pharmacy-hq-no-data', (req, res) => {
+ resetSession(req)
+ const data = req.session.data
+ data.currentUserId = '6424325235325'
+ data.currentOrganisationId = 'P15951'
+ // Remove all pharmacies belonging to P15951 to start with a clean slate
+ data.organisations = data.organisations.filter(org => org.companyId !== 'P15951' || org.type === 'Pharmacy HQ')
+ res.redirect('/home')
+ })
+
// ----------------------------------------------------------------
// Preset: Recorder, single organisation (Ocean Merritt, FR4V56)
// ----------------------------------------------------------------
diff --git a/app/views/includes/_preset-scenarios-table.html b/app/views/includes/_preset-scenarios-table.html
index 3d9bd63b..05a98cf7 100644
--- a/app/views/includes/_preset-scenarios-table.html
+++ b/app/views/includes/_preset-scenarios-table.html
@@ -71,6 +71,14 @@
role: "Group administrator",
scenarioDetails: "Group admin for a large pharmacy chain (same-name pharmacies)."
},
+ {
+ presetLabel: "Group admin, pharmacy HQ (no data)",
+ presetPath: "/prototype-setup/preset/pharmacy-hq-no-data",
+ userName: "Amanda White",
+ userEmail: "amanda.white@nhs.net",
+ role: "Group administrator",
+ scenarioDetails: "Group admin for a pharmacy HQ with no pre-loaded data, pharmacies or users."
+ },
{
presetLabel: "Regional lead",
presetPath: "/prototype-setup/preset/regions",
diff --git a/app/views/pharmacies/index.html b/app/views/pharmacies/index.html
index b033a9bf..ac9ab6ff 100644
--- a/app/views/pharmacies/index.html
+++ b/app/views/pharmacies/index.html
@@ -43,65 +43,67 @@ Pharmacies
href: "/pharmacies/select"
}) }}
-
- Pharmacies added ({{ organisations | length }})
-
- {% if (organisations | length) > 10 %}
-
- Search
-
-
- {% endif %}
-
+ {% endif %}
{% if closedOrganisations.length > 0 %}
From 7d21a4028c3182a081a1d89cce48be0ddf18a1b2 Mon Sep 17 00:00:00 2001
From: Caitlin Roach
Date: Wed, 9 Sep 2026 13:39:53 +0100
Subject: [PATCH 2/4] vaccs records added
---
app/data/vaccinations-recorded.js | 264 +++++++++++++++++++++
app/routes/vaccines.js | 49 +++-
app/views/vaccines/edit-batch.html | 4 +-
app/views/vaccines/product-page.html | 19 ++
app/views/vaccines/update-batch-check.html | 73 ++++++
5 files changed, 405 insertions(+), 4 deletions(-)
create mode 100644 app/views/vaccines/update-batch-check.html
diff --git a/app/data/vaccinations-recorded.js b/app/data/vaccinations-recorded.js
index c5de4ae2..76a4257b 100644
--- a/app/data/vaccinations-recorded.js
+++ b/app/data/vaccinations-recorded.js
@@ -142,6 +142,270 @@ module.exports = [
injectionSite: "Left arm",
notes: "",
editable: true
+ },
+
+ // Additional records below ensure every batch at Jane Smith's organisation
+ // (Central Manchester Trust, RW3) has at least 2 vaccination records
+
+ {
+ id: "700001",
+ date: { day: "24", month: "11", year: "2025" },
+ vaccine: "COVID-19",
+ vaccineProduct: "Comirnaty 3 LP.8.1",
+ patient: {
+ name: "Priya Patel",
+ nhsNumber: "9111222333"
+ },
+ batchNumber: "8535345", // Batch AB2514
+ batchExpiryDate: "2028-03-06",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3NM", // North Manchester General Hospital
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700002",
+ date: { day: "24", month: "11", year: "2025" },
+ vaccine: "flu",
+ vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)",
+ patient: {
+ name: "Michael Osei",
+ nhsNumber: "9111222334"
+ },
+ batchNumber: "634643636", // Batch 914-151
+ batchExpiryDate: "2028-02-19",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3NM", // North Manchester General Hospital
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700003",
+ date: { day: "23", month: "11", year: "2025" },
+ vaccine: "flu",
+ vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)",
+ patient: {
+ name: "Sarah Kowalski",
+ nhsNumber: "9111222335"
+ },
+ batchNumber: "634643636", // Batch 914-151
+ batchExpiryDate: "2028-02-19",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3NM", // North Manchester General Hospital
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700004",
+ date: { day: "22", month: "11", year: "2025" },
+ vaccine: "COVID-19",
+ vaccineProduct: "Comirnaty 3 LP.8.1",
+ patient: {
+ name: "Liam O'Connor",
+ nhsNumber: "9111222336"
+ },
+ batchNumber: "343643636", // Batch NT2524
+ batchExpiryDate: "2028-03-06",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700005",
+ date: { day: "21", month: "11", year: "2025" },
+ vaccine: "COVID-19",
+ vaccineProduct: "Comirnaty 3 LP.8.1",
+ patient: {
+ name: "Grace Nakamura",
+ nhsNumber: "9111222337"
+ },
+ batchNumber: "343643636", // Batch NT2524
+ batchExpiryDate: "2028-03-06",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700006",
+ date: { day: "20", month: "11", year: "2025" },
+ vaccine: "pertussis",
+ vaccineProduct: "Adacel vaccine suspension",
+ patient: {
+ name: "Daniel Wojcik",
+ nhsNumber: "9111222338"
+ },
+ batchNumber: "46464363", // Batch M8-25-14
+ batchExpiryDate: "2027-11-19",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700007",
+ date: { day: "19", month: "11", year: "2025" },
+ vaccine: "pertussis",
+ vaccineProduct: "Adacel vaccine suspension",
+ patient: {
+ name: "Freya Thomsen",
+ nhsNumber: "9111222339"
+ },
+ batchNumber: "46464363", // Batch M8-25-14
+ batchExpiryDate: "2027-11-19",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700008",
+ date: { day: "18", month: "11", year: "2025" },
+ vaccine: "flu",
+ vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)",
+ patient: {
+ name: "Tomasz Nowak",
+ nhsNumber: "9111222340"
+ },
+ batchNumber: "25523523", // Batch 194-151
+ batchExpiryDate: "2027-12-04",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700009",
+ date: { day: "17", month: "11", year: "2025" },
+ vaccine: "flu",
+ vaccineProduct: "Adjuvanted Trivalent Influenza Vaccine (aTIV)",
+ patient: {
+ name: "Amara Okafor",
+ nhsNumber: "9111222341"
+ },
+ batchNumber: "25523523", // Batch 194-151
+ batchExpiryDate: "2027-12-04",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700010",
+ date: { day: "16", month: "11", year: "2025" },
+ vaccine: "RSV",
+ vaccineProduct: "Abrysvo",
+ patient: {
+ name: "Harold Fitzgerald",
+ nhsNumber: "9111222342"
+ },
+ batchNumber: "46436346", // Batch ANEUEC-91
+ batchExpiryDate: "2027-11-23",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700011",
+ date: { day: "15", month: "11", year: "2025" },
+ vaccine: "RSV",
+ vaccineProduct: "Abrysvo",
+ patient: {
+ name: "Beatrice Lindqvist",
+ nhsNumber: "9111222343"
+ },
+ batchNumber: "46436346", // Batch ANEUEC-91
+ batchExpiryDate: "2027-11-23",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW3HT", // Heaton Moor Health Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700012",
+ date: { day: "14", month: "11", year: "2025" },
+ vaccine: "COVID-19",
+ vaccineProduct: "Comirnaty 3 LP.8.1",
+ patient: {
+ name: "Ellis Marsh",
+ nhsNumber: "9111222344"
+ },
+ batchNumber: "325252145", // Batch PN8471
+ batchExpiryDate: "2024-02-06",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW07E", // Central Manchester Medical Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Left arm",
+ notes: "",
+ editable: true
+ },
+ {
+ id: "700013",
+ date: { day: "13", month: "11", year: "2025" },
+ vaccine: "COVID-19",
+ vaccineProduct: "Comirnaty 3 LP.8.1",
+ patient: {
+ name: "Nadia Hussain",
+ nhsNumber: "9111222345"
+ },
+ batchNumber: "325252145", // Batch PN8471
+ batchExpiryDate: "2024-02-06",
+ organisationId: "RW3", // Central Manchester Trust
+ siteId: "RW07E", // Central Manchester Medical Centre
+ vaccinatorId: "2387441662601", // Jane Smith
+ eligibility: "Based on age",
+ consent: "patient",
+ injectionSite: "Right arm",
+ notes: "",
+ editable: true
}
]
diff --git a/app/routes/vaccines.js b/app/routes/vaccines.js
index a799cb13..01d15ec2 100644
--- a/app/routes/vaccines.js
+++ b/app/routes/vaccines.js
@@ -309,6 +309,8 @@ module.exports = (router) => {
const addedType = req.query.addedType
const deactivated = req.query.deactivated === 'true'
const reactivated = req.query.reactivated === 'true'
+ const updated = req.query.updated === 'true'
+ const updatedRecordsCount = parseInt(req.query.updatedRecordsCount) || 0
const updatedBatchNumber = req.query.batchNumber
const vaccine = data.vaccineStock.find((vaccine) => vaccine.id === req.params.id)
@@ -370,6 +372,8 @@ module.exports = (router) => {
addedType,
deactivated,
reactivated,
+ updated,
+ updatedRecordsCount,
updatedBatchNumber
})
})
@@ -450,6 +454,30 @@ module.exports = (router) => {
})
})
+ // Checking how many records will be affected by an update to a batch
+ router.post('/vaccines/:vaccineId/:batchNumber/update-check', (req, res) => {
+
+ const data = req.session.data
+ const vaccine = data.vaccineStock.find((vaccine) => vaccine.id === req.params.vaccineId)
+ if (!vaccine) { res.redirect('/vaccines'); return }
+ const batch = vaccine.batches.find((batch) => batch.batchNumber === req.params.batchNumber)
+ if (!batch) { res.redirect(`/vaccines/${vaccine.id}`); return }
+
+ const affectedRecordsCount = (data.vaccinationsRecorded || []).filter((record) => {
+ return record.organisationId === vaccine.organisationId &&
+ record.siteId === vaccine.siteId &&
+ record.vaccine === vaccine.vaccine &&
+ record.vaccineProduct === vaccine.vaccineProduct &&
+ record.batchNumber === batch.id
+ }).length
+
+ res.render('vaccines/update-batch-check', {
+ vaccine,
+ batch,
+ affectedRecordsCount
+ })
+ })
+
// Updating a batch
router.post('/vaccines/:vaccineId/:batchNumber/update', (req, res) => {
@@ -461,10 +489,29 @@ module.exports = (router) => {
const expiryDate = new Date(data.batchExpiryDate.year, (parseInt(data.batchExpiryDate.month) - 1), data.batchExpiryDate.day, 12).toISOString().substring(0,10)
+ let updatedRecordsCount = 0
+
+ if (data.updateExistingRecords === 'yes') {
+ for (const record of (data.vaccinationsRecorded || [])) {
+ if (record.organisationId === vaccine.organisationId &&
+ record.siteId === vaccine.siteId &&
+ record.vaccine === vaccine.vaccine &&
+ record.vaccineProduct === vaccine.vaccineProduct &&
+ record.batchNumber === batch.id) {
+ record.batchExpiryDate = expiryDate
+ updatedRecordsCount++
+ }
+ }
+ }
+
+ const updatedBatchNumber = data.batchNumber
+
batch.batchNumber = data.batchNumber;
batch.expiryDate = expiryDate;
- res.redirect('/vaccines/' + vaccine.id)
+ req.session.data.updateExistingRecords = ''
+
+ res.redirect(`/vaccines/${vaccine.id}?updated=true&batchNumber=${encodeURIComponent(updatedBatchNumber)}&updatedRecordsCount=${updatedRecordsCount}`)
})
// View page to deactivate a batch
diff --git a/app/views/vaccines/edit-batch.html b/app/views/vaccines/edit-batch.html
index 44f417cb..4db56248 100644
--- a/app/views/vaccines/edit-batch.html
+++ b/app/views/vaccines/edit-batch.html
@@ -14,9 +14,7 @@
Edit batch
- Your changes will only apply to vaccination records saved in future.
-
-
+
@@ -51,10 +52,15 @@ {{ pageName }}
}) }}
{{ button({
- "text": "Continue"
+ "text": "Confirm update"
}) }}
+ {{ details({
+ summaryText: "Where will these updates show?",
+ html: "Updating this batch will also update the batch number and expiry date on any existing vaccination records that use it. It will update in the PDS and in the patient's GP record.
"
+ }) }}
+
{% else %}
No vaccination records have been saved using batch {{ batch.batchNumber }}.
From fc7f567b9c8776121c79347007c2faae57579a14 Mon Sep 17 00:00:00 2001
From: Caitlin Roach
Date: Wed, 9 Sep 2026 16:15:05 +0100
Subject: [PATCH 4/4] update
---
app/routes/vaccines.js | 1 +
app/views/vaccines/update-batch-check.html | 81 ++++++++++++++++++++--
2 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/app/routes/vaccines.js b/app/routes/vaccines.js
index 01d15ec2..88367ad0 100644
--- a/app/routes/vaccines.js
+++ b/app/routes/vaccines.js
@@ -472,6 +472,7 @@ module.exports = (router) => {
}).length
res.render('vaccines/update-batch-check', {
+ data,
vaccine,
batch,
affectedRecordsCount
diff --git a/app/views/vaccines/update-batch-check.html b/app/views/vaccines/update-batch-check.html
index 444ef612..1709e428 100644
--- a/app/views/vaccines/update-batch-check.html
+++ b/app/views/vaccines/update-batch-check.html
@@ -1,6 +1,6 @@
{% extends 'layout.html' %}
-{% set pageName = "Update existing records for this batch?" %}
+{% set pageName = "Check and confirm batch changes" %}
{% set currentSection = "vaccines" %}
@@ -14,13 +14,80 @@
{{ pageName }}
- {% if affectedRecordsCount > 0 %}
+ {% set summaryRows = [] %}
+ {% set newExpiryDate = data.batchExpiryDate | isoDateFromDateInput %}
+
+ {% if data.batchNumber == batch.batchNumber %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "Batch number"
+ },
+ value: {
+ html: batch.batchNumber
+ }
+ }), summaryRows) %}
+ {% else %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "Previous batch number"
+ },
+ value: {
+ html: batch.batchNumber
+ }
+ }), summaryRows) %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "New batch number"
+ },
+ value: {
+ html: data.batchNumber
+ }
+ }), summaryRows) %}
+ {% endif %}
-
- {{ affectedRecordsCount }} existing vaccination record{{ "s" if affectedRecordsCount != 1 }}
- {{ "uses" if affectedRecordsCount == 1 else "use" }} batch {{ batch.batchNumber }}.
-
-
+ {% if newExpiryDate == batch.expiryDate %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "Expiry date"
+ },
+ value: {
+ html: (batch.expiryDate | govukDate)
+ }
+ }), summaryRows) %}
+ {% else %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "Previous expiry date"
+ },
+ value: {
+ html: (batch.expiryDate | govukDate)
+ }
+ }), summaryRows) %}
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "New expiry date"
+ },
+ value: {
+ html: (newExpiryDate | govukDate)
+ }
+ }), summaryRows) %}
+ {% endif %}
+
+ {% set summaryRows = (summaryRows.push({
+ key: {
+ text: "Number of records affected"
+ },
+ value: {
+ html: affectedRecordsCount
+ }
+ }), summaryRows) %}
+
+ {{ summaryList({
+ classes: "nhsuk-u-margin-bottom-6",
+ rows: summaryRows
+ }) }}
+
+ {% if affectedRecordsCount > 0 %}