TuffOps stores tenant configuration in the settings table. Each row has a name, a typed value (stored as JSON), and a readonly flag. Office users with settings.view / settings.edit manage non-readonly keys from Settings in the admin UI. The SettingsSeeder and some migrations insert defaults with putIfMissing so new installs get a baseline without overwriting production edits.
Values are read through Setting::get($name, $default). Several flags have small helpers on Setting (for example featureChecklistsEnabled(), complianceEnabled(), featureCustomerPortalEpaEnabled()).
Readonly vs editable
SettingsController only lists rows where readonly is false. Readonly keys still exist in the database and in code paths; they are not shown on the bulk edit screen.
- Seeder example:
customer_integration_enabled is readonly: true — integration wiring is treated as infrastructure, not day-to-day ops.
Feature flags (boolean)
| Key | Default (seeder / migration) | What it gates |
|---|
feature_flag_checklists_enabled | false (seeder) | Checklist admin, work-order checklist UI, auto-attach, mobile checklist actions, and checklist-related compliance guards. See Checklists. |
feature_flag_invoices_enabled | true (seeder) | Invoice admin (InvoicesController), invoice blocks on the work order form, and related flows. |
feature_flag_customer_portal_enabled | false (seeder) | Customer portal subdomain and login. Off → portal middleware returns 403 (EnsureCustomerPortalFeatureEnabled). |
feature_flag_customer_portal_epa_enabled | false (seeder) | Exposes EPA/compliance data on the portal. Effective only when feature_flag_customer_portal_enabled, compliance_enabled, and this flag are all on. See Setting::featureCustomerPortalEpaEnabled(). |
feature_flag_customer_count_limit_enabled | false (seeder) | When on, Customer::customerLimitReached() enforces max_customers on create/import paths. |
compliance_enabled | false (migration) | Master switch for EPA Part 84 features, notifications, and many compliance routes. Off → short-circuits scans and hides compliance experiences. See EPA Part 84. |
Company, calendar, and catalog defaults
| Key | Default | Role |
|---|
company_name, company_address, company_tin, company_phone, company_email, company_logo, company_motto, currency, currency_note | See seeder | Branding on PDFs, emails, portal, and public quotation pages. |
default_tax_rate | 17 | New quotations when no override applies. |
default_estimated_work_order_duration | 2 | Default hours (or unit your install uses) on new work orders and related flows. |
calendar_start_hour, calendar_end_hour | 8, 20 | Schedule grid bounds (web home schedule and mobile schedule). |
item_types, item_units | Arrays | Allowed item type/unit labels in the catalog UI. |
quotation_statuses, quotation_lead_types | Arrays | Quotation status pick list and lead-type pick list. |
phone_country_code, phone_area_code, phone_landline_prefixes | See seeder | Phone normalization helpers. |
Invoices and CaribeTickets
| Key | Default | Role |
|---|
invoice_prefix | TFT | Invoice number prefix. |
invoice_allow_cash, invoice_allow_card, invoice_allow_paypal | true | Passed into CaribeTickets / payment flows. |
invoice_notification_send_via_whatsapp, invoice_notification_send_via_email, invoice_notification_send_via_sms | WhatsApp+email on, SMS off | Notification channels. |
invoice_caribeeats_restaurant_id | UUID in seeder | Restaurant identifier for the billing integration. |
Requires feature_flag_invoices_enabled for invoice admin to load.
Work orders, mobile, and reminders
| Key | Default | Role |
|---|
work_order_tap2pay_expiry_hours_after_waiting_approval_transition | 24 | How long a work order stays in the mobile “follow-up” list after the tech sets waiting_approval, when the invoice is still unpaid. |
work_order_reminder_hours_prior_to_due_at | [72, 48, 24] | Offsets (hours before due_at) for customer reminder scheduling. |
work_order_reminder_hours_internal_no_customer_response_alert | 4 | Internal alert threshold when confirmation is pending. |
work_order_reminder_auto_cancel_on_decline | true | Whether customer rejection on the public work order confirmation can auto-cancel per your rules. |
work_order_reminder_ui_display_provider_name | false | UI toggle for reminder copy. |
upcoming_tasks_auto_create_work_orders | false | Console job: auto-create work orders from upcoming tasks. |
upcoming_tasks_auto_assign_to_last_person | false | When auto-create runs, assign to last technician. |
Customer limits
| Key | Default | Role |
|---|
max_customers | 100 | Inclusive cap when feature_flag_customer_count_limit_enabled is on. |
These are created or assumed by compliance code:
| Key | Default | Role |
|---|
leak_rate_method | annualizing | Default method for leak-rate calculations (LeakRateCalculator, customer import). |
leak_full_charge_threshold_lbs | 15.0 | Threshold (lbs) for full-charge logic in compliance flows. |
compliance_enabled is listed under Feature flags above.
Additional keys (for example compliance_repair_due_lead_days, compliance_cert_gating_mode, compliance_chronic_classification_enabled) may be absent until an admin creates them; services use defaults in code when missing. Treat the Concepts → Compliance pages as the source of truth for behavior.
System / integration (often readonly or sensitive)
| Key | Default | Role |
|---|
max_users | 100 (seeder) | Seeded for operator reference; not read by application code in this repository (no enforcement wired yet). |
allowed_ips | Example list in seeder | Intended for IP allowlisting (verify against your middleware). |
customer_integration_enabled | false, readonly | External customer integration toggle. |
unknown_device_model_id | Set by migration | ID of the placeholder “unknown” device model; used to exclude it from pickers. |
Adding or changing keys
- Prefer
Setting::putIfMissing in seeders or migrations so re-runs do not clobber operator changes.
Setting::put updates or creates; it throws if the row is readonly unless force is used (typically avoided in app code).
- After adding a new key, document it here and wire
settings.edit testers through the Settings screen if it should be operator-tunable.
- Manage Users — who can open System Settings vs Users depends on
users.* and settings.*.
- Permissions —
settings.view / settings.edit.
- Checklists —
feature_flag_checklists_enabled.
- Invoices — invoice flag and billing.
- Mobile API — schedule hours and Tap to Pay list window.