feat(practice-domain): add practice domains and let a clinic select one

A practice domain is the field a clinic operates in — beauty, dentistry —
and unlike Specialty it is configuration, not a label: treatment workflows
will bind to its code, so the code is immutable once created and only a
platform admin can mint one. A clinic that has not chosen a domain keeps
behaving exactly as it does today.

Assignment reuses PATCH /api/v1/clinic/{uuid} rather than adding a second
endpoint. An unknown domain uuid is rejected instead of silently dropped,
because a lost selection would only surface at the first protocol-driven
booking.

Also corrects ADR-0003: resource occupancy does not in fact guard the panel
booking path, which writes appointments.resource_id and no occupancy row at
all, so the doctor slot key cannot simply be dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-06 16:06:29 +03:30
co-authored by Claude Opus 5
parent 1d43475724
commit 85985b04a0
13 changed files with 736 additions and 23 deletions
+55
View File
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260806121429 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add practice_domains and let a clinic point at one';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE practice_domains (
id INT AUTO_INCREMENT NOT NULL,
uuid VARCHAR(36) NOT NULL,
code VARCHAR(40) NOT NULL,
name VARCHAR(100) NOT NULL,
sort_order SMALLINT DEFAULT 0 NOT NULL,
active TINYINT DEFAULT 1 NOT NULL,
created_at INT NOT NULL,
updated_at INT NOT NULL,
UNIQUE INDEX UNIQ_820CA399D17F50A6 (uuid),
UNIQUE INDEX uq_practice_domains_code (code),
INDEX idx_practice_domains_active (active, sort_order),
PRIMARY KEY (id)
) DEFAULT CHARACTER SET utf8mb4
SQL);
// NULL یعنی «حوزه‌ای انتخاب نشده» و همان رفتار امروز؛ کلینیک‌های موجود
// عمداً مقدار پیش‌فرض نمی‌گیرند.
$this->addSql('ALTER TABLE clinics ADD practice_domain_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE clinics ADD CONSTRAINT FK_D7053B66613299D9 FOREIGN KEY (practice_domain_id) REFERENCES practice_domains (id) ON DELETE SET NULL');
$this->addSql('CREATE INDEX IDX_D7053B66613299D9 ON clinics (practice_domain_id)');
$this->addSql(
'INSERT INTO practice_domains (uuid, code, name, sort_order, active, created_at, updated_at) VALUES (UUID(), :code, :name, 0, 1, :now, :now)',
['code' => 'beauty', 'name' => 'کلینیک زیبایی', 'now' => time()],
);
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE clinics DROP FOREIGN KEY FK_D7053B66613299D9');
$this->addSql('DROP INDEX IDX_D7053B66613299D9 ON clinics');
$this->addSql('ALTER TABLE clinics DROP practice_domain_id');
$this->addSql('DROP TABLE practice_domains');
}
}