Complete Dentistry commercial suite with PMS depth.
Deploy Ladill Care / deploy (push) Successful in 31s

Add plan lifecycle, visit stages with chair/recovery points, primary dentition charting, imaging void, revenue reports, perio exams, lab cases, and recalls.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-18 17:11:15 +00:00
co-authored by Cursor
parent e227f8705f
commit ce782382c5
37 changed files with 2458 additions and 204 deletions
@@ -0,0 +1,155 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (Schema::hasTable('care_visits') && ! Schema::hasColumn('care_visits', 'specialty_stage')) {
Schema::table('care_visits', function (Blueprint $table) {
$table->string('specialty_stage', 32)->nullable()->after('status')->index();
});
}
if (Schema::hasTable('care_dental_charts') && ! Schema::hasColumn('care_dental_charts', 'dentition')) {
Schema::table('care_dental_charts', function (Blueprint $table) {
$table->string('dentition', 16)->default('adult')->after('notation');
});
}
if (Schema::hasTable('care_dental_treatment_plans')) {
Schema::table('care_dental_treatment_plans', function (Blueprint $table) {
if (! Schema::hasColumn('care_dental_treatment_plans', 'rejected_at')) {
$table->timestamp('rejected_at')->nullable()->after('completed_at');
}
if (! Schema::hasColumn('care_dental_treatment_plans', 'rejection_reason')) {
$table->string('rejection_reason')->nullable()->after('rejected_at');
}
});
}
if (Schema::hasTable('care_dental_images') && ! Schema::hasColumn('care_dental_images', 'deleted_at')) {
Schema::table('care_dental_images', function (Blueprint $table) {
$table->softDeletes();
});
}
if (! Schema::hasTable('care_dental_perio_exams')) {
Schema::create('care_dental_perio_exams', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('owner_ref', 64)->index();
$table->foreignId('organization_id')->constrained('care_organizations')->cascadeOnDelete();
$table->foreignId('patient_id')->constrained('care_patients')->cascadeOnDelete();
$table->foreignId('visit_id')->nullable()->constrained('care_visits')->nullOnDelete();
$table->date('exam_date')->nullable();
$table->string('examiner')->nullable();
$table->text('notes')->nullable();
$table->timestamps();
$table->index(['organization_id', 'patient_id'], 'care_den_perio_exams_org_pt_idx');
});
}
if (! Schema::hasTable('care_dental_perio_sites')) {
Schema::create('care_dental_perio_sites', function (Blueprint $table) {
$table->id();
$table->foreignId('perio_exam_id')->constrained('care_dental_perio_exams')->cascadeOnDelete();
$table->string('tooth_code', 8);
$table->string('surface', 8);
$table->unsignedTinyInteger('probing_mm')->nullable();
$table->boolean('bleeding')->default(false);
$table->boolean('plaque')->default(false);
$table->unsignedTinyInteger('recession_mm')->nullable();
$table->timestamps();
$table->unique(['perio_exam_id', 'tooth_code', 'surface'], 'care_den_perio_sites_unique');
});
}
if (! Schema::hasTable('care_dental_lab_cases')) {
Schema::create('care_dental_lab_cases', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('owner_ref', 64)->index();
$table->foreignId('organization_id')->constrained('care_organizations')->cascadeOnDelete();
$table->foreignId('patient_id')->constrained('care_patients')->cascadeOnDelete();
$table->foreignId('visit_id')->nullable()->constrained('care_visits')->nullOnDelete();
$table->foreignId('plan_item_id')->nullable()->constrained('care_dental_plan_items')->nullOnDelete();
$table->json('tooth_codes')->nullable();
$table->string('case_type', 64);
$table->string('lab_name')->nullable();
$table->string('status', 32)->default('draft')->index();
$table->timestamp('ordered_at')->nullable();
$table->date('due_at')->nullable();
$table->timestamp('received_at')->nullable();
$table->text('notes')->nullable();
$table->string('created_by')->nullable();
$table->timestamps();
$table->index(['organization_id', 'status'], 'care_den_lab_cases_org_status_idx');
$table->index(['patient_id', 'status'], 'care_den_lab_cases_pt_status_idx');
});
}
if (! Schema::hasTable('care_dental_recalls')) {
Schema::create('care_dental_recalls', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('owner_ref', 64)->index();
$table->foreignId('organization_id')->constrained('care_organizations')->cascadeOnDelete();
$table->foreignId('patient_id')->constrained('care_patients')->cascadeOnDelete();
$table->date('due_on');
$table->string('reason')->nullable();
$table->string('status', 32)->default('scheduled')->index();
$table->foreignId('linked_visit_id')->nullable()->constrained('care_visits')->nullOnDelete();
$table->text('notes')->nullable();
$table->string('created_by')->nullable();
$table->timestamps();
$table->index(['organization_id', 'due_on'], 'care_den_recalls_org_due_idx');
$table->index(['patient_id', 'status'], 'care_den_recalls_pt_status_idx');
});
}
}
public function down(): void
{
Schema::dropIfExists('care_dental_recalls');
Schema::dropIfExists('care_dental_lab_cases');
Schema::dropIfExists('care_dental_perio_sites');
Schema::dropIfExists('care_dental_perio_exams');
if (Schema::hasTable('care_dental_images') && Schema::hasColumn('care_dental_images', 'deleted_at')) {
Schema::table('care_dental_images', function (Blueprint $table) {
$table->dropSoftDeletes();
});
}
if (Schema::hasTable('care_dental_treatment_plans')) {
Schema::table('care_dental_treatment_plans', function (Blueprint $table) {
if (Schema::hasColumn('care_dental_treatment_plans', 'rejection_reason')) {
$table->dropColumn('rejection_reason');
}
if (Schema::hasColumn('care_dental_treatment_plans', 'rejected_at')) {
$table->dropColumn('rejected_at');
}
});
}
if (Schema::hasTable('care_dental_charts') && Schema::hasColumn('care_dental_charts', 'dentition')) {
Schema::table('care_dental_charts', function (Blueprint $table) {
$table->dropColumn('dentition');
});
}
if (Schema::hasTable('care_visits') && Schema::hasColumn('care_visits', 'specialty_stage')) {
Schema::table('care_visits', function (Blueprint $table) {
$table->dropColumn('specialty_stage');
});
}
}
};