Files
clinicpro/docs/new_feture/taskes/task-04-service-catalog-v2/database.md
T
hamed 021d0eb6b2 feat: implement cancellation policy, no-show tracking, and waitlist management
- Add implementation notes for cancellation and waitlist features.
- Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting.
- Establish architecture for domain events and outbox pattern to ensure reliable event publishing.
- Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports.
- Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
2026-07-30 11:43:58 +03:30

6.4 KiB
Raw Blame History

دیتابیس — تسک ۰۴

تغییر جدول موجود: service_items

ALTER TABLE service_items
  ADD COLUMN service_category_id INT NULL AFTER section_id,
  ADD COLUMN session_count SMALLINT NOT NULL DEFAULT 1,      -- ۱ = تک‌جلسه، >۱ = دوره‌ای
  ADD COLUMN preparation_note TEXT NULL,                     -- «آماده‌سازی بیمار» — مستند بند ۵
  ADD CONSTRAINT fk_service_items_category
      FOREIGN KEY (service_category_id) REFERENCES service_categories(id) ON DELETE SET NULL,
  ADD KEY idx_service_items_category (service_category_id);

duration_minutes موجود حذف نمی‌شود و همچنان «مدت پایهٔ سرویس» است. آیتم‌ها روی آن اضافه می‌کنند. در حالت booking_mode=service فعلی هیچ تغییری در رفتار نیست چون هیچ سرویسی گروه ندارد و totalMinutes برابر همان duration_minutes می‌ماند.

service_categories — درختی

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE
entity_type / entity_id VARCHAR(10) / INT NOT NULL
parent_id INT NULL FK → خودش، ON DELETE RESTRICT
name VARCHAR(150) NOT NULL
path VARCHAR(255) NOT NULL materialized path: /1/7/23/
depth TINYINT NOT NULL DEFAULT 0
sort_order SMALLINT NOT NULL DEFAULT 0
active TINYINT(1) NOT NULL DEFAULT 1
created_at/updated_at INT NOT NULL
KEY idx_svc_cat_tenant (entity_type, entity_id, active)
KEY idx_svc_cat_parent (parent_id, sort_order)
KEY idx_svc_cat_path   (path)

materialized path به‌جای adjacency خالص: تسک ۰۹ شرط «سرویس در دستهٔ جراحی یا زیردسته‌هایش» را می‌خواهد و با path LIKE '/1/7/%' یک کوئری است، نه یک پیمایش بازگشتی.

سقف عمق: ۴. در ServiceCategoryService اجبار شود.

item_groups

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE
entity_type / entity_id VARCHAR(10) / INT NOT NULL از سرویس مشتق می‌شود
service_item_id INT NOT NULL FK → service_items.id ON DELETE CASCADE
name VARCHAR(150) NOT NULL «نواحی موردنظر»
min_select SMALLINT NOT NULL DEFAULT 0 ۰ = اختیاری
max_select SMALLINT NULL NULL = نامحدود
sort_order SMALLINT NOT NULL DEFAULT 0
active TINYINT(1) NOT NULL DEFAULT 1
created_at/updated_at INT NOT NULL
KEY idx_item_groups_tenant  (entity_type, entity_id, active)
KEY idx_item_groups_service (service_item_id, sort_order)

service_options — «آیتم» مستند

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE از request می‌آید
entity_type / entity_id VARCHAR(10) / INT NOT NULL
item_group_id INT NOT NULL FK ON DELETE CASCADE
name VARCHAR(150) NOT NULL
solo_minutes SMALLINT NOT NULL «زمان تنها»
additional_minutes SMALLINT NULL «زمان اضافه»؛ NULL → solo_minutes
price_rials INT NOT NULL DEFAULT 0
sort_order SMALLINT NOT NULL DEFAULT 0
active TINYINT(1) NOT NULL DEFAULT 1
created_at/updated_at INT NOT NULL
KEY idx_service_options_tenant (entity_type, entity_id, active)
KEY idx_service_options_group  (item_group_id, sort_order)

قید اپلیکیشنی: additional_minutes <= solo_minutes (زمان اضافه هرگز بیشتر از زمان تنها نیست — آماده‌سازی که دو بار نمی‌شود). نقض → 422 با پیام روشن.

service_option_relations

CREATE TABLE service_option_relations (
  id INT PRIMARY KEY AUTO_INCREMENT,
  source_option_id INT NOT NULL,
  target_option_id INT NOT NULL,
  type VARCHAR(20) NOT NULL,                  -- incompatible | requires
  UNIQUE KEY uniq_sor (source_option_id, target_option_id, type),
  KEY idx_sor_target (target_option_id, type),
  CONSTRAINT fk_sor_source FOREIGN KEY (source_option_id) REFERENCES service_options(id) ON DELETE CASCADE,
  CONSTRAINT fk_sor_target FOREIGN KEY (target_option_id) REFERENCES service_options(id) ON DELETE CASCADE
);

فرزند aggregate با ریشهٔ ServiceOption. قید اپلیکیشنی: source != target.

service_branch_overrides

CREATE TABLE service_branch_overrides (
  id INT PRIMARY KEY AUTO_INCREMENT,
  uuid VARCHAR(36) NOT NULL UNIQUE,
  entity_type VARCHAR(10) NOT NULL,
  entity_id INT NOT NULL,
  service_item_id INT NOT NULL,
  branch_id INT NOT NULL,
  price_rials INT NULL,                       -- NULL = ارث از سرویس
  duration_minutes SMALLINT NULL,             -- NULL = ارث از سرویس
  bookable TINYINT(1) NULL,                   -- NULL = ارث؛ 0 = این شعبه ارائه نمی‌دهد
  created_at INT NOT NULL,
  updated_at INT NOT NULL,
  UNIQUE KEY uniq_sbo (service_item_id, branch_id),
  KEY idx_sbo_tenant (entity_type, entity_id),
  KEY idx_sbo_branch (branch_id),
  CONSTRAINT fk_sbo_service FOREIGN KEY (service_item_id) REFERENCES service_items(id) ON DELETE CASCADE,
  CONSTRAINT fk_sbo_branch  FOREIGN KEY (branch_id)       REFERENCES branches(id)      ON DELETE CASCADE
);

سه ستون تهی‌پذیرند تا override جزئی ممکن باشد: فقط قیمت، بدون دست زدن به مدت.

Migration و backfill

ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

backfill لازم نیست: سرویس‌های موجود گروه ندارند، DurationCalculator مقدار service_items.duration_minutes را برمی‌گرداند و رفتار حالت service بدون تغییر می‌ماند.

طبقه‌بندی tenant

جدول وضعیت
service_categories, item_groups, service_options, service_branch_overrides جفت tenant
service_option_relations AGGREGATE_CHILDREN → ریشه ServiceOption

TenantLookupInventoryTest شمارنده دارد؛ findByUuid های جدید (ServiceOptionRepository, ItemGroupRepository) باید با TenantOwnershipChecker جفت شوند و بعد عدد به‌روز شود.