feat(patient): add SessionAuditLog entity for financial/service change trail
New SessionAuditLog (mirrors AppointmentEvent): session FK, field, operation (create/update/delete), old_value/new_value, actor id+name, note, created_at. Repository saves and lists a session's history (newest first, array hydration). Migration creates the table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20260717113519 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add session_audit_logs table (financial/service change audit trail)';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE session_audit_logs (id INT AUTO_INCREMENT NOT NULL, uuid VARCHAR(36) NOT NULL, field VARCHAR(40) NOT NULL, operation VARCHAR(10) NOT NULL, old_value LONGTEXT DEFAULT NULL, new_value LONGTEXT DEFAULT NULL, actor_user_id INT DEFAULT NULL, actor_name VARCHAR(191) DEFAULT NULL, note VARCHAR(191) DEFAULT NULL, created_at INT NOT NULL, session_id INT NOT NULL, UNIQUE INDEX UNIQ_36B92303D17F50A6 (uuid), INDEX IDX_36B92303613FECDF (session_id), INDEX idx_session_audit_session (session_id, created_at), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
|
||||
$this->addSql('ALTER TABLE session_audit_logs ADD CONSTRAINT FK_36B92303613FECDF FOREIGN KEY (session_id) REFERENCES patient_sessions (id) ON DELETE CASCADE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE session_audit_logs DROP FOREIGN KEY FK_36B92303613FECDF');
|
||||
$this->addSql('DROP TABLE session_audit_logs');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Patient\Entity;
|
||||
|
||||
use App\Patient\Repository\SessionAuditLogRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* تاریخچهی تغییرات مالی/خدماتی یک مراجعه (Audit Log): چه کسی، چه چیزی، کِی و
|
||||
* مقدار قبل/بعد را تغییر داد. برای شفافیت و قابلیت پیگیری کامل پرونده.
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: SessionAuditLogRepository::class)]
|
||||
#[ORM\Table(name: 'session_audit_logs')]
|
||||
#[ORM\Index(columns: ['session_id', 'created_at'], name: 'idx_session_audit_session')]
|
||||
class SessionAuditLog
|
||||
{
|
||||
public const OP_CREATE = 'create';
|
||||
public const OP_UPDATE = 'update';
|
||||
public const OP_DELETE = 'delete';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
||||
private string $uuid;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: PatientSession::class)]
|
||||
#[ORM\JoinColumn(name: 'session_id', nullable: false, onDelete: 'CASCADE')]
|
||||
private PatientSession $session;
|
||||
|
||||
/** فیلد تغییرکرده: visit_price_rials | services | consumables | payment | discount | ... */
|
||||
#[ORM\Column(type: 'string', length: 40)]
|
||||
private string $field;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 10)]
|
||||
private string $operation;
|
||||
|
||||
#[ORM\Column(name: 'old_value', type: 'text', nullable: true)]
|
||||
private ?string $oldValue = null;
|
||||
|
||||
#[ORM\Column(name: 'new_value', type: 'text', nullable: true)]
|
||||
private ?string $newValue = null;
|
||||
|
||||
#[ORM\Column(name: 'actor_user_id', type: 'integer', nullable: true)]
|
||||
private ?int $actorUserId = null;
|
||||
|
||||
#[ORM\Column(name: 'actor_name', type: 'string', length: 191, nullable: true)]
|
||||
private ?string $actorName = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 191, nullable: true)]
|
||||
private ?string $note = null;
|
||||
|
||||
#[ORM\Column(name: 'created_at', type: 'integer')]
|
||||
private int $createdAt;
|
||||
|
||||
public function __construct(PatientSession $session, string $field, string $operation)
|
||||
{
|
||||
$this->uuid = Uuid::v4()->toRfc4122();
|
||||
$this->session = $session;
|
||||
$this->field = $field;
|
||||
$this->operation = $operation;
|
||||
$this->createdAt = time();
|
||||
}
|
||||
|
||||
public function setActor(?int $userId, ?string $name): self { $this->actorUserId = $userId; $this->actorName = $name; return $this; }
|
||||
public function setValues(?string $old, ?string $new): self { $this->oldValue = $old; $this->newValue = $new; return $this; }
|
||||
public function setNote(?string $note): self { $this->note = $note; return $this; }
|
||||
|
||||
public function getUuid(): string { return $this->uuid; }
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'field' => $this->field,
|
||||
'operation' => $this->operation,
|
||||
'old_value' => $this->oldValue,
|
||||
'new_value' => $this->newValue,
|
||||
'actor_name' => $this->actorName,
|
||||
'note' => $this->note,
|
||||
'created_at' => $this->createdAt,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Patient\Repository;
|
||||
|
||||
use App\Patient\Entity\SessionAuditLog;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class SessionAuditLogRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry) { parent::__construct($registry, SessionAuditLog::class); }
|
||||
|
||||
public function save(SessionAuditLog $e, bool $flush = true): void
|
||||
{
|
||||
$this->getEntityManager()->persist($e);
|
||||
if ($flush) {
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/** تاریخچهی یک مراجعه (جدید → قدیم)، آرایهای. */
|
||||
public function findBySessionUuid(string $sessionUuid): array
|
||||
{
|
||||
return $this->createQueryBuilder('l')
|
||||
->select('l.field AS field', 'l.operation AS operation', 'l.oldValue AS old_value', 'l.newValue AS new_value', 'l.actorName AS actor_name', 'l.note AS note', 'l.createdAt AS created_at')
|
||||
->join('l.session', 's')
|
||||
->where('s.uuid = :uuid')->setParameter('uuid', $sessionUuid)
|
||||
->orderBy('l.createdAt', 'DESC')->addOrderBy('l.id', 'DESC')
|
||||
->getQuery()->getArrayResult();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user