mandant-crm/backend/app/Modules/CustomField/Actions/UpdateCustomFieldValueAction.php
Tim Stollberg f0bb80707a feat(backend): commit uncommitted CustomField, dynamic HistoryType, and Import modules
Mirrors the frontend catch-up in 570f17e: a substantial amount of
finished-but-never-committed backend work had accumulated locally.

- New CustomField module: admin-managed field definitions per client,
  reorderable, typed values stored per client (backing the frontend's
  ClientCustomFieldCard + settings/fields page)
- History entries switch from a hardcoded type enum to a dynamic,
  admin-managed HistoryType module (reorderable, backing
  settings/history-types)
- New Import module: CSV client import (backing settings/import)
- Clients table gains soft deletes (deleted_at)
- Supporting factories, seeders, policies, and feature tests for all
  of the above

All 63 backend tests pass locally before this commit.
2026-07-18 16:34:18 +07:00

68 lines
2.1 KiB
PHP

<?php
namespace App\Modules\CustomField\Actions;
use App\Models\User;
use App\Modules\Client\Models\Client;
use App\Modules\CustomField\Models\CustomFieldDefinition;
use App\Modules\CustomField\Models\CustomFieldValue;
use App\Modules\History\Models\HistoryEntry;
use App\Modules\History\Models\HistoryEntryType;
use Illuminate\Support\Facades\DB;
final class UpdateCustomFieldValueAction
{
/**
* Upsert a client's value for one custom field. When the value actually
* changes, append a read-only entry to the Verlauf log (history_entries)
* so every base-data change is audited alongside the manual notes.
*/
public function handle(
Client $client,
CustomFieldDefinition $definition,
?string $value,
User $author
): CustomFieldValue {
$new = $value === null || $value === '' ? null : (string) $value;
return DB::transaction(function () use ($client, $definition, $new, $author) {
$record = CustomFieldValue::firstOrNew([
'client_id' => $client->id,
'custom_field_definition_id' => $definition->id,
]);
$old = $record->value;
if ($old === $new) {
return $record;
}
$record->value = $new;
$record->author_id = $author->id;
$record->save();
HistoryEntry::create([
'client_id' => $client->id,
'type' => HistoryEntryType::FieldChange,
'body' => $this->changeBody($definition->label, $old, $new),
'custom_field_definition_id' => $definition->id,
'author_id' => $author->id,
]);
return $record;
});
}
private function changeBody(string $label, ?string $old, ?string $new): string
{
if ($old === null) {
return "{$label} gesetzt: „{$new}";
}
if ($new === null) {
return "{$label} entfernt (war „{$old}“)";
}
return "{$label} geändert: „{$old}“ → „{$new}";
}
}