Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/lerichardv/patolab-platform/llms.txt

Use this file to discover all available pages before exploring further.

PatoLab’s persistence layer is built on Laravel’s Eloquent ORM and the standard migration system. For local development and automated testing the default driver is SQLite (a file-based database stored at database/database.sqlite), which requires zero configuration. Staging and production deployments should target MySQL or PostgreSQL, configured through the DB_* variables in your .env file. All three engines are supported without any code changes — only the DB_CONNECTION value and the matching host/credential variables differ.

Supported Databases

EnvironmentDriverDB_CONNECTION
Local developmentSQLite (file)sqlite
CI / automated testsSQLite (:memory:)sqlite
Staging / ProductionMySQLmysql
Staging / ProductionPostgreSQLpgsql
Configure your chosen driver in .env:
# SQLite (default — no additional vars required)
DB_CONNECTION=sqlite

# MySQL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=patolab
DB_USERNAME=root
DB_PASSWORD=secret

# PostgreSQL
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=patolab
DB_USERNAME=postgres
DB_PASSWORD=secret

Running Migrations

PatoLab ships with a comprehensive set of migrations that build the entire schema from scratch. Always run migrations before first use and after pulling updates.
1

Run pending migrations

Apply all outstanding migration files to bring the schema up to date:
php artisan migrate
2

Fresh install (wipe, migrate, and seed)

Drop all tables, re-run every migration in order, and load all seed data. Use this when setting up a clean local environment:
php artisan migrate:fresh --seed
3

Production-safe migration

Run migrations on a production database. The --force flag suppresses the interactive confirmation prompt that Laravel shows when APP_ENV=production:
php artisan migrate --force

Seeder Classes

Running php artisan db:seed (or php artisan migrate:fresh --seed) invokes DatabaseSeeder, which calls the following seeders in order:
#Seeder classWhat it populates
1PermissionsSeederSystem permissions
2RolesSeederUser roles and permission assignments
3SettingsSeederApplication-wide settings
4DepartmentSeederGeographic departments
5MunicipalitySeederMunicipalities linked to departments
6SpecimenTypeSeederSpecimen types and examinations
7ReferrerSeederReferring physicians / entities
8LocationSeederLab locations
9SequenceSeederSpecimen sequence counters
10StorageSeederStorage locations
11ProductSeederInventory products / reagents
12CaiRangeSeederCAI (invoice control) ranges
13PrioritySeederKanban board priorities
14SpecimenCategorySeederSpecimen categories
15WorkOrderTypeSeederWork-order type definitions
16CustomerSeederSample customer records
17BanksSeederBank reference list
18UsersSeederDemo admin users
To run a single seeder class without touching the rest of the data:
php artisan db:seed --class=PermissionsSeeder

Audit Logging

PatoLab models that require a full change history use the Auditable trait (app/Traits/Auditable.php). Attaching the trait to any Eloquent model automatically writes a row to the audit_log table for every create, update, and delete event. What is tracked per event:
EventColumns stored
createEvery attribute on the new record (old_value = null, new_value = <value>)
updateOnly the changed columns — old_value holds the previous value, new_value the new one
deleteA single row: column = deleted_at, old_value = active, new_value = deleted
Fields written to audit_log:
  • audit_session_code — a unique 24-character code grouping all rows from a single save operation
  • actioncreate | update | delete
  • table — the model’s database table
  • row_id — the primary key of the affected record
  • column — the specific column that changed
  • old_value / new_value — cast to string (arrays and objects are JSON-encoded)
  • user — the authenticated user’s ID at the time of the change
The following columns are never written to the audit log, regardless of model: created_at, updated_at, deleted_at, password, remember_token.
Audit entries are only created when a user is authenticated. Artisan commands or queue jobs running without an authenticated session will not produce audit records.

Key Migration Files

