53 lines
3.1 KiB
PHP
53 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class PortalAudit
|
|
{
|
|
/** @param array<string, mixed> $data */
|
|
public static function save(Model $record, array $data, int $actor, ?int $expectedVersion = null): void
|
|
{
|
|
DB::transaction(function () use ($record, $data, $actor, $expectedVersion) {
|
|
$created = ! $record->exists;
|
|
if (! $created) {
|
|
$locked = $record->newQuery()->where($record->getKeyName(), $record->getKey())->lockForUpdate()->firstOrFail();
|
|
$record->setRawAttributes($locked->getAttributes(), true);
|
|
if ($expectedVersion !== null && (int) $locked->getAttribute('version_no') !== $expectedVersion) {
|
|
throw ValidationException::withMessages(['version' => 'Este registo foi alterado por outra operação. Compare os dados actuais antes de continuar; as suas alterações foram conservadas.']);
|
|
}
|
|
}
|
|
$versionedCrm = in_array($record->getTable(), ['investors', 'contacts', 'leads', 'tasks'], true);
|
|
// Exclude free text and personal data from the immutable event payload.
|
|
$fields = ['primary_territory_id', 'territory_version', 'version_no', 'status', 'is_active', 'is_featured', 'sort_order', 'sector_id', 'maturity_level',
|
|
'estimated_investment_amount', 'estimated_investment_currency', 'owner_user_id', 'assignee_user_id', 'due_at',
|
|
'cluster_id', 'project_id', 'corridor_id', 'dossier_id', 'interest_request_id', 'publication_status', 'editor_id', 'reviewer_id', 'role', 'stage', 'value_chain_id', 'from_value_chain_id', 'to_value_chain_id', 'link_type', 'deleted_at'];
|
|
$before = array_intersect_key($record->getAttributes(), array_flip($fields));
|
|
$record->fill($data);
|
|
if ($versionedCrm && ! $created && ! $record->isDirty()) {
|
|
return;
|
|
}
|
|
if ($versionedCrm && ! array_key_exists('version_no', $data)) {
|
|
$record->setAttribute('version_no', $created ? ($record->getAttribute('version_no') ?? 1) : (int) $record->getOriginal('version_no') + 1);
|
|
}
|
|
$changed = array_keys($record->getDirty());
|
|
$record->save();
|
|
if (! $changed) {
|
|
return;
|
|
}
|
|
DB::table('audit_events')->insert([
|
|
'source_system' => 'portal-management', 'actor_user_id' => $actor,
|
|
'entity_type' => $record->getTable(), 'entity_id' => Str::isUuid((string) $record->getKey()) ? $record->getKey() : null,
|
|
'action' => $created ? 'created' : 'updated',
|
|
'before_data' => json_encode(['record_id' => $record->getKey(), 'values' => $before], JSON_THROW_ON_ERROR),
|
|
'after_data' => json_encode(['record_id' => $record->getKey(), 'changed_fields' => $changed,
|
|
'values' => array_intersect_key($record->getAttributes(), array_flip($fields))], JSON_THROW_ON_ERROR),
|
|
]);
|
|
});
|
|
}
|
|
}
|