$fieldValues */ public function createRevision( int $tenantId, int $handoverId, array $fieldValues, string $status, int $schemaVersion, int $userId, string $changeType = self::CHANGE_TYPE_FIELDS, ): ?int { $nextRevision = $this->revisionRepository->getLatestRevisionNumber($tenantId, $handoverId) + 1; $fieldValuesJson = json_encode($fieldValues, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); return $this->revisionRepository->insert([ 'tenant_id' => $tenantId, 'handover_id' => $handoverId, 'revision' => $nextRevision, 'field_values' => $fieldValuesJson, 'status' => $status, 'schema_version' => $schemaVersion, 'change_type' => $changeType, 'changed_by' => $userId, ]); } /** * List all revisions for a handover, newest first. * * @return list> */ public function listByHandover(int $tenantId, int $handoverId): array { return $this->revisionRepository->listByHandover($tenantId, $handoverId); } /** * Find a specific revision. * * @return array|null */ public function findRevision(int $tenantId, int $handoverId, int $revision): ?array { return $this->revisionRepository->findByRevision($tenantId, $handoverId, $revision); } /** * Compute the diff between two sets of field values. * * @param array $oldValues * @param array $newValues * @return array */ public function computeDiff(array $oldValues, array $newValues): array { $diff = []; $allKeys = array_unique(array_merge(array_keys($oldValues), array_keys($newValues))); foreach ($allKeys as $key) { $oldVal = $oldValues[$key] ?? null; $newVal = $newValues[$key] ?? null; if ($oldVal === null && $newVal !== null) { $diff[$key] = ['type' => 'added', 'old' => null, 'new' => $newVal]; } elseif ($oldVal !== null && $newVal === null) { $diff[$key] = ['type' => 'removed', 'old' => $oldVal, 'new' => null]; } elseif ((string) $oldVal !== (string) $newVal) { $diff[$key] = ['type' => 'changed', 'old' => $oldVal, 'new' => $newVal]; } } return $diff; } /** * Restore a handover to a previous revision's values. * Creates a new revision with the restored values. * * @return array{ok: bool, errors: array} */ public function restoreRevision( int $tenantId, int $handoverId, int $revisionNumber, int $userId, string $permissionLevel, ): array { if ($permissionLevel !== HandoverService::PERMISSION_MANAGE) { return ['ok' => false, 'errors' => ['general' => t('Only managers can restore revisions')]]; } $revision = $this->revisionRepository->findByRevision($tenantId, $handoverId, $revisionNumber); if ($revision === null) { return ['ok' => false, 'errors' => ['general' => t('Revision not found')]]; } $handover = $this->handoverRepository->findById($tenantId, $handoverId); if ($handover === null) { return ['ok' => false, 'errors' => ['general' => t('Handover not found')]]; } $restoredValues = json_decode((string) ($revision['field_values'] ?? '{}'), true); if (!is_array($restoredValues)) { $restoredValues = []; } $restoredStatus = (string) ($revision['status'] ?? $handover['status']); // Update the handover with restored values $fieldValuesJson = json_encode($restoredValues, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $this->handoverRepository->updateFieldValues($tenantId, $handoverId, $fieldValuesJson, $userId); $currentStatus = (string) ($handover['status'] ?? ''); if ($restoredStatus !== $currentStatus) { $this->handoverRepository->updateStatus($tenantId, $handoverId, $restoredStatus, $userId); } // Determine schema version from current handover $schema = json_decode((string) ($handover['schema_snapshot'] ?? '{}'), true); $schemaVersion = is_array($schema) ? (int) ($schema['version'] ?? 1) : 1; // Create a new revision marking the restore $this->createRevision( $tenantId, $handoverId, $restoredValues, $restoredStatus, $schemaVersion, $userId, self::CHANGE_TYPE_RESTORE ); return ['ok' => true, 'errors' => []]; } /** * Get a human-readable label for a change type. */ public static function changeTypeLabel(string $changeType): string { return match ($changeType) { self::CHANGE_TYPE_INITIAL => t('Created'), self::CHANGE_TYPE_FIELDS => t('Fields changed'), self::CHANGE_TYPE_STATUS => t('Status changed'), self::CHANGE_TYPE_BOTH => t('Fields and status changed'), self::CHANGE_TYPE_RESTORE => t('Restored'), default => $changeType, }; } }