Files
clinicpro/migrations/Version20260806124440.php
hamedandClaude Opus 5 e2e3e6b43b feat(treatment): add treatment protocols, the multi-session course of a service
A protocol says a course of a service runs over several sessions, when each
falls due, which doctor supervises it and which staff may perform it. The row
existing IS the "طول درمان" switch, so there is no separate boolean that could
disagree with the step list.

Each step's offset is measured from the previous session rather than from the
start of the course: laser spacing is a clinical requirement — hair regrows
relative to the last treatment — so a late patient shifts the rest of their
course instead of getting the next session early. That also lets one course use
uneven gaps, which a single min/ideal/max triple cannot express: a botox course
is session 1, then +15 days, then monthly.

Steps and staff are cleared and rewritten in two flushes inside a transaction.
A single flush sends inserts before deletes and the replacement row collides
with the unique (protocol, step_number) index — caught by the replace test.

Removes docs/api/course.md and the task-12 folder. They documented src/Course/,
a module deleted in 65d5831c whose commit message only mentions removing two
test files; that design is superseded by this one.

ServiceItem::$sessionCount is marked deprecated. It never had logic behind it
and session count now comes from the protocol; the column stays in payloads so
existing clients keep working.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 16:36:04 +03:30

81 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260806124440 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add treatment protocols: the multi-session course a service can carry';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE treatment_protocols (
id INT AUTO_INCREMENT NOT NULL,
uuid VARCHAR(36) NOT NULL,
service_item_id INT NOT NULL,
supervisor_doctor_id INT DEFAULT NULL,
active TINYINT DEFAULT 1 NOT NULL,
created_at INT NOT NULL,
updated_at INT NOT NULL,
UNIQUE INDEX UNIQ_76894114D17F50A6 (uuid),
UNIQUE INDEX uq_treatment_protocol_service (service_item_id),
INDEX IDX_76894114EB665C09 (supervisor_doctor_id),
PRIMARY KEY (id)
) DEFAULT CHARACTER SET utf8mb4
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE treatment_protocol_steps (
id INT AUTO_INCREMENT NOT NULL,
protocol_id INT NOT NULL,
step_number SMALLINT NOT NULL,
offset_days SMALLINT NOT NULL,
created_at INT NOT NULL,
UNIQUE INDEX uq_protocol_step_number (protocol_id, step_number),
INDEX IDX_65053F5ACCD59258 (protocol_id),
PRIMARY KEY (id)
) DEFAULT CHARACTER SET utf8mb4
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE treatment_protocol_staff (
id INT AUTO_INCREMENT NOT NULL,
protocol_id INT NOT NULL,
staff_id INT NOT NULL,
UNIQUE INDEX uq_protocol_staff (protocol_id, staff_id),
INDEX IDX_1349C6BACCD59258 (protocol_id),
INDEX IDX_1349C6BAD4D57CD (staff_id),
PRIMARY KEY (id)
) DEFAULT CHARACTER SET utf8mb4
SQL);
// حذف سرویس، پروتکلش را هم می‌برد: پروتکلِ بی‌سرویس معنایی ندارد.
$this->addSql('ALTER TABLE treatment_protocols ADD CONSTRAINT FK_76894114DDEB00C2 FOREIGN KEY (service_item_id) REFERENCES service_items (id) ON DELETE CASCADE');
// ولی رفتن پزشک ناظر نباید دوره را حذف کند — فقط ناظرش خالی می‌شود.
$this->addSql('ALTER TABLE treatment_protocols ADD CONSTRAINT FK_76894114EB665C09 FOREIGN KEY (supervisor_doctor_id) REFERENCES doctors (id) ON DELETE SET NULL');
$this->addSql('ALTER TABLE treatment_protocol_steps ADD CONSTRAINT FK_65053F5ACCD59258 FOREIGN KEY (protocol_id) REFERENCES treatment_protocols (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE treatment_protocol_staff ADD CONSTRAINT FK_1349C6BACCD59258 FOREIGN KEY (protocol_id) REFERENCES treatment_protocols (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE treatment_protocol_staff ADD CONSTRAINT FK_1349C6BAD4D57CD FOREIGN KEY (staff_id) REFERENCES clinic_staff (id) ON DELETE CASCADE');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE treatment_protocol_staff DROP FOREIGN KEY FK_1349C6BACCD59258');
$this->addSql('ALTER TABLE treatment_protocol_staff DROP FOREIGN KEY FK_1349C6BAD4D57CD');
$this->addSql('ALTER TABLE treatment_protocol_steps DROP FOREIGN KEY FK_65053F5ACCD59258');
$this->addSql('ALTER TABLE treatment_protocols DROP FOREIGN KEY FK_76894114DDEB00C2');
$this->addSql('ALTER TABLE treatment_protocols DROP FOREIGN KEY FK_76894114EB665C09');
$this->addSql('DROP TABLE treatment_protocol_staff');
$this->addSql('DROP TABLE treatment_protocol_steps');
$this->addSql('DROP TABLE treatment_protocols');
}
}