Until now a resource was picked by type and skill alone, so two devices of the same type were indistinguishable even when only one of them performed the service — and there was nowhere to say that this doctor takes 30 minutes for a filler while that one takes 45. ResourceServiceOffering is that link: resource ↔ service item, with an optional duration, an optional price and an active flag. Because a "service option" here is itself a ServiceItem inside an ItemGroup, one table covers both levels the spec asks for — a row against the parent item is "resource + service", a row against a member item is "resource + option". A third table would have meant two sources of truth for one concept and a rewrite of every path that already speaks ServiceItem. It is an aggregate child of ClinicResource, like ResourceSkill: no tenant columns of its own, since the resource already carries the pair and a copy is just something that can drift. The constructor refuses a resource and a service from different environments — TenantFilter does not cover that case, as both uuids arrive from the request body and the filter does not apply to aggregate children. null means inherit, not zero: an explicit zero is a duration that does not exist, while null means this resource has nothing to say and the resolver should look one level up. Zero and negative values are rejected outright. Tests cover the pair being stored, the duplicate pair hitting the unique constraint, the cross-environment guard, null-means-inherit, one service across two devices with different numbers, and deactivating without losing them. Suite 1264 green, phpstan at its 14-error baseline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38 lines
2.2 KiB
PHP
38 lines
2.2 KiB
PHP
<?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 Version20260801172619 extends AbstractMigration
|
|
{
|
|
public function getDescription(): string
|
|
{
|
|
return '';
|
|
}
|
|
|
|
public function up(Schema $schema): void
|
|
{
|
|
// this up() migration is auto-generated, please modify it to your needs
|
|
$this->addSql('CREATE TABLE resource_service_offerings (id INT AUTO_INCREMENT NOT NULL, duration_minutes SMALLINT DEFAULT NULL, price_rials BIGINT DEFAULT NULL, active TINYINT DEFAULT 1 NOT NULL, created_at INT NOT NULL, updated_at INT NOT NULL, resource_id INT NOT NULL, service_item_id INT NOT NULL, INDEX IDX_6D71D02D89329D25 (resource_id), INDEX IDX_6D71D02DDDEB00C2 (service_item_id), INDEX idx_offering_service (service_item_id, active), UNIQUE INDEX uniq_resource_service (resource_id, service_item_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
|
|
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 (queue_name, available_at, delivered_at, id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
|
|
$this->addSql('ALTER TABLE resource_service_offerings ADD CONSTRAINT FK_6D71D02D89329D25 FOREIGN KEY (resource_id) REFERENCES clinic_resources (id) ON DELETE CASCADE');
|
|
$this->addSql('ALTER TABLE resource_service_offerings ADD CONSTRAINT FK_6D71D02DDDEB00C2 FOREIGN KEY (service_item_id) REFERENCES service_items (id) ON DELETE CASCADE');
|
|
}
|
|
|
|
public function down(Schema $schema): void
|
|
{
|
|
// this down() migration is auto-generated, please modify it to your needs
|
|
$this->addSql('ALTER TABLE resource_service_offerings DROP FOREIGN KEY FK_6D71D02D89329D25');
|
|
$this->addSql('ALTER TABLE resource_service_offerings DROP FOREIGN KEY FK_6D71D02DDDEB00C2');
|
|
$this->addSql('DROP TABLE resource_service_offerings');
|
|
$this->addSql('DROP TABLE messenger_messages');
|
|
}
|
|
}
|