| Job Type | Meter Read & Property Check |
| Service Category | Utility |
| Client | Birmingham City Council |
| Location | 42 High Street, Birmingham |
| Status | Completed |
| Completed At | 08/12/2024, 10:45 AM |
| Duration | 1h 30m |
| Our Reference | GSC-2024-0847 |
| Job Type | Meter Read & Property Check |
| Service Category | Utility |
| Client | Birmingham City Council |
| Location | 42 High Street, Birmingham |
| Status | Completed |
| Completed At | 08/12/2024, 10:45 AM |
| Duration | 1h 30m |
| Our Reference | GSC-2024-0847 |
| property@birmingham.gov.uk | |
| Customer Ref | BHAM-2024-847 |
| Helpdesk | 0800 123 4567 |
| Property Type | 3 bed semi-detached house |
| Occupancy | Vacant |
| Access Method | Key safe |
| Access Result | ✓ Successful - All areas accessed |
| Visit Reason | Routine meter reading and property condition check |
| Tasks Completed | 6/6 ✓ All tasks completed successfully |
| Start Time | 09:15 AM |
| End Time | 10:45 AM |
| Total Duration | 1h 30m |
| Job Value | £85.00 |
| Electric Meter |
Serial: E123456789 Reading: 12567 (Previous: 12345) Consumption: 222 kWh Read at: 09:30 AM |
| Gas Meter |
Serial: G987654321 Reading: 6823 (Previous: 6789) Consumption: 34 units Read at: 09:35 AM |
| Water Meter |
Serial: W555123789 Reading: 3478 (Previous: 3456) Consumption: 22 units Read at: 09:40 AM |
| Assigned At | 08/12/24, 8:00 AM |
| Accepted At | 08/12/24, 8:30 AM |
| Scheduled Date | 08/12/2024 |
| Scheduled Time | 9:00 AM - 11:00 AM |
| Started At | 08/12/2024 9:15 AM |
| Completed At | 08/12/2024 10:45 AM |
| Report Due | Today by 11:59 PM |
// Get job details GET /api/subjob/{subJobId} Authorization: Bearer {token} // Submit job report POST /api/subjob/{subJobId}/submitreport Authorization: Bearer {token} Content-Type: multipart/form-data Body: { "reportData": { /* job-specific report data */ }, "finalImages": [files], "agentSignature": file, "submittedAt": "2024-12-08T14:30:00Z" } // Download job report GET /api/subjob/{subJobId}/report Authorization: Bearer {token} // Get report template GET /api/subjob/{subJobId}/reporttemplate Authorization: Bearer {token} // Update meter readings PUT /api/subjob/{subJobId}/meterreadings Authorization: Bearer {token} Body: { "electricReading": { "value": "12567", "readAt": "2024-12-08T09:30:00Z" }, "gasReading": { "value": "6823", "readAt": "2024-12-08T09:35:00Z" }, "waterReading": { "value": "3478", "readAt": "2024-12-08T09:40:00Z" } }
{
"subJob": {
"id": 847,
"ourJobReference": "GSC-2024-0847",
"status": "Completed",
"priority": "Standard",
"price": 85.00,
"rateType": "Standard",
"assignedAt": "2024-12-08T08:00:00Z",
"acceptedAt": "2024-12-08T08:30:00Z",
"startedAt": "2024-12-08T09:15:00Z",
"completedAt": "2024-12-08T10:45:00Z",
"scheduledDate": "2024-12-08",
"scheduledTime": "9:00 AM - 11:00 AM",
"estimatedDurationMinutes": 30,
"notes": "Water damage visible in kitchen ceiling corner. All meters read successfully. Property secured.",
"ppeRequirements": "Standard PPE",
"requiresSpecialEquipment": false,
"assignedAgentName": "Current User"
},
"clientJob": {
"id": 2847,
"clientName": "Birmingham City Council",
"jobType": "Meter Read & Property Check",
"jobServiceCategory": "Utility",
"clientJobReference": "BHAM-2024-847",
"customerName": "Property Manager",
"customerEmail": "property@birmingham.gov.uk",
"customerTelephone": "07700 123456",
"customerAddress": "42 High Street",
"customerCity": "Birmingham",
"customerCounty": "West Midlands",
"customerPostcode": "B1 2LB",
"customerCountry": "United Kingdom",
"customerReference": "BHAM-2024-847",
"companyName": "Birmingham City Council",
"clientHelpdeskPhone": "0800 123 4567",
"visitReason": "Routine meter reading and property condition check",
"propertyDetails": {
"propertyType": "3 bed semi-detached house",
"occupancyStatus": "Vacant",
"keySafeLocation": "Left of front door",
"keySafeCode": "2847"
},
"meters": [
{
"product": "Electric",
"serial": "E123456789",
"mpan": "1234567890123",
"type": "Digital",
"size": "Standard",
"location": "External meter box",
"lastReading": "12345",
"lastReadingDate": "2024-11-01"
},
{
"product": "Gas",
"serial": "G987654321",
"mpan": "9876543210987",
"type": "Credit",
"size": "U6",
"location": "External box - front",
"lastReading": "6789",
"lastReadingDate": "2024-11-01"
},
{
"product": "Water",
"serial": "W555123789",
"type": "Standard",
"location": "Under kitchen sink",
"lastReading": "3456",
"lastReadingDate": "2024-11-01"
}
],
"jobImages": [
{
"id": 301,
"description": "Property Front",
"uploadedAt": "2024-12-08T09:15:00Z",
"isPdf": false,
"isMandatory": true
},
{
"id": 302,
"description": "Electric Meter",
"uploadedAt": "2024-12-08T09:30:00Z",
"isPdf": false,
"isMandatory": true
},
{
"id": 303,
"description": "Gas Meter",
"uploadedAt": "2024-12-08T09:35:00Z",
"isPdf": false,
"isMandatory": true
},
{
"id": 304,
"description": "Water Meter",
"uploadedAt": "2024-12-08T09:40:00Z",
"isPdf": false,
"isMandatory": true
},
{
"id": 305,
"description": "Kitchen Area",
"uploadedAt": "2024-12-08T10:20:00Z",
"isPdf": false,
"isMandatory": false
},
{
"id": 306,
"description": "Water Damage",
"uploadedAt": "2024-12-08T10:25:00Z",
"isPdf": false,
"isMandatory": false
},
{
"id": 307,
"description": "Property Rear",
"uploadedAt": "2024-12-08T10:40:00Z",
"isPdf": false,
"isMandatory": false
},
{
"id": 308,
"description": "Secured Property",
"uploadedAt": "2024-12-08T10:45:00Z",
"isPdf": false,
"isMandatory": false
}
]
}
}
// 1. Total duration calculation
const startTime = new Date(subJob.startedAt);
const endTime = new Date(subJob.completedAt);
const durationMs = endTime.getTime() - startTime.getTime();
const totalHours = Math.floor(durationMs / (1000 * 60 * 60));
const totalMinutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
const totalDuration = `${totalHours}h ${totalMinutes}m`;
// 2. Customer address concatenation
const customerAddress = `${clientJob.customerAddress}, ${clientJob.customerCity}`;
// 3. Image count
const imageCount = clientJob.jobImages?.length || 0;
// 4. Report due countdown
const completedTime = new Date(subJob.completedAt);
const reportDeadline = new Date(completedTime);
reportDeadline.setHours(23, 59, 59, 999); // Same day 11:59 PM
const now = new Date();
const timeRemaining = reportDeadline.getTime() - now.getTime();
const hours = Math.floor(timeRemaining / (1000 * 60 * 60));
const minutes = Math.floor((timeRemaining % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeRemaining % (1000 * 60)) / 1000);
const timeRemainingDisplay = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
// 5. Meter reading calculations
const electricMeterReading = {
serial: clientJob.meters.find(m => m.product === 'Electric')?.serial,
currentReading: '12567', // from report submission
previousReading: '12345',
consumption: 12567 - 12345
};
// 6. Report submission status
const reportSubmissionStatus = subJob.status === 'Completed' ? 'Awaiting Report Submission' : 'Report Submitted';
// 7. Access result summary
const accessResult = '✓ Successful - All areas accessed'; // based on completion and no issues
// 8. Tasks completed count
const tasksCompleted = '6/6 ✓ All tasks completed successfully'; // calculate from checklist
// 1. Total duration calculation
let startTime = ISO8601DateFormatter().date(from: subJob.startedAt) ?? Date()
let endTime = ISO8601DateFormatter().date(from: subJob.completedAt) ?? Date()
let duration = endTime.timeIntervalSince(startTime)
let hours = Int(duration) / 3600
let minutes = (Int(duration) % 3600) / 60
let totalDuration = "\(hours)h \(minutes)m"
// 2. Customer address
let customerAddress = "\(clientJob.customerAddress), \(clientJob.customerCity)"
// 3. Image count
let imageCount = clientJob.jobImages?.count ?? 0
// 4. Report countdown timer
let completedTime = ISO8601DateFormatter().date(from: subJob.completedAt) ?? Date()
var reportDeadline = Calendar.current.startOfDay(for: completedTime)
reportDeadline = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: reportDeadline) ?? completedTime
let timeRemaining = reportDeadline.timeIntervalSinceNow
let remainingHours = Int(timeRemaining) / 3600
let remainingMinutes = (Int(timeRemaining) % 3600) / 60
let remainingSeconds = Int(timeRemaining) % 60
let timeRemainingDisplay = String(format: "%02d:%02d:%02d", remainingHours, remainingMinutes, remainingSeconds)
// 5. Meter reading calculations
let electricMeter = clientJob.meters?.first { $0.product == "Electric" }
let electricReading = MeterReading(
serial: electricMeter?.serial ?? "",
current: "12567",
previous: electricMeter?.lastReading ?? "",
consumption: 12567 - Int(electricMeter?.lastReading ?? "0") ?? 0
)
// 1. Total duration calculation
const startTime = new Date(subJob.startedAt);
const endTime = new Date(subJob.completedAt);
const durationMs = endTime - startTime;
const totalHours = Math.floor(durationMs / (1000 * 60 * 60));
const totalMinutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
const totalDuration = `${totalHours}h ${totalMinutes}m`;
// 2. Report countdown with live updates
const [timeRemaining, setTimeRemaining] = useState('00:00:00');
useEffect(() => {
const completedTime = new Date(subJob.completedAt);
const reportDeadline = new Date(completedTime);
reportDeadline.setHours(23, 59, 59, 999);
const interval = setInterval(() => {
const now = new Date();
const remaining = reportDeadline - now;
if (remaining > 0) {
const hours = Math.floor(remaining / (1000 * 60 * 60));
const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((remaining % (1000 * 60)) / 1000);
setTimeRemaining(
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
);
} else {
setTimeRemaining('00:00:00');
}
}, 1000);
return () => clearInterval(interval);
}, [subJob.completedAt]);
// 3. Meter readings with consumption calculation
const processedReadings = clientJob.meters?.map(meter => ({
...meter,
currentReading: getCurrentReading(meter.product), // from report data
consumption: getCurrentReading(meter.product) - parseInt(meter.lastReading || '0')
}));
New → Assigned → Accepted → En Route → In Progress → Completed → Report Submitted → Invoiced
↑ Current State