Added validation when task type, category, signatories and action taken are empty upon completing a task
This commit is contained in:
parent
1074572905
commit
98355c3707
|
|
@ -556,22 +556,56 @@ class TasksController {
|
||||||
required String status,
|
required String status,
|
||||||
}) async {
|
}) async {
|
||||||
if (status == 'completed') {
|
if (status == 'completed') {
|
||||||
// fetch current metadata to validate
|
// fetch current metadata to validate several required fields
|
||||||
try {
|
try {
|
||||||
final row = await _client
|
final row = await _client
|
||||||
.from('tasks')
|
.from('tasks')
|
||||||
.select('request_type, request_category')
|
// include all columns that must be non-null/empty before completing
|
||||||
|
.select(
|
||||||
|
'request_type, request_category, requested_by, noted_by, received_by, action_taken',
|
||||||
|
)
|
||||||
.eq('id', taskId)
|
.eq('id', taskId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
final rt = row is Map ? row['request_type'] : null;
|
if (row is! Map<String, dynamic>) {
|
||||||
final rc = row is Map ? row['request_category'] : null;
|
throw Exception('Task not found');
|
||||||
if (rt == null || rc == null) {
|
}
|
||||||
|
|
||||||
|
final rt = row['request_type'];
|
||||||
|
final rc = row['request_category'];
|
||||||
|
final requested = row['requested_by'];
|
||||||
|
final noted = row['noted_by'];
|
||||||
|
final received = row['received_by'];
|
||||||
|
final action = row['action_taken'];
|
||||||
|
|
||||||
|
final missing = <String>[];
|
||||||
|
if (rt == null || (rt is String && rt.trim().isEmpty)) {
|
||||||
|
missing.add('request type');
|
||||||
|
}
|
||||||
|
if (rc == null || (rc is String && rc.trim().isEmpty)) {
|
||||||
|
missing.add('request category');
|
||||||
|
}
|
||||||
|
if (requested == null ||
|
||||||
|
(requested is String && requested.trim().isEmpty)) {
|
||||||
|
missing.add('requested by');
|
||||||
|
}
|
||||||
|
if (noted == null || (noted is String && noted.trim().isEmpty)) {
|
||||||
|
missing.add('noted by');
|
||||||
|
}
|
||||||
|
if (received == null ||
|
||||||
|
(received is String && received.trim().isEmpty)) {
|
||||||
|
missing.add('received by');
|
||||||
|
}
|
||||||
|
if (action == null || (action is String && action.trim().isEmpty)) {
|
||||||
|
missing.add('action taken');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missing.isNotEmpty) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Request type and category must be set before completing a task.',
|
'The following fields must be set before completing a task: ${missing.join(', ')}.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// rethrow so callers can handle
|
// rethrow so callers can handle (UI will display message)
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2731,9 +2731,16 @@ class _TaskDetailScreenState extends ConsumerState<TaskDetailScreen>
|
||||||
// Update DB only — Supabase realtime stream will emit the
|
// Update DB only — Supabase realtime stream will emit the
|
||||||
// updated task list, so explicit invalidation here causes a
|
// updated task list, so explicit invalidation here causes a
|
||||||
// visible loading/refresh and is unnecessary.
|
// visible loading/refresh and is unnecessary.
|
||||||
|
try {
|
||||||
await ref
|
await ref
|
||||||
.read(tasksControllerProvider)
|
.read(tasksControllerProvider)
|
||||||
.updateTaskStatus(taskId: task.id, status: value);
|
.updateTaskStatus(taskId: task.id, status: value);
|
||||||
|
} catch (e) {
|
||||||
|
// surface validation or other errors to user
|
||||||
|
if (mounted) {
|
||||||
|
showErrorSnackBar(context, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (context) => _statusOptions
|
itemBuilder: (context) => _statusOptions
|
||||||
.map(
|
.map(
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,8 @@ void main() {
|
||||||
// note: controller expects SupabaseClient; using dynamic bypass.
|
// note: controller expects SupabaseClient; using dynamic bypass.
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cannot complete a task without request details', () async {
|
test('cannot complete a task without required metadata', () async {
|
||||||
// insert a task with no metadata
|
// insert a task with no metadata at all
|
||||||
final row = {'id': 'tsk-1', 'status': 'queued'};
|
final row = {'id': 'tsk-1', 'status': 'queued'};
|
||||||
fake.tables['tasks']!.add(row);
|
fake.tables['tasks']!.add(row);
|
||||||
|
|
||||||
|
|
@ -101,6 +101,43 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cannot complete when signatories or action taken missing', () async {
|
||||||
|
// insert a task that has the basic request metadata but nothing else
|
||||||
|
final row = {
|
||||||
|
'id': 'tsk-3',
|
||||||
|
'status': 'queued',
|
||||||
|
'request_type': 'Repair',
|
||||||
|
'request_category': 'Hardware',
|
||||||
|
};
|
||||||
|
fake.tables['tasks']!.add(row);
|
||||||
|
|
||||||
|
// still missing signatories/actionTaken
|
||||||
|
expect(
|
||||||
|
() => controller.updateTaskStatus(taskId: 'tsk-3', status: 'completed'),
|
||||||
|
throwsA(isA<Exception>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// add signatories but actionTaken still missing
|
||||||
|
await controller.updateTask(
|
||||||
|
taskId: 'tsk-3',
|
||||||
|
requestedBy: 'Alice',
|
||||||
|
notedBy: 'Bob',
|
||||||
|
receivedBy: 'Carol',
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
() => controller.updateTaskStatus(taskId: 'tsk-3', status: 'completed'),
|
||||||
|
throwsA(isA<Exception>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// add action taken (empty JSON string)
|
||||||
|
await controller.updateTask(taskId: 'tsk-3', actionTaken: '{}');
|
||||||
|
await controller.updateTaskStatus(taskId: 'tsk-3', status: 'completed');
|
||||||
|
expect(
|
||||||
|
fake.tables['tasks']!.firstWhere((t) => t['id'] == 'tsk-3')['status'],
|
||||||
|
'completed',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('completing after adding metadata succeeds', () async {
|
test('completing after adding metadata succeeds', () async {
|
||||||
// insert a task
|
// insert a task
|
||||||
final row = {'id': 'tsk-2', 'status': 'queued'};
|
final row = {'id': 'tsk-2', 'status': 'queued'};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user