diff --git a/migrations/000_baseline.sql b/migrations/000_baseline.sql index 42af57c..dc7ddc8 100644 --- a/migrations/000_baseline.sql +++ b/migrations/000_baseline.sql @@ -59,7 +59,8 @@ CREATE TABLE IF NOT EXISTS `bulk_operations` ( `ticket_ids` text NOT NULL, `performed_by` int(11) NOT NULL, `parameters` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`parameters`)), - `status` varchar(20) DEFAULT 'pending', + -- 32, not 20: 'completed_with_errors' is 21 chars (see 001_widen_bulk_operations_status.sql) + `status` varchar(32) DEFAULT 'pending', `total_tickets` int(11) DEFAULT NULL, `processed_tickets` int(11) DEFAULT 0, `failed_tickets` int(11) DEFAULT 0, diff --git a/migrations/001_widen_bulk_operations_status.sql b/migrations/001_widen_bulk_operations_status.sql new file mode 100644 index 0000000..0ded271 --- /dev/null +++ b/migrations/001_widen_bulk_operations_status.sql @@ -0,0 +1,12 @@ +-- Widen bulk_operations.status +-- +-- The code writes 'completed_with_errors' (21 chars) when a bulk operation +-- finishes with per-ticket failures, but the column was varchar(20), so the +-- write failed with "Data too long for column 'status'". This was unreachable +-- while bulk status changes forced every transition through; now that they +-- honour the Workflow Designer, partial failures are a normal outcome. +-- +-- Safe to re-run. + +ALTER TABLE `bulk_operations` + MODIFY COLUMN `status` varchar(32) DEFAULT 'pending'; diff --git a/models/BulkOperationsModel.php b/models/BulkOperationsModel.php index 5ae4533..0b601d4 100644 --- a/models/BulkOperationsModel.php +++ b/models/BulkOperationsModel.php @@ -377,14 +377,22 @@ class BulkOperationsModel ]; } - // Update operation status - $status = $failed > 0 ? 'completed_with_errors' : 'completed'; - $sql = "UPDATE bulk_operations SET status = ?, processed_tickets = ?, failed_tickets = ?, - completed_at = NOW() WHERE operation_id = ?"; - $stmt = $this->conn->prepare($sql); - $stmt->bind_param("siii", $status, $processed, $failed, $operationId); - $stmt->execute(); - $stmt->close(); + // Update operation status. This is bookkeeping only and runs after the + // ticket changes are committed, so a failure here (e.g. the status column + // not yet widened by 001_widen_bulk_operations_status.sql on an instance + // deployed ahead of its migrations) must not turn a completed operation + // into an error response. + try { + $status = $failed > 0 ? 'completed_with_errors' : 'completed'; + $sql = "UPDATE bulk_operations SET status = ?, processed_tickets = ?, failed_tickets = ?, + completed_at = NOW() WHERE operation_id = ?"; + $stmt = $this->conn->prepare($sql); + $stmt->bind_param("siii", $status, $processed, $failed, $operationId); + $stmt->execute(); + $stmt->close(); + } catch (Throwable $e) { + error_log("Bulk operation $operationId completed but status bookkeeping failed: " . $e->getMessage()); + } $result = ['processed' => $processed, 'failed' => $failed]; if (!empty($errors)) {