The table below maps the most significant migration files to the domain concepts they establish:
Migration fileTable(s) created / alteredPurpose
0001_01_01_000000_create_users_table.phpusers, password_reset_tokens, sessionsAuthentication and session management
0001_01_01_000001_create_cache_table.phpcache, cache_locksDatabase-backed cache driver
0001_01_01_000002_create_jobs_table.phpjobs, job_batches, failed_jobsDatabase queue infrastructure
2026_05_08_230814_create_customers_table.phpcustomersPatient / customer profiles
2026_05_11_170346_create_specimen_type_table.phpspecimen_typeSpecimen type catalogue
2026_05_11_170423_create_specimen_type_examination_table.phpspecimen_type_examinationExaminations belonging to a specimen type
2026_05_12_214531_create_specimen_table.phpspecimenCentral specimen entity
2026_05_12_210027_create_specimen_category_table.phpspecimen_categoryCategories grouping specimen types
2026_05_13_133345_create_audit_log_table.phpaudit_logImmutable audit trail
2026_05_19_135200_create_priorities_table.phpprioritiesKanban board priority lanes
2026_05_19_144344_priorities_specimens_order_table.phppriorities_specimens_orderPer-priority card ordering for Kanban
2026_05_19_171558_create_cai_ranges_table.phpcai_rangesFiscal invoice control ranges (CAI)
2026_05_20_154346_create_invoices_table.phpinvoicesBilling invoices
2026_05_20_155852_create_credits_table.phpcreditsCredit notes linked to invoices
2026_05_12_220218_create_inventory_table.phpinventoryProduct stock levels per storage location
2026_05_12_220541_create_specimen_products_table.phpspecimen_productsProducts consumed by a specimen
2026_05_12_225745_create_inventory_movements_table.phpinventory_movementsStock movement log
2026_05_12_142422_create_sequences_table.phpsequencesAuto-increment sequence counters
2026_05_27_192304_create_settings_table.phpsettingsKey-value application configuration
2026_05_28_125509_create_roles_table.phprolesRBAC roles
2026_05_28_125553_create_permissions_table.phppermissionsGranular permissions
2026_06_04_163351_create_specimen_reports_table.phpspecimen_reportsCollaborative pathology reports (Yjs/TipTap)
2026_06_09_154618_create_specimen_type_templates_table.phpspecimen_type_templatesReport templates per specimen type
2026_06_10_185823_create_rentals_table.phprentalsEquipment rental invoicing
2026_06_17_194200_create_user_commission_rules_table.phpuser_commission_rulesCommission rule definitions per user
2026_06_17_195147_create_user_commissions_table.phpuser_commissionsCalculated commissions per specimen
2026_06_30_191219_create_work_order_types_table.phpwork_order_typesWork-order type catalogue
2026_06_30_191250_create_work_orders_table.phpwork_ordersLab work orders

Database Restoration Warning

Always clean up priorities_specimens_order after restoring a backup.When restoring a database backup or importing a dump into a new database, the priorities_specimens_order table can contain stale or orphaned records — rows that reference a specimen_id / priority_id combination that no longer exists in the specimen table. These stale rows cause specimen cards to appear duplicated on the Kanban board.Run the following SQL to delete all orphaned rows:
DELETE FROM priorities_specimens_order
WHERE NOT EXISTS (
    SELECT 1 FROM specimen
    WHERE specimen.id = priorities_specimens_order.specimen_id
      AND specimen.priority_id = priorities_specimens_order.priority_id
);
Alternatively, run the equivalent one-liner via Laravel Tinker:
php artisan tinker --execute="DB::table('priorities_specimens_order')->whereNotExists(function(\$q) { \$q->select(DB::raw(1))->from('specimen')->whereColumn('specimen.id', '=', 'priorities_specimens_order.specimen_id')->whereColumn('specimen.priority_id', '=', 'priorities_specimens_order.priority_id'); })->delete();"
This cleanup is also handled automatically by the migration 2026_06_02_213618_clean_up_obsolete_priorities_specimens_order.php, which runs as part of the normal migration sequence on a fresh installation.

Build docs developers (and LLMs) love