System
Audit trail of every save, delete, import, and workflow update across the backend.
248 results · Page 5 of 5
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","resource_kind":"file","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"curl/8.19.0","is_office_client":0}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Microsoft Office Existence Discovery","is_office_client":1}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"GET","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Mozilla/4.0 (compatible; ms-office; MSOffice 16)","is_office_client":0}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Microsoft Office Excel 2014","is_office_client":1}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Microsoft Office Existence Discovery","is_office_client":1}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"GET","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Mozilla/4.0 (compatible; ms-office; MSOffice 16)","is_office_client":0}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Microsoft Office Existence Discovery","is_office_client":1}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"GET","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Mozilla/4.0 (compatible; ms-office; MSOffice 16)","is_office_client":0}
[]
{"success":true,"status":200}
{"backend_document_id":26,"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","request_uri":"/backend/document_file.php/26/2026-Daily-Customer-Totals.xlsx","path_info":"/26/2026-Daily-Customer-Totals.xlsx","query_string":"","request_method":"HEAD","content_type":"","content_length":0,"http_if":"","lock_token":"","depth":"","timeout":"","user_agent":"Microsoft Office Excel 2014","is_office_client":1}
[]
{"success":true,"status":200}
{"file_name":"2026-Daily-Customer-Totals.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}
[]
{"backend_document_id":"26","document_type":"upload","title":"2026 Daily Totals","slug":"2026-daily-totals","summary_text":"2026 Daily Totals","content_markdown":null,"content_html":null,"file_name":"2026-Daily-Customer-Totals.xlsx","stored_name":"20260509-201236-d80df210.xlsx","mime_type":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","file_size_bytes":"20821","storage_path":"/mnt/drive3/customerdb/backend/documents_storage/20260509-201236-d80df210.xlsx","is_deleted":"0","created_at":"2026-05-09 20:12:36","updated_at":"2026-05-09 20:12:36","editor_content":"report_date total_customers new_customers cash_sales check_sales edwards credit_card_sales tips total_sales total_sales_no_edwards Totals","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"25","document_type":"upload","title":"Codex - Documentation 04-25-2026","slug":"codex-documentation-04-25-2026","summary_text":"2026-04-25-codex-changes.md\r\nIt includes:\r\n\r\neverything created/updated/removed\r\ndatabase schema objects added\r\nDB data changes performed\r\nAPI endpoints/actions touched\r\nprocedures/functions and scripts added/changed\r\ncron/notification behavior\r\nvalidation performed today","content_markdown":null,"content_html":null,"file_name":"2026-04-25-codex-changes.md","stored_name":"20260425-182957-0b251cd7.md","mime_type":"application/octet-stream","file_size_bytes":"6092","storage_path":"/mnt/drive3/customerdb/backend/documents_storage/20260425-182957-0b251cd7.md","is_deleted":"0","created_at":"2026-04-25 18:29:57","updated_at":"2026-04-25 18:29:57","editor_content":"# CustomerDB Changes Log\nDate: 2026-04-25\nAuthor: Codex\n\n## Scope\nThis document summarizes all work completed today in this session, including created/updated/removed files, database schema objects introduced by code, API behaviors touched, and operational procedures/scripts added.\n\n## 1) Files Created\n- `backend/search_data_service.php`\n- `backend/search_data.php`\n- `webui/backend/search_data.php` (compatibility loader)\n- `webui/backend/search_data_service.php` (compatibility loader)\n- `/mnt/drive1/scripts/cleanup_customerdb_branches.sh` (runtime ops script; outside repo tree)\n\n## 2) Files Updated\n- `backend/module_service.php`\n- `backend/index.php`\n- `backend/page_helpers.php`\n- `webui/settings.html`\n- `backend/api/sysadmin.php`\n- `backend/sysadmin.php`\n- `backend/jobs/cron_runtime_service.php`\n- `backend/cron_manager.php`\n- `backend/search_data.php` (restyling updates for spreadsheet-like sections)\n- `backend/search_data_service.php` (provider detection/compat fixes)\n\n## 3) Files Removed\n- None.\n\n## 4) Database Schema Objects Added (by code)\nImplemented in `backend/search_data_service.php` via `app_install_search_data_schema(...)`:\n\n### Tables\n1. `backend_search_dataset`\n2. `backend_search_import_batch`\n3. `backend_search_dataset_field`\n4. `backend_search_data_row`\n\n### Indexes / Constraints (high level)\n- Unique dataset key by provider (`provider_name`, `dataset_key`)\n- Import batch indexes by dataset/status\n- Unique field key per dataset\n- Unique row identity hash per dataset\n- Analytical indexes on date/clicks/impressions\n\n## 5) Database Data Changes Performed Today\n- A cron job row was inserted in `cron_job`:\n - Label: `Cleanup Repo Branch Folders`\n - Schedule: `20 3 * * *`\n - Command: `bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n - Enabled: `1`\n- Search data smoke import was executed once in this environment for validation, which inserted dataset/import/data rows in the new search tables.\n\n## 6) New/Updated Backend Module\n### Module: Search Data Imports (`Bing / Google CSV + AI`)\n- New page: `backend/search_data.php`\n- New service: `backend/search_data_service.php`\n- Registered in backend module overview and dashboard card.\n- Added to module jump links and settings module visibility list.\n\n### Functional capabilities added\n- Adaptive CSV import for Google/Bing (and unknown/future providers).\n- Header evolution tracking (new columns are cataloged without code changes).\n- Batch import auditing (rows seen/inserted/updated/skipped).\n- Query context construction from imported tables.\n- Ollama-based AI question answering over imported data using existing settings:\n - `ollama_url`\n - `ollama_default_model`\n\n## 7) API Surface and Call Behavior Changes\n## Updated endpoint\n- `backend/api/sysadmin.php`\n\n### Updated actions\n1. `action=backups_list`\n - Added new `dir` key support:\n - `repo-branches` => `/mnt/drive3/customerdb_repo/branches`\n2. `action=backups_delete`\n - Previously file-only delete.\n - Now supports both file and folder delete.\n - Added safe recursive folder deletion with path-boundary checks.\n - Response now includes:\n - `deleted_type` (`file` or `folder`)\n\n### Existing actions used by UI (not newly created today, but part of touched API flow)\n- `logs_list`, `logs_read`, `logs_clear`\n- `scripts_list`, `scripts_read`, `scripts_save`, `scripts_run`, `scripts_notify_save`\n- `backups_list`, `backups_delete`\n\n## 8) Sysadmin UI Changes\nUpdated `backend/sysadmin.php`:\n- Backups tab now includes:\n - `customerdb_repo/branches/` source (`data-backupdir=\"repo-branches\"`)\n- Directory rows now include a `Delete Folder` button.\n- Delete handler updated to support file vs folder confirmation text.\n\n## 9) Cron / Ops Procedure Changes\n## Updated cron runtime service\nUpdated `backend/jobs/cron_runtime_service.php`:\n- Added constants:\n - `APP_BRANCH_CLEANUP_SCRIPT_PATH`\n - `APP_BRANCH_CLEANUP_TARGET_PATH`\n- Added helper:\n - `app_cron_ensure_branch_cleanup_script()`\n- `app_cron_migrate_command_paths(...)` now:\n - Ensures command normalization for `Cleanup Repo Branch Folders`\n - Ensures cleanup script exists on disk\n\n## Updated cron manager seeding\nUpdated `backend/cron_manager.php`:\n- Added seeded/ensured job:\n - `Cleanup Repo Branch Folders`\n - `20 3 * * *`\n - `bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n\n## Script created\n`/mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n- Deletes first-level folders older than 5 days under:\n - `/mnt/drive3/customerdb_repo/branches`\n- Emits clear stdout status suitable for cron logs/notifications.\n\n## Notifications\nNo new notification channels were added.\nThe new cron job uses existing cron execution + notification flow:\n- Telegram (depending on settings)\n- Email (depending on settings)\n\n## 10) Jump-To-Module Improvements\nUpdated `backend/page_helpers.php`:\n- Jump dropdown list now sorted A-Z by label.\n- List expanded to include missing backend destinations.\n- Corrected invalid links for queue pages to existing routes:\n - `/upcoming.html`\n - `/workload.html`\n\n## 11) Styling/UI Updates Requested\nUpdated `backend/search_data.php`:\n- For sections:\n - `CSV Files`\n - `Dataset Totals`\n - `Recent Imports`\n- Removed rounded corners and applied spreadsheet-like flat table style (Excel-like grid presentation).\n\n## 12) Compatibility / Robustness Fixes Applied\nIn `backend/search_data_service.php`:\n- Added CSV parsing compatibility for `fgetcsv` escape parameter.\n- Replaced `mb_substr` with `substr` in one path to avoid missing extension errors in this environment.\n- Improved provider detection rules for Google export naming patterns:\n - `KeywordReport`\n - `PageTrafficReport`\n - `SearchPerformanceOverview`\n\n## 13) Validation Performed\n- PHP syntax checks (`php -l`) passed for all modified PHP files.\n- Search data smoke import executed successfully in this environment.\n- Verified script path existence after direct creation:\n - `/mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n\n## 14) Notes\n- Existing unrelated local repo changes were not reverted.\n- No destructive git operations were used.\n","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"25","document_type":"upload","title":"Subscription List - Auto Pay","slug":"subscription-list-auto-pay","summary_text":"Subscription List - Auto Pay","content_markdown":"Subscription List\r\nSunPass as used\r\nAmazon Prime 139 Nov 12 Yearly\r\nAsurion 16.99 month through Amazon\r\nAdobe Express 9.99 monthly\r\nChatGPT 20.00 Monthly\r\nSpectrum 79.99 Monthly\r\nMetroPCS 106.00 Monthly\r\nHulu 1.12 Monthly\r\nRoboForm Jan 31, 2030 199.00 Change license\r\nTunnelToTower 11.99 Monthly\r\nPasco Trash Last 01/21/2025 next 3 months 64.50\r\nGoogle Ella ICloud .99 Monthly\r\nGoogle Peter ICloud 2.99 Monthly\r\nZmodo - 49.99 Year 02/27/2025\r\nPeachock Oct 87.00\r\nMax Aug 169.99\r\nClaude $20\r\nCursor $20","content_html":"<p>Subscription List</p>\n<p>SunPass as used</p>\n<p>Amazon Prime 139 Nov 12 Yearly</p>\n<p>Asurion 16.99 month through Amazon</p>\n<p>Adobe Express 9.99 monthly</p>\n<p>ChatGPT 20.00 Monthly</p>\n<p>Spectrum 79.99 Monthly</p>\n<p>MetroPCS 106.00 Monthly</p>\n<p>Hulu 1.12 Monthly</p>\n<p>RoboForm Jan 31, 2030 199.00 Change license</p>\n<p>TunnelToTower 11.99 Monthly</p>\n<p>Pasco Trash Last 01/21/2025 next 3 months 64.50</p>\n<p>Google Ella ICloud .99 Monthly</p>\n<p>Google Peter ICloud 2.99 Monthly</p>\n<p>Zmodo - 49.99 Year 02/27/2025</p>\n<p>Peachock Oct 87.00</p>\n<p>Max Aug 169.99</p>\n<p>Claude $20</p>\n<p>Cursor $20</p>","file_name":"2026-04-25-codex-changes.md","stored_name":"20260425-182957-0b251cd7.md","mime_type":"application/octet-stream","file_size_bytes":"6092","storage_path":"/mnt/drive3/customerdb/backend/documents_storage/20260425-182957-0b251cd7.md","is_deleted":"0","created_at":"2026-04-25 18:29:57","updated_at":"2026-05-09 16:50:44","editor_content":"Subscription List\r\nSunPass as used\r\nAmazon Prime 139 Nov 12 Yearly\r\nAsurion 16.99 month through Amazon\r\nAdobe Express 9.99 monthly\r\nChatGPT 20.00 Monthly\r\nSpectrum 79.99 Monthly\r\nMetroPCS 106.00 Monthly\r\nHulu 1.12 Monthly\r\nRoboForm Jan 31, 2030 199.00 Change license\r\nTunnelToTower 11.99 Monthly\r\nPasco Trash Last 01/21/2025 next 3 months 64.50\r\nGoogle Ella ICloud .99 Monthly\r\nGoogle Peter ICloud 2.99 Monthly\r\nZmodo - 49.99 Year 02/27/2025\r\nPeachock Oct 87.00\r\nMax Aug 169.99\r\nClaude $20\r\nCursor $20","is_text_editable":1,"can_edit_inline":1}
{"file_name":"2026-04-25-codex-changes.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"25","document_type":"upload","title":"Codex - Documentation 04-25-2026","slug":"codex-documentation-04-25-2026","summary_text":"2026-04-25-codex-changes.md\r\nIt includes:\r\n\r\neverything created/updated/removed\r\ndatabase schema objects added\r\nDB data changes performed\r\nAPI endpoints/actions touched\r\nprocedures/functions and scripts added/changed\r\ncron/notification behavior\r\nvalidation performed today","content_markdown":null,"content_html":null,"file_name":"2026-04-25-codex-changes.md","stored_name":"20260425-182957-0b251cd7.md","mime_type":"application/octet-stream","file_size_bytes":"6092","storage_path":"/mnt/drive3/customerdb/backend/documents_storage/20260425-182957-0b251cd7.md","is_deleted":"0","created_at":"2026-04-25 18:29:57","updated_at":"2026-04-25 18:29:57","editor_content":"# CustomerDB Changes Log\nDate: 2026-04-25\nAuthor: Codex\n\n## Scope\nThis document summarizes all work completed today in this session, including created/updated/removed files, database schema objects introduced by code, API behaviors touched, and operational procedures/scripts added.\n\n## 1) Files Created\n- `backend/search_data_service.php`\n- `backend/search_data.php`\n- `webui/backend/search_data.php` (compatibility loader)\n- `webui/backend/search_data_service.php` (compatibility loader)\n- `/mnt/drive1/scripts/cleanup_customerdb_branches.sh` (runtime ops script; outside repo tree)\n\n## 2) Files Updated\n- `backend/module_service.php`\n- `backend/index.php`\n- `backend/page_helpers.php`\n- `webui/settings.html`\n- `backend/api/sysadmin.php`\n- `backend/sysadmin.php`\n- `backend/jobs/cron_runtime_service.php`\n- `backend/cron_manager.php`\n- `backend/search_data.php` (restyling updates for spreadsheet-like sections)\n- `backend/search_data_service.php` (provider detection/compat fixes)\n\n## 3) Files Removed\n- None.\n\n## 4) Database Schema Objects Added (by code)\nImplemented in `backend/search_data_service.php` via `app_install_search_data_schema(...)`:\n\n### Tables\n1. `backend_search_dataset`\n2. `backend_search_import_batch`\n3. `backend_search_dataset_field`\n4. `backend_search_data_row`\n\n### Indexes / Constraints (high level)\n- Unique dataset key by provider (`provider_name`, `dataset_key`)\n- Import batch indexes by dataset/status\n- Unique field key per dataset\n- Unique row identity hash per dataset\n- Analytical indexes on date/clicks/impressions\n\n## 5) Database Data Changes Performed Today\n- A cron job row was inserted in `cron_job`:\n - Label: `Cleanup Repo Branch Folders`\n - Schedule: `20 3 * * *`\n - Command: `bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n - Enabled: `1`\n- Search data smoke import was executed once in this environment for validation, which inserted dataset/import/data rows in the new search tables.\n\n## 6) New/Updated Backend Module\n### Module: Search Data Imports (`Bing / Google CSV + AI`)\n- New page: `backend/search_data.php`\n- New service: `backend/search_data_service.php`\n- Registered in backend module overview and dashboard card.\n- Added to module jump links and settings module visibility list.\n\n### Functional capabilities added\n- Adaptive CSV import for Google/Bing (and unknown/future providers).\n- Header evolution tracking (new columns are cataloged without code changes).\n- Batch import auditing (rows seen/inserted/updated/skipped).\n- Query context construction from imported tables.\n- Ollama-based AI question answering over imported data using existing settings:\n - `ollama_url`\n - `ollama_default_model`\n\n## 7) API Surface and Call Behavior Changes\n## Updated endpoint\n- `backend/api/sysadmin.php`\n\n### Updated actions\n1. `action=backups_list`\n - Added new `dir` key support:\n - `repo-branches` => `/mnt/drive3/customerdb_repo/branches`\n2. `action=backups_delete`\n - Previously file-only delete.\n - Now supports both file and folder delete.\n - Added safe recursive folder deletion with path-boundary checks.\n - Response now includes:\n - `deleted_type` (`file` or `folder`)\n\n### Existing actions used by UI (not newly created today, but part of touched API flow)\n- `logs_list`, `logs_read`, `logs_clear`\n- `scripts_list`, `scripts_read`, `scripts_save`, `scripts_run`, `scripts_notify_save`\n- `backups_list`, `backups_delete`\n\n## 8) Sysadmin UI Changes\nUpdated `backend/sysadmin.php`:\n- Backups tab now includes:\n - `customerdb_repo/branches/` source (`data-backupdir=\"repo-branches\"`)\n- Directory rows now include a `Delete Folder` button.\n- Delete handler updated to support file vs folder confirmation text.\n\n## 9) Cron / Ops Procedure Changes\n## Updated cron runtime service\nUpdated `backend/jobs/cron_runtime_service.php`:\n- Added constants:\n - `APP_BRANCH_CLEANUP_SCRIPT_PATH`\n - `APP_BRANCH_CLEANUP_TARGET_PATH`\n- Added helper:\n - `app_cron_ensure_branch_cleanup_script()`\n- `app_cron_migrate_command_paths(...)` now:\n - Ensures command normalization for `Cleanup Repo Branch Folders`\n - Ensures cleanup script exists on disk\n\n## Updated cron manager seeding\nUpdated `backend/cron_manager.php`:\n- Added seeded/ensured job:\n - `Cleanup Repo Branch Folders`\n - `20 3 * * *`\n - `bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n\n## Script created\n`/mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n- Deletes first-level folders older than 5 days under:\n - `/mnt/drive3/customerdb_repo/branches`\n- Emits clear stdout status suitable for cron logs/notifications.\n\n## Notifications\nNo new notification channels were added.\nThe new cron job uses existing cron execution + notification flow:\n- Telegram (depending on settings)\n- Email (depending on settings)\n\n## 10) Jump-To-Module Improvements\nUpdated `backend/page_helpers.php`:\n- Jump dropdown list now sorted A-Z by label.\n- List expanded to include missing backend destinations.\n- Corrected invalid links for queue pages to existing routes:\n - `/upcoming.html`\n - `/workload.html`\n\n## 11) Styling/UI Updates Requested\nUpdated `backend/search_data.php`:\n- For sections:\n - `CSV Files`\n - `Dataset Totals`\n - `Recent Imports`\n- Removed rounded corners and applied spreadsheet-like flat table style (Excel-like grid presentation).\n\n## 12) Compatibility / Robustness Fixes Applied\nIn `backend/search_data_service.php`:\n- Added CSV parsing compatibility for `fgetcsv` escape parameter.\n- Replaced `mb_substr` with `substr` in one path to avoid missing extension errors in this environment.\n- Improved provider detection rules for Google export naming patterns:\n - `KeywordReport`\n - `PageTrafficReport`\n - `SearchPerformanceOverview`\n\n## 13) Validation Performed\n- PHP syntax checks (`php -l`) passed for all modified PHP files.\n- Search data smoke import executed successfully in this environment.\n- Verified script path existence after direct creation:\n - `/mnt/drive1/scripts/cleanup_customerdb_branches.sh`\n\n## 14) Notes\n- Existing unrelated local repo changes were not reverted.\n- No destructive git operations were used.\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"CHANGE_LOG_2026-04-25.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"24","document_type":"upload","title":"Cursor documentation 04-25-2026","slug":"cursor-documentation-04-25-2026","summary_text":"t includes:\r\n\r\nSummary table — created/updated/removed, DDL vs DML, API impact.\r\nScoped file diff — full detail for uncommitted changes in:\r\nbackend/jobs/cron_runtime_service.php (backup bash migration, branch cleanup constants, app_cron_ensure_branch_cleanup_script(), related UPDATEs)\r\nbackend/cron_manager.php (Database Backup seed + Cleanup Repo Branch Folders seed/ensure)\r\nDatabase — cron_job / cron_job_run column list (from CREATE TABLE IF NOT EXISTS), all runtime UPDATE/INSERT behavior, optional manual SQL for backup.\r\nProcedures — new/changed PHP functions (no MySQL stored procedures).\r\nAPI — cron_manager POST actions and that no HTTP API files changed in this diff; run_cron_job.php is CLI.\r\nWider repo — pointer to git status for other changed/untracked files (not enumerated line-by-line).\r\nOps notes — bash vs execute bit, MAILTO / Hostinger from the earlier support thread.\r\nNote: There were no commits on 2026-04-25, so the document is based on the current working tree (including the branch-cleanup work in the same two files as the backup bash fix). If you want the doc to only cover the backup line and exclude branch cleanup, say so and it can be trimmed.","content_markdown":null,"content_html":null,"file_name":"CHANGE_LOG_2026-04-25.md","stored_name":"20260425-182448-2570d279.md","mime_type":"application/octet-stream","file_size_bytes":"7947","storage_path":"/mnt/drive3/customerdb/backend/documents_storage/20260425-182448-2570d279.md","is_deleted":"0","created_at":"2026-04-25 18:24:48","updated_at":"2026-04-25 18:24:48","editor_content":"# Change log — 2026-04-25\n\nThis document records **uncommitted work in the local workspace** as of generation time. There were **no git commits** on 2026-04-25; content is based on `git diff` and `git status` for this repository.\n\n---\n\n## 1. Summary\n\n| Category | What changed |\n|----------|---------------|\n| **Files created** | This document: `docs/CHANGE_LOG_2026-04-25.md` |\n| **Files removed** | None in the scoped diffs below |\n| **Database DDL** | **None** — no new `CREATE TABLE` / `ALTER` in these changes. Existing `cron_job` / `cron_job_run` definitions unchanged (see §4). |\n| **Database DML (runtime)** | `UPDATE` statements inside `app_cron_migrate_command_paths()` to normalize `cron_job.command` values (see §4). |\n| **HTTP API routes** | **None** added or changed by the `backend/jobs/cron_runtime_service.php` and `backend/cron_manager.php` diffs. |\n| **CLI / entry points** | `php backend/jobs/run_cron_job.php <cron_job_id>` (unchanged contract). |\n\n---\n\n## 2. Files updated (detailed) — cron / backup / branch cleanup\n\nThe following are the **only files** with local modifications captured in a full-file diff at documentation time: `backend/jobs/cron_runtime_service.php` and `backend/cron_manager.php`.\n\n### 2.1 `backend/jobs/cron_runtime_service.php`\n\n| Change | Description |\n|--------|-------------|\n| **New constants** | `APP_BRANCH_CLEANUP_SCRIPT_PATH` → `/mnt/drive1/scripts/cleanup_customerdb_branches.sh` |\n| | `APP_BRANCH_CLEANUP_TARGET_PATH` → `/mnt/drive3/customerdb_repo/branches` |\n| **`app_cron_migrate_command_paths()`** | After the existing `REPLACE` of `drive1` → `drive3` customerdb paths, **new `UPDATE`**: if `command` is exactly `/mnt/drive3/customerdb/backup.sh`, set it to `bash /mnt/drive3/customerdb/backup.sh` (avoids `Permission denied` when the script is not executable or the filesystem is `noexec`). |\n| | **New `UPDATE`**: rows with `label = 'Cleanup Repo Branch Folders'` get `command = 'bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh'`. |\n| | Calls new helper **`app_cron_ensure_branch_cleanup_script()`** at end of migration. |\n| **New function** | **`app_cron_ensure_branch_cleanup_script()`** — if `/mnt/drive1/scripts` exists and is writable, writes an embedded **bash** script to `cleanup_customerdb_branches.sh` (deletes first-level subdirectories under the target older than 5 days), `chmod` `0755` if possible. If script content differs, file is updated. |\n\n**Notable behavior:** `app_cron_migrate_command_paths()` is invoked from `run_cron_job.php`, `cron_manager.php`, and any code path that loads this service before running jobs, so the `UPDATE` statements run idempotently when those paths execute.\n\n**Compatibility loader:** `webui/backend/jobs/cron_runtime_service.php` only `require`s the canonical `backend` file; no separate logic.\n\n### 2.2 `backend/cron_manager.php`\n\n| Change | Description |\n|--------|-------------|\n| **Seed job (empty `cron_job` table only)** | **Database Backup** command changed from `/mnt/drive3/customerdb/backup.sh` to **`bash /mnt/drive3/customerdb/backup.sh`**. |\n| | New seed job: **Cleanup Repo Branch Folders** — schedule `20 3 * * *`, command **`bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh`**, `sort_order` 55. |\n| **`$ensureJobs` (insert if missing by label)** | Same **Cleanup Repo Branch Folders** block added so the job appears when the table already had data (seeds not run). |\n\n**Web entry:** `GET/POST` to `/backend/cron_manager.php` — manages rows in `cron_job`, can run jobs manually, regenerates export file for system crontab install (unchained URL; still the same page).\n\n---\n\n## 3. New or changed PHP “procedures” (functions)\n\nNo database **stored procedures** are introduced; the project uses **PHP functions** in `cron_runtime_service.php`.\n\n| Name | Action |\n|------|--------|\n| `app_cron_migrate_command_paths` | Extended with `backup.sh` `bash` migration, branch-folder command `UPDATE`, and `app_cron_ensure_branch_cleanup_script()` call. |\n| `app_cron_ensure_branch_cleanup_script` | **New** — materializes/updates the branch cleanup shell script and permissions when possible. |\n\nAll other existing functions in this file (`app_cron_execute_job`, `app_cron_send_notifications`, etc.) are **unchanged in signature**; execution still uses `exec($cmd . ' 2>&1', ...)` in `app_cron_execute_job`.\n\n---\n\n## 4. Database schema and data touchpoints\n\n### 4.1 Tables (no DDL change in this work)\n\nThese are **ensured** by `cron_manager.php` on load (`CREATE TABLE IF NOT EXISTS`):\n\n- **`cron_job`**: `cron_job_id`, `label`, `description`, `schedule`, `command`, `enabled`, `last_run_at`, `last_run_ok`, `last_run_out`, `sort_order`, `created_at`\n- **`cron_job_run`**: `cron_job_run_id`, `cron_job_id`, `job_label`, `run_source`, `started_at`, `finished_at`, `exit_code`, `ran_ok`, `output_text`\n\nNo new columns or indexes in the recorded diff.\n\n### 4.2 Data migrations (`UPDATE` only / idempotent)\n\nExecuted inside **`app_cron_migrate_command_paths($conn)`**:\n\n1. `UPDATE cron_job SET command = REPLACE(command, '/mnt/drive1/customerdb', '/mnt/drive3/customerdb')` where the substring appears.\n2. `UPDATE cron_job SET command = 'bash /mnt/drive3/customerdb/backup.sh' WHERE command = '/mnt/drive3/customerdb/backup.sh'`\n3. Daily Cleanup: existing logic rewriting old shell/PHP daily cleanup commands to `php8.3 .../daily_cleanup.php` (unchanged in intent).\n4. `UPDATE cron_job SET command = 'bash /mnt/drive1/scripts/cleanup_customerdb_branches.sh' WHERE label = 'Cleanup Repo Branch Folders'`\n5. Branch cleanup: `app_cron_ensure_branch_cleanup_script()` (filesystem, not SQL).\n\n**Manual SQL** (if migration has not run yet) for backup only:\n\n```sql\nUPDATE cron_job\nSET command = 'bash /mnt/drive3/customerdb/backup.sh'\nWHERE command = '/mnt/drive3/customerdb/backup.sh';\n```\n\n### 4.3 Inserts (new default job)\n\nIf **`cron_job` was empty** on first run after deploy, the seed includes the new **Cleanup Repo Branch Folders** row. If the table was **not** empty, **`$ensureJobs`** may insert that job when no row with the same label exists.\n\n---\n\n## 5. API surface\n\n| Type | Endpoints / scripts | Notes |\n|------|----------------------|--------|\n| **Changed in this diff** | None | |\n| **Related backend page** | `GET/POST /backend/cron_manager.php` | Form actions: `save_job`, `toggle`, `delete_job`, `run_now` (unchained pattern). |\n| **CLI** | `php8.3 /mnt/drive3/customerdb/backend/jobs/run_cron_job.php <id> [--verbose]` | Invoked by system cron per generated `/etc/cron.d/customerdb` lines. |\n\n**Other HTTP APIs** in the repository (e.g. under `webui/api/`, `frontend/api/`) are **not** modified by the two files above; the working tree still contains many other modified and untracked files—see §6.\n\n---\n\n## 6. Other modified / new paths in the working tree (not individually documented here)\n\nA snapshot of the repository showed **numerous** other modified and untracked files (e.g. `backend/api/*.php`, `frontend/api/*.php`, `webui/api/*.php`, `backup.sh`, logs, etc.). Those are **outside** the scoped `cron_runtime_service.php` / `cron_manager.php` diffs in §2. For a full list, run from the repo root:\n\n```text\ngit status\ngit diff --name-only\n```\n\n---\n\n## 7. Related operational notes (from 2026-04-25 support context)\n\n- **Shell `Permission denied` on `*.sh`:** prefer `bash /path/to/script.sh` (as implemented for `backup.sh`) and/or `chmod +x` on the script; check for **`noexec`** mounts.\n- **Email bounces to `*smtpout*.hostinger.io`:** that hostname is for **sending** mail, not a mailbox. Set **`MAILTO`** in crontab and app **notification** addresses to a real user address. Application logic in `app_cron_notification_email()` already discards some invalid Hostinger `smtpout` targets for cron emails.\n\n---\n\n## 8. Document history\n\n| Date | Note |\n|------|------|\n| 2026-04-25 | Initial version: cron file diffs, schema touchpoints, no API change in scope. |\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"ESSENTIAL_FILES_ANALYSIS.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"23","document_type":"upload","title":"All Essential Files for Customer DB","slug":"all-essential-files-for-customer-db","summary_text":"All Essential Files for Customer DB","content_markdown":null,"content_html":null,"file_name":"ESSENTIAL_FILES_ANALYSIS.md","stored_name":"20260420-183627-ab045aa6.md","mime_type":"application/octet-stream","file_size_bytes":"9785","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260420-183627-ab045aa6.md","is_deleted":"0","created_at":"2026-04-20 14:36:27","updated_at":"2026-04-20 14:36:27","editor_content":"# Essential Files Analysis - C:\\code\\customerdb\n\n**Generated:** April 17, 2026\n\n---\n\n## Summary\n\nThis document identifies which files are essential to keep vs which can be safely removed to reduce disk usage and simplify the codebase.\n\n**Estimated Space Savings:** ~200+ MB\n\n---\n\n## KEEP - Essential Files\n\n### Core Application\n\n```\nC:\\code\\customerdb\\backend\\ # Main PHP backend (98 files)\nC:\\code\\customerdb\\frontend\\ # Front Desk app (15 HTML pages)\nC:\\code\\customerdb\\frontend\\api\\ # Frontend API endpoints\nC:\\code\\customerdb\\scripts\\ # Import/sync scripts\nC:\\code\\customerdb\\migrations\\ # SQL migrations\nC:\\code\\customerdb\\csv\\ # Active CSV data\nC:\\code\\customerdb\\docs\\ # Documentation\nC:\\code\\customerdb\\backups\\ # Database backups (prune old)\nC:\\code\\customerdb\\mariadb_core_schema.sql\nC:\\code\\customerdb\\backup.sh\n```\n\n### Backend - Essential Files (98 PHP files)\n\n| File | Purpose |\n|------|---------|\n| `bootstrap.php` | Core bootstrap |\n| `customer_service.php` | Customer management |\n| `customer.php` | Customer API |\n| `visit_service.php` | Visit records |\n| `visit.php` | Visit API |\n| `customer_totals_service.php` | Daily totals |\n| `report_service.php` | Reporting (80KB) |\n| `schedule.php` | Appointment scheduling |\n| `setmore_service.php` | Setmore integration |\n| `email_service.php` | Email handling |\n| `bank_service.php` | Banking |\n| `gold_silver_service.php` | Precious metals |\n| `duke_service.php` | Energy tracking |\n| `payroll_service.php` | Payroll |\n| `stocks_crypto_service.php` | Investments |\n| `medication_service.php` | Medication tracking |\n| `backup_database_service.php` | Database backup |\n| `cron_manager.php` | Cron management |\n| `nightly_reports_service.php` | Nightly reports |\n| `morning_jobs_service.php` | Morning batch jobs |\n| `square_terminal_service.php` | Square POS |\n| `live_table_manager_service.php` | Live data tables |\n| `notifications_service.php` | Notifications |\n\n**API Endpoints:** 58 files in `/backend/api/` - all active REST endpoints\n\n### Frontend - HTML Pages (15 files)\n\n| File | Purpose |\n|------|---------|\n| `visit.html` | Visit management (254KB) |\n| `customer.html` | Customer records |\n| `index.html` | Main dashboard |\n| `reports.html` | Reports viewer |\n| `schedule.html` | Schedule view |\n| `work_order.html` | Work orders |\n| `workload.html` | Workload view |\n| `receipt.html` | Receipt printing |\n| `login.html` | Authentication |\n| `upcoming.html` | Upcoming appointments |\n| `square_customer.html` | Square integration |\n| `item_types.html` | Item types |\n| `todo.html` | Todo lists |\n| `customer_display.html` | Customer display |\n\n### Scripts - Essential (7 files)\n\n| Script | Purpose |\n|--------|---------|\n| `import_csv_v2.py` | CSV import |\n| `import_access_v2.py` | Access DB import |\n| `access_to_mariadb_parser.py` | Access parser |\n| `import_legacy_totals_from_excel.ps1` | Excel import |\n| `sync_backend_mirror.ps1` | Mirror sync |\n| `check_backend_mirror.ps1` | Mirror check |\n| `signature_blob_loader.py` | Signature loader |\n\n### Migrations - Essential (3 files)\n\n| File | Purpose |\n|------|---------|\n| `20260407_add_conversion_job.sql` | Conversion job table |\n| `20260411_order_items_item_type_name_no_fk.sql` | Item type changes |\n| `20260416_blog_prompt_templates.sql` | Blog prompts |\n\n### CSV Data - Essential\n\n| File | Purpose |\n|------|---------|\n| `CustomerTable.csv` | Customer records |\n| `CustomerVisits.csv` | Visit records |\n| `CustomerTotals.csv` | Daily totals |\n| `AlterationItems.csv` | Alteration items |\n\n---\n\n## REMOVE - Redundant/Unused Files\n\n### Priority 1 - Safe to Delete (Complete Directories)\n\n| Directory | Size | Action |\n|-----------|------|--------|\n| `notused\\` | ~85 MB | DELETE ENTIRE |\n| `venv\\` | ~100 MB | DELETE ENTIRE |\n| `OldFiles\\` | ~1 MB | DELETE ENTIRE |\n| `.vs\\` | ~1 MB | DELETE ENTIRE |\n| `.vscode\\` | small | DELETE |\n\n#### notused/ Contents (DELETE ALL)\n```\nnotused\\access_to_mariadb_parser (1).py # duplicates\nnotused\\access_to_mariadb_parser (2).py # duplicates\nnotused\\customerdb_v2_staging_schema.sql # duplicate\nnotused\\mariadb_core_schema.sql # duplicate\nnotused\\mariadb-12.2.2-winx64.msi # MariaDB installer (89MB!)\nnotused\\Backend\\ # old PHP code\nnotused\\backups\\ # old backups\nnotused\\csv\\ # old CSVs\nnotused\\docs\\ # old docs\nnotused\\schema\\ # old schemas\nnotused\\scripts\\ # old scripts\nnotused\\webui\\ # old webui\n```\n\n### Priority 2 - Duplicate WebUI (Check First)\n\n| Directory | Purpose |\n|-----------|---------|\n| `webui\\` | ~15 MB - mirrors backend/frontend |\n\nThis directory appears to mirror the main `backend/` and `frontend/` directories. If `webui` is NOT served as a separate application, DELETE ENTIRE:\n\n```\nwebui\\backend\\ # 89 files - duplicate of ../backend/\nwebui\\frontend\\ # mirrors ../frontend/\nwebui\\api\\ # similar to ../frontend/api/\n```\n\n### Priority 3 - Temporary Files\n\nDelete all files matching patterns:\n```\ntmp_*.php (~30 files) # temp PHP scripts\ntmp_*.sql (~30 files) # temp SQL queries\ntmp_*.sh (1 file) # temp shell scripts\ntmp_*.csv (1 file) # temp CSV files\n```\n\nExamples:\n- `tmp_check_129411.sql`\n- `tmp_nightly_preview.php`\n- `tmp_compare_sheet_customer_totals.sql`\n- `tmp_mirror_drive.sh`\n- `tmp_customertotalsaccess.csv`\n\n### Priority 4 - Check Before Delete\n\n| Directory | Purpose | Action |\n|-----------|---------|--------|\n| `blog\\` | Blog module | Verify if used, else DELETE |\n| `Topaz\\` | Signature pad drivers | Verify if needed, else DELETE |\n| `wordpress-plugin\\` | WP integration | Verify if used, else DELETE |\n| `schedule\\` | Laravel scheduling app | Verify if integrated, else DELETE |\n| `csv\\back\\` | 8 backup CSVs | Likely duplicate, verify then DELETE |\n\n### Priority 5 - Backup Files\n\n| File Pattern | Action |\n|-------------|--------|\n| `*.bak_*` | DELETE |\n| `*.bak` | DELETE |\n| `*.backup` | DELETE |\n| `*~` | DELETE |\n\n### Priority 6 - Root Level Large Unused Files\n\n| File | Size | Action |\n|------|------|--------|\n| `Items To Test or Comlete.odt` | 26KB | Move to docs or DELETE |\n\n---\n\n## Directory Structure After Cleanup\n\n```\ncustomerdb/\n├── backend/ # KEEP - Main PHP backend\n│ ├── api/ # KEEP - API endpoints\n│ ├── jobs/ # KEEP - Cron jobs\n│ └── bin/ # KEEP - Binaries\n├── frontend/ # KEEP - Front Desk app\n│ ├── api/ # KEEP - API calls\n│ └── reports/ # KEEP - PDF reports\n├── scripts/ # KEEP - Import scripts\n├── migrations/ # KEEP - DB migrations\n├── csv/ # KEEP - Active data\n│ └── back/ # DELETE - backup copies\n├── docs/ # KEEP - Documentation\n├── backups/ # KEEP - Keep latest, prune old\n├── schema/ # KEEP or DELETE (empty)\n├── mariadb_core_schema.sql # KEEP - Core schema\n├── migrate_add_visit_type.sql # KEEP - Migration\n├── backup.sh # KEEP - Backup script\n├── .gitignore # KEEP\n│\n├── notused/ # DELETE ENTIRE (~85 MB)\n├── OldFiles/ # DELETE ENTIRE (~1 MB)\n├── .vs/ # DELETE (~1 MB)\n├── .vscode/ # DELETE\n├── venv/ # DELETE (~100 MB)\n├── webui/ # DELETE OR VERIFY FIRST\n├── blog/ # VERIFY - then delete\n├── Topaz/ # VERIFY - then delete\n├── wordpress-plugin/ # VERIFY - then delete\n├── schedule/ # VERIFY - then delete\n└── tmp_*.* # DELETE ALL (~2 MB)\n```\n\n---\n\n## Space Savings Summary\n\n| Category | Savings |\n|----------|----------|\n| `notused/` directory | ~85 MB |\n| `venv/` directory | ~100 MB |\n| `tmp_*` files | ~2 MB |\n| `OldFiles/` | ~1 MB |\n| `.vs`, `.vscode` | ~1 MB |\n| `webui/` (if duplicate) | ~15 MB |\n| `csv/back/` | ~2 MB |\n| **TOTAL** | **~200+ MB** |\n\n---\n\n## Commands to Remove Files\n\n### PowerShell - Delete Priority 1 (Safe to Delete)\n\n```powershell\n# Delete notused directory\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\notused\"\n\n# Delete OldFiles directory\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\OldFiles\"\n\n# Delete .vs directory\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\.vs\"\n\n# Delete .vscode directory\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\.vscode\"\n\n# Delete venv directory\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\venv\"\n```\n\n### PowerShell - Delete Temporary Files\n\n```powershell\n# Delete all tmp_* files in root\nGet-ChildItem \"C:\\code\\customerdb\" -Filter \"tmp_*\" | Remove-Item -Force\n\n# Delete backup files\nGet-ChildItem \"C:\\code\\customerdb\" -Filter \"*.bak*\" | Remove-Item -Force\n```\n\n### PowerShell - Delete Duplicate WebUI (After Verification)\n\n```powershell\n# Only run if webui is NOT served separately\nRemove-Item -Recurse -Force \"C:\\code\\customerdb\\webui\"\n```\n\n---\n\n## Verification Checklist Before Delete\n\n- [ ] **webui/** - Is port 8894 served from this directory? If yes, KEEP. If served from `backend/`, DELETE.\n- [ ] **blog/** - Is blog feature actively used in the application?\n- [ ] **Topaz/** - Is the Topaz signature pad still in use?\n- [ ] **wordpress-plugin/** - Is WordPress integration still active?\n- [ ] **schedule/** - Is the Laravel scheduling app still integrated?\n\n---\n\n*End of Analysis*","is_text_editable":1,"can_edit_inline":1}
{"file_name":"2026-04-17-implementation-report.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"22","document_type":"upload","title":"Codex Updates 04-17-2026","slug":"codex-updates-04-17-2026","summary_text":"Codex Updates 04-17-2026","content_markdown":null,"content_html":null,"file_name":"2026-04-17-implementation-report.md","stored_name":"20260417-191931-4b36a7df.md","mime_type":"application/octet-stream","file_size_bytes":"8180","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-191931-4b36a7df.md","is_deleted":"0","created_at":"2026-04-17 15:19:31","updated_at":"2026-04-17 15:19:31","editor_content":"# Implementation Report - April 17, 2026\n\n## Scope\nThis document summarizes all completed work from this support/maintenance session, including:\n- Issues found\n- Root causes\n- Fixes implemented\n- Files changed\n- Live deployment actions\n- Verification results\n- Remaining follow-up items\n\nDate: 2026-04-17\n\n---\n\n## 1) Cron Email Failures (421 Downstream server error)\n\n### Reported issue\nCron jobs were failing email delivery from:\n- `/mnt/drive1/customerdb/backend/jobs/run_cron_job.php 7`\n\nBounce details showed:\n- Relay path through `relay.mailchannels.net`\n- Recipient looked like an SMTP host mailbox (`kefa@de-fra-smtpout7.hostinger.io`)\n- Error: `421 Downstream server error`\n\n### Root causes identified\n1. Mis-targeted recipient (SMTP server domain used as mailbox address).\n2. Mail path could fall back to `mail()` when SMTP config was missing/unavailable, which routed through MailChannels.\n\n### Fixes completed\n#### Code hardening\nUpdated `backend/mail_service.php`:\n- Added recipient and sender email validation (`FILTER_VALIDATE_EMAIL`).\n- Improved SMTP security mode handling (`ssl`, `tls`, `none`).\n- SMTP auth now enabled only when username exists.\n- Disabled `mail()` fallback for CLI jobs (cron path), so cron fails explicitly instead of silently routing via MailChannels.\n\n#### Live SMTP configuration\nOn live host (`192.168.7.202`), app settings were updated to Hostinger SMTP:\n- `smtp_host = smtp.hostinger.com`\n- `smtp_port = 465`\n- `smtp_secure = ssl`\n- `smtp_user = admin@ellasalterations.com`\n- `smtp_pass = [updated in DB]`\n- `smtp_from_email = admin@ellasalterations.com`\n- `smtp_from_name = Ella's Alterations`\n- `cron_notify_email = admin@ellasalterations.com`\n- `nightly_report_to_email = admin@ellasalterations.com`\n\n### Validation\n- `app_send_mail()` test on live host returned success via PHPMailer.\n- Cron job re-run succeeded:\n - `php8.3 /mnt/drive1/customerdb/backend/jobs/run_cron_job.php 7`\n - Returned `\"ok\": true`\n\n### Follow-up\n- Recommended mailbox password rotation after credentials were shared in chat.\n\n---\n\n## 2) Receipt Disclosure Requirements\n\n### Requested behavior\nEnsure the following actions include required disclosures at the end:\n- Print Receipt\n- Email Receipt\n- Save Receipt (PDF)\n\n### Fixes completed\nUpdated disclosure handling across all receipt generation paths:\n\n#### `frontend/visit.html`\n- Added canonical `REQUIRED_RECEIPT_DISCLOSURES` constant.\n- Added `buildReceiptDisclosureHtml()` to render disclosure blocks.\n- `sendEmailReceipt()` path now appends required disclosures in generated email HTML.\n- `generateVisitReport()` now sends `report_disclaimer_override` to backend PDF generator so saved PDFs use same required text.\n\n#### `frontend/api/visit_report_generate.php`\n- Added support for request payload key: `report_disclaimer_override`.\n- Enhanced disclaimer parser to handle uppercase heading lines with colon format (e.g., `DEPOSIT POLICY:`).\n- Uses override text when provided, otherwise settings fallback.\n\n#### `frontend/receipt.html`\n- Added required disclosure constant and merge logic.\n- Replaced old extra notice injection with required disclosure merge.\n- Terms rendering updated to preserve heading/body format.\n\n### Validation\n- PHP syntax check passed on `frontend/api/visit_report_generate.php`.\n- Confirmed references to new disclosure constants/functions in all required paths.\n\n---\n\n## 3) Dashboard Today Totals Incorrect (5/5 vs expected 8/8)\n\n### Reported issue\nDashboard showed:\n- `Today Customers: 5`\n- `Today New: 5`\nExpected:\n- `8` and `8`\n\n### Root causes identified\nIn `frontend/api/totals_snapshot_live.php`:\n1. Logic prioritized `customer_totals` and only checked `visits` if count was zero.\n - If `customer_totals` lagged, dashboard stayed low.\n2. A `Walk In` drop-off row needed exclusion for this specific metric.\n\n### Fixes completed\nUpdated `ts_today_payment_totals_live()` in `frontend/api/totals_snapshot_live.php`:\n- Compute drop-off counts from both sources (`customer_totals`, `visits`).\n- Use max of the two counts instead of one-way fallback.\n- Exclude `booking_source = 'walk in'` when visits table has that column.\n\n### Deployment and validation\n- Deployed updated API file to live host.\n- Live endpoint check returned:\n - `\"today_totals\":{\"total_customers\":8,\"new_customers\":8,...}`\n\n---\n\n## 4) Morning Import Visit Reuse Rules\n\n### Requested rules\nFor morning Setmore import:\n1. If customer has visit type `drop off` or `rescheduled` or `no show`: reuse visit and add Setmore notes.\n2. If customer has visit type `pick up`: create a new visit and add Setmore notes.\n3. If customer has no visit record: create a new visit and add Setmore notes.\n\n### Fixes completed\nUpdated `backend/setmore_service.php`:\n\n#### Rule enforcement\n- `app_setmore_reusable_dropoff_visit()` now inspects latest customer visit and allows reuse only for copy-source statuses (`dropoff`, `rescheduled`, `noshow`).\n- If latest status is pickup, reuse is not selected, so a new visit is created.\n- If no prior visit exists, new visit creation continues as normal.\n\n#### Notes behavior\n- Added `app_setmore_merge_notes()`.\n- Reused visit updates now merge/append Setmore notes instead of replacing existing operator notes.\n\n#### Safety improvement\n- On reused visits, preserve existing `source_key_value` if it belongs to another appointment key.\n\n### Deployment and validation\n- Deployed to live host.\n- Lint with `php8.3 -l` passed.\n- Morning smoke test passed:\n - `php8.3 /mnt/drive1/customerdb/backend/jobs/smoke_morning_jobs.php`\n\nNote:\n- Default `php -l` binary on host showed parse issue due different PHP binary path; `php8.3` (actual cron/runtime binary) is clean.\n\n---\n\n## 5) Nightly Summary Section Order Drift\n\n### Reported issue\nNightly summary sections had moved around.\n\n### Required order\n1. Daily Detail Report\n2. Daily Summary Report\n3. Month To Date Report\n4. Yearly Summary Report\n5. Daily Imported People List\n6. Alterations Next 7 Days\n\n### Fix completed\nReordered report block assembly in `backend/nightly_reports_service.php` to match exact required sequence.\n\n### Deployment and validation\n- Deployed to live host.\n- `php8.3 -l /mnt/drive1/customerdb/backend/nightly_reports_service.php` passed.\n\n---\n\n## 6) PDF Files Saving with Raw HTML Text\n\n### Reported issue\nSome saved PDFs looked wrong (HTML source printed in the PDF instead of rendered receipt layout).\n\n### Investigation findings\n- Sample file `Hannah-Goncalves-448248-04032026.pdf` is a valid PDF container but includes raw HTML source text (`<!DOCTYPE html>`, tags visible in extracted text).\n- Bulk scan found 4 affected files, all in `04032026` batch:\n - `Hannah-Goncalves-448248-04032026.pdf`\n - `Emiliano-Delgado-3226-04032026.pdf`\n - `Laura-Coyne-488970-04032026.pdf`\n - `Jamie-Miller-488977-04032026.pdf`\n- Newer reports were rendering normally.\n\n### Fix completed (preventive)\nUpdated `frontend/api/visit_report_generate.php`:\n- Added `vrg_pdf_looks_like_html_source()` quality gate.\n- If generated PDF appears to contain HTML source text, generation now fails fast and does not save corrupt output.\n\n### Deployment and validation\n- Deployed to live host.\n- `php8.3 -l` passed.\n\n### Follow-up required\n- Re-generate the 4 historical affected files from UI (`Save Receipt PDF`) to replace bad copies.\n\n---\n\n## Files Changed In This Session (Core)\n\n### Backend\n- `backend/mail_service.php`\n- `backend/setmore_service.php`\n- `backend/nightly_reports_service.php`\n\n### Frontend\n- `frontend/visit.html`\n- `frontend/receipt.html`\n- `frontend/api/visit_report_generate.php`\n- `frontend/api/totals_snapshot_live.php`\n\n---\n\n## Operational Notes\n\n- Multiple live updates were deployed directly to host `192.168.7.202`.\n- Runtime checks were executed with `php8.3` to match cron/runtime behavior.\n- Temporary maintenance scripts used during diagnosis were deleted from local and remote `/tmp`.\n\n---\n\n## Remaining Action Items\n\n1. Rotate SMTP mailbox password (credential was provided in conversation text).\n2. Re-generate the 4 known bad PDFs from `04032026` batch.\n3. Optional: Add an admin audit endpoint to list historically malformed PDFs using the same detection method, for one-time cleanup.\r\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"SESSION_COMPLETION_2026-04-17.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"21","document_type":"upload","title":"Curser Updates 04-17-2026","slug":"curser-updates-04-17-2026","summary_text":"Curser Updates 04-17-2026","content_markdown":null,"content_html":null,"file_name":"SESSION_COMPLETION_2026-04-17.md","stored_name":"20260417-191850-3b41b6c5.md","mime_type":"application/octet-stream","file_size_bytes":"11631","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-191850-3b41b6c5.md","is_deleted":"0","created_at":"2026-04-17 15:18:50","updated_at":"2026-04-17 15:18:50","editor_content":"# Session completion summary — 2026-04-17\n\nThis document records work completed across multiple backend/UI areas: problems observed, root causes, fixes applied, and new or changed behavior. Paths are relative to the repository root (`customerdb`).\n\n---\n\n## 1. Reminders (`backend/reminders.php`)\n\n### Requested behavior\n- Embedded reminders iframe should use more of the layout; later, remove the reminder queue panel entirely and enlarge the embed.\n\n### Changes\n1. **Two-column layout (1/4 + 3/4)** \n - Replaced shared `grid cols-2` (~50/50) with a local class `reminders-embed-grid` using `grid-template-columns: 1fr 3fr`, with a `920px` breakpoint stacking to one column (aligned with `page_helpers.php`).\n\n2. **Queue removed; full-width tall iframe** \n - Removed the “Reminder Queue” article (stats + table) and all related PHP (`reminder_service.php` queries, `$summary`, `$rows`).\n - Single `article.panel` with the iframe only.\n - CSS: `min-height: 720px`, `height: calc(100vh - 200px)` and `calc(100dvh - 200px)` for viewport-relative height (with `vh` fallback).\n - Removed nested duplicate `.shell` if it had been introduced earlier in this file (payroll had a similar issue; reminders stayed within header shell only).\n\n### Notes\n- `webui/backend/reminders.php` remains a thin loader to `backend/reminders.php`.\n\n---\n\n## 2. Umami analytics (`backend/umami.php`)\n\n### Issues\n- Iframe pointed at a non-existent path (`/backend/umami-embed/login`) → “not found” in the frame.\n- After switching to `https://umami.myexperttailor.com/...`, browsers showed **refused to connect** / embedding failures (CSP / `frame-ancestors`, or network).\n\n### Fixes\n- Set public dashboard URL to the provided Umami website URL.\n- **Embedding:** iframe now targets the **LAN Umami** instance `http://192.168.7.206:3006` with the same `/websites/{uuid}` path so framing is not blocked like the public HTTPS host often is.\n- Copy updated to explain LAN vs public; **Open Public Umami** still opens HTTPS in a new tab.\n\n### Notes\n- True fix for embedding the public host requires server-side headers or a reverse proxy; documented in repo history (`documents_storage`, session logs).\n\n---\n\n## 3. Issue tracker (`backend/issue_tracker.php`)\n\n### Requested behavior\n- Square corners on “boxes,” not rounded.\n\n### Implementation\n- Wrapper class `issue-tracker-square` with overrides for `.panel`, `.card`, `.stat`, `.notice`, inputs, selects, textareas, and `.btn` → `border-radius: 0`.\n- `.done-pill` / `.open-pill` set to `border-radius: 0` for consistency.\n- Scoped so global backend chrome outside the wrapper is unchanged.\n\n---\n\n## 4. Medications (`backend/medication.php`)\n\n### Requested behavior\n- No rounded corners on medication page “boxes.”\n- **Update Medication** as a pill (rounded control); row action aligned with that wording.\n\n### Implementation\n- Wrapper `medication-page-square` with overrides (panels, notices, inputs, buttons square).\n- Exception class **`med-update-pill`** → `border-radius: 999px !important` on:\n - Submit button in **edit** mode (“Update Medication”).\n - Table row link (label **Update Medication**, was “Edit”).\n- Local components (`med-calc-row`, `filter-tab`, `badge`, `person-chip`) set to square radii.\n\n### Follow-on (reports)\n- See **§10 Medication reports** (`medication_report.php`).\n\n---\n\n## 5. Medication reports — printable Excel-style (`backend/medication_report.php`)\n\n### Issues\n- `@media print` hid **`.panel:not(.print-keep)`**, which hid almost all report content; “Print” flow was broken for real output.\n- “Due soon” was card layout, not spreadsheet-like.\n\n### Fixes / features\n- **`mrep-excel`** tables: `border-collapse`, full cell borders, gray header row, zebra rows.\n- **Due in next 5 days** converted from flex cards to a **single table** with columns including an on-screen Edit column; **`.mrep-no-print`** hides action column in print.\n- Overdue, by-person inner tables, inactive list → **`mrep-excel`**.\n- Print CSS: hide summary dash, hero actions, buttons; tighten typography; `print-color-adjust: exact` where helpful.\n- Header action: **Print Report** with stable **`id=\"mrep-print-btn\"`**; `page_helpers.php` extended so **anchor** actions can carry **`id`** (same as buttons).\n\n---\n\n## 6. Payroll — stats & theme (`backend/payroll.php`)\n\n### Issues\n- Stat numbers appeared “all black” / not aligned with **settings / theme** CSS variables.\n- Extra **nested `<div class=\"shell\">`** duplicated `app_backend_render_header()`’s shell.\n\n### Fixes\n- **`.pay-stat`**: `var(--backend-box-bg)`, `--backend-line`, `--backend-box-text`, labels `--backend-muted`.\n- **`.worker-yana` / `.worker-lola`**: `var(--teal)` and `var(--accent)` instead of hard-coded hex.\n- All six stats (including **Yana/Lola total rows**) use worker color classes.\n- Tabs / table / paste area hints aligned with backend variables where touched.\n- Unpaid tab badge uses `var(--accent)` + `var(--button-text)`.\n- **Removed inner `.shell` wrapper.**\n\n---\n\n## 7. Payroll — paste import & Excel dates (`backend/payroll_service.php` + `payroll.php`)\n\n### Bugs / issues\n1. **Header row detection** compared normalized Excel headers to a list of **non-normalized** variants (e.g. `amount paid` vs `amount_paid`) → header row often misclassified → wrong column map → skips / zero imports (especially **Yana**-style sheets).\n2. **Excel date serials** and US dates not parsed by `DateTimeImmutable` alone.\n3. **UTF-8 BOM** and **NBSP** breaking header matching.\n4. On paste **exception**, POST resubmit UX; success notice used POST worker string vs canonical.\n\n### Fixes\n- Strip **BOM**; normalize **NBSP** to space in paste text.\n- Build **`$knownHeaderVariants`** using the same **`$norm()`** as header cells.\n- **`$toDate`**: serial range **20000–119999** via **25569** Excel→Unix conversion; explicit `createFromFormat` attempts (`n/j/Y`, `m/d/Y`, etc.) with `DateTimeImmutable::getLastErrors()` guard; fallback parse.\n- Expanded **column aliases** (`customerdatetime`, `custdate`, `client`, etc.).\n- Return array includes **`worker`** (canonical); notice uses it.\n- **Redirect** `?tab=paste&error=...` on paste import exception.\n- **`payroll.php`**: hint text mentions serials and BOM.\n\n---\n\n## 8. Payroll — printable Excel-style (`backend/payroll.php`)\n\n### Features\n- Wrapper **`pay-print-root`**; tables use **`pay-excel`** (grid borders, header strip, zebra).\n- **`@media print`**: hide stats, tabs, mark-paid toolbar, check-all links, inputs/forms inside data tables; compact borders/fonts.\n- **Print** button in hero (`id=\"pay-print-btn\"`) + JS `window.print()`.\n\n---\n\n## 9. Payroll — mark multiple paid (`backend/payroll.php`)\n\n### Bug\n- **Nested `<form>`** for row delete inside the **mark paid** `<form>`. HTML forbids nested forms; the browser **closes the outer form** at the inner `<form>`, so most **`entry_ids[]`** checkboxes were **outside** the mark-paid form → only **one** (or erratic) ID posted.\n\n### Fix\n- Delete control: **`<button type=\"submit\" form=\"payroll-del-{id}\">`**.\n- **Hidden `<form id=\"payroll-del-…\">`** per row, emitted **after** `</form>` of mark-paid (merged Yana + Lola unpaid rows).\n- Main form **`id=\"payroll-mark-paid\"`**.\n\n---\n\n## 10. Stocks & crypto — average cost precision (`backend/stocks_crypto.php`, `backend/stocks_crypto_service.php`)\n\n### Issue\n- **`avg_cost DECIMAL(14,6)`** and form **`step=\"0.000001\"`** / six-decimal display capped precision (problematic for crypto).\n\n### Fixes\n- Schema: **`DECIMAL(24,12)`**; `ALTER` to widen existing DBs (try/catch).\n- Save: pass **numeric string** into SQL (comma stripped, escaped) instead of float stringification for INSERT/UPDATE.\n- Form: **`step=\"any\"`**; edit value from **`trim((string)$editAsset['avg_cost'])`** (DB string).\n- Portfolio avg column: display **string** from DB to avoid float truncation.\n- Chart: **`hist_asset`** path uses JSON-encoded string → **`Number()`** in JS for the avg line.\n\n---\n\n## 11. Stocks & crypto — duplicate ticker / lots (`backend/stocks_crypto_service.php`, `backend/stocks_crypto.php`, `backend/api/stocks_summary.php`)\n\n### Issue\n- **`UNIQUE KEY uq_ticker (ticker)`** prevented a second **BTC** row even when notes differed.\n\n### Fixes\n- New installs: **`KEY ix_stocks_asset_ticker (ticker)`** (non-unique) instead of `uq_ticker`.\n- Existing DBs: **`ALTER TABLE stocks_asset DROP INDEX uq_ticker`** (try/catch); **`ADD KEY ix_stocks_asset_ticker`** if missing (try/catch).\n- **Portfolio / chart links:** `hist={ticker}&hist_asset={stocks_asset_id}` (+ optional `type=` filter) so **avg cost** matches the correct lot when tickers repeat.\n- **Prices tab:** inputs **`name=\"price_a_{stocks_asset_id}\"`**; **`save_prices`** resolves asset id → ticker (duplicate tickers no longer overwrite the same POST key).\n- **`app_stocks_fetch_all_live`:** fetch **once per distinct ticker** (dedupe).\n- **`stocks_summary` API:** each asset includes **`id`** (`stocks_asset_id`).\n\n### Related: `backend/page_helpers.php`\n- **`id`** attribute supported on **link**-type header actions (used by medication report print link).\n\n---\n\n## Files touched (summary)\n\n| File | Nature of change |\n|------|------------------|\n| `backend/reminders.php` | Layout + iframe-only page |\n| `backend/umami.php` | Embed URL, copy, LAN vs public |\n| `backend/issue_tracker.php` | Square-corner scoped CSS |\n| `backend/medication.php` | Square UI + pill update control + row link label |\n| `backend/medication_report.php` | Excel tables + print CSS + print button id on links |\n| `backend/page_helpers.php` | `id` on `<a>` action items |\n| `backend/payroll.php` | Theme vars, no nested shell, Excel/print, paste UX, mark-paid/delete forms |\n| `backend/payroll_service.php` | Paste import robustness, dates, headers, worker in return |\n| `backend/stocks_crypto.php` | avg_cost UI, `hist_asset`, portfolio URLs, `price_a_*` |\n| `backend/stocks_crypto_service.php` | Schema avg_cost, drop uq_ticker, index, save avg string, live fetch dedupe |\n| `backend/api/stocks_summary.php` | `id` on each asset in JSON |\n\nThin loaders under `webui/backend/` were unchanged where they only `require` canonical `backend/*.php`.\n\n---\n\n## Testing suggestions (manual)\n\n1. **Reminders:** load `/backend/reminders.php` — iframe fills width and height sensibly.\n2. **Umami:** confirm LAN iframe loads; public link in new tab.\n3. **Issue tracker / medication:** visual check square corners and pill(s) on medication update path.\n4. **Medication report:** Print preview — tables have grid lines; no empty printout.\n5. **Payroll:** paste Yana/Lola sheets with BOM, serial dates, varied headers; mark **multiple** unpaid rows paid in one submit.\n6. **Payroll print:** Print preview hides chrome; tables print.\n7. **Stocks:** add two **BTC** lots with different notes; set high-precision avg cost; prices tab saves both rows; chart `hist_asset` matches correct avg line; API returns distinct `id` per row.\n\n---\n\n## Known limitations / follow-ups\n\n- **Umami public host** embedding still depends on remote CSP / network; LAN proxy is the reliable approach.\n- **PHP `float`** still limits extreme precision in some JS chart paths; DB stores **12** fractional digits for `avg_cost`.\n- **Payroll paste** very large pastes may hit `max_input_vars` / post size limits (server config), not addressed in code.\n- **Medication** “Update Medication” row link text is long in narrow tables; acceptable per product request.\n\n---\n\n*Generated from implementation session work; adjust dates or add ticket IDs if you track work in an external system.*\n","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"20","document_type":"upload","title":"Complete Documentation up to date 04-17-2026","slug":"complete-documentation-up-to-date","summary_text":"Complete Documentation up to date 04-17-2026","content_markdown":"# Ella's Alterations - Comprehensive System Documentation\r\n\r\n**Generated:** April 17, 2026\r\n**Location:** C:\\code\\customerdb\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n1. [System Overview](#1-system-overview)\r\n2. [Directory Structure](#2-directory-structure)\r\n3. [Database Schema](#3-database-schema)\r\n4. [API Endpoints](#4-api-endpoints)\r\n5. [External Integrations](#5-external-integrations)\r\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\r\n7. [Web Applications](#7-web-applications)\r\n8. [Settings & Configuration](#8-settings--configuration)\r\n9. [Migration Notes](#9-migration-notes)\r\n10. [Scripts & Tools](#10-scripts--tools)\r\n\r\n---\r\n\r\n## 1. System Overview\r\n\r\n### Purpose\r\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\r\n\r\n### Server Information\r\n- **Primary Server:** kefa@192.168.7.202\r\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\r\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\r\n- **Web Server:** Apache 2.4 with mod-php\r\n- **PHP Version:** 8.3\r\n\r\n### Three Web Applications\r\n\r\n| App | Local URL | Public URL | Purpose |\r\n|-----|-----------|------------|---------|\r\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\r\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\r\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\r\n\r\n---\r\n\r\n## 2. Directory Structure\r\n\r\n```\r\ncustomerdb/\r\n├── backend/ # Main PHP backend API and services\r\n│ ├── api/ # REST API endpoints (~57 files)\r\n│ ├── bin/ # Binary/executable scripts\r\n│ ├── backup/ # Backup versions of services\r\n│ └── jobs/ # Background job scripts\r\n├── frontend/ # Front Desk app (port 8895)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\r\n├── webui/ # Management app (port 8894)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ ├── backend/ # Backend API copy\r\n│ └── email/ # Email templates\r\n├── schedule/ # Easy!Appointments installation\r\n├── schema/ # Database schema files\r\n├── migrations/ # SQL migrations\r\n├── scripts/ # PowerShell/Python import scripts\r\n├── docs/ # Documentation\r\n├── blog/ # Blog system and reengineering\r\n├── csv/ # CSV data files\r\n├── backups/ # Backup archives\r\n├── Topaz/ # Topaz signature pad configs\r\n├── webui/ # Second web interface\r\n│ ├── api/\r\n│ ├── backend/\r\n│ └── frontend/\r\n├── notused/ # Deprecated/unused files\r\n└── wordpress-plugin/ # WordPress integration\r\n```\r\n\r\n---\r\n\r\n## 3. Database Schema\r\n\r\n### Core Tables (from mariadb_core_schema.sql)\r\n\r\n#### customers\r\nMain customer registry.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| customer_id | INT UNSIGNED PK | Auto-increment |\r\n| legacy_customer_id | INT NULL | Original Access DB ID |\r\n| created_at | DATETIME | Record creation time |\r\n| full_name | VARCHAR(200) | Customer full name |\r\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\r\n| email | VARCHAR(255) | Email address |\r\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\r\n| description | TEXT | Description |\r\n| notes | TEXT | Internal notes |\r\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\r\n\r\n#### visits\r\nOne row per alteration job/order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| visit_id | INT UNSIGNED PK | |\r\n| legacy_visit_id | INT NULL | Original Access DB ID |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| visit_date | DATETIME | Drop-off date |\r\n| pickup_date | DATETIME | Expected pickup |\r\n| notes | TEXT | General visit notes |\r\n| customer_name_snapshot | VARCHAR(200) | |\r\n| phone_snapshot | VARCHAR(30) | |\r\n| email_snapshot | VARCHAR(255) | |\r\n| color | VARCHAR(100) | Garment color |\r\n| alterations_needed | TEXT | |\r\n| alterations_notes | TEXT | Detailed alteration instructions |\r\n| alterations_price | DECIMAL(10,2) | Manual price field |\r\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\r\n| expedited_fee | DECIMAL(10,2) | |\r\n| paid_date | DATETIME | When paid |\r\n| signature_text | TEXT | Text signature (legacy) |\r\n| signature_date | DATETIME | |\r\n| paid_flag | TINYINT(1) | 1 = paid |\r\n| source_key_value | VARCHAR(100) | |\r\n\r\n#### visit_signatures\r\nFull resolution signature images per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| signature_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNIQUE | One signature per visit |\r\n| signature_date | DATETIME | |\r\n| image_format | VARCHAR(50) | Default 'image/png' |\r\n| original_filename | VARCHAR(255) | |\r\n| original_extension | VARCHAR(20) | |\r\n| file_size_bytes | BIGINT | |\r\n| sha256_hash | CHAR(64) | Hash for verification |\r\n| signature_image | LONGBLOB | Raw PNG binary |\r\n| created_at | DATETIME | |\r\n\r\n#### item_types\r\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| item_type_id | INT UNSIGNED PK | |\r\n| type_name | VARCHAR(100) UNIQUE | Display name |\r\n\r\n#### orders\r\nOrders per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNSIGNED FK | → visits |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| created_at | DATETIME | |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| picked_up | TINYINT(1) | |\r\n| source_table | VARCHAR(50) | |\r\n\r\n#### order_items\r\nLine items for each alteration within a visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| legacy_alteration_id | INT NULL | |\r\n| item_type_id | INT FK | → item_types |\r\n| item_type_name | VARCHAR(100) | |\r\n| description | TEXT | |\r\n| quantity | INT | Default 1 |\r\n| unit_price | DECIMAL(10,2) | |\r\n| total_price | DECIMAL(10,2) | |\r\n| notes | TEXT | Per-item notes |\r\n| color | VARCHAR(100) | Garment color |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| is_range_price | TINYINT(1) | |\r\n| range_price_label | VARCHAR(100) | |\r\n| paid_flag | TINYINT(1) | |\r\n| paid_type | VARCHAR(100) | |\r\n| picked_up | TINYINT(1) | |\r\n| created_at | DATETIME | |\r\n\r\n#### order_item_type_links\r\nMany-to-many links between order items and item types.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\r\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\r\n| item_type_id | INT UNSIGNED FK | → item_types |\r\n| slot_number | TINYINT UNSIGNED | |\r\n\r\n#### payments\r\nPayment records per order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| payment_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n\r\n#### tax_payments\r\nTax payment records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| tax_payment_id | INT UNSIGNED PK | |\r\n| legacy_tax_payment_id | INT NULL | |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n| receipt_blob | LONGBLOB | |\r\n\r\n#### change_log\r\nAudit log for changes.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| log_id | BIGINT UNSIGNED PK | |\r\n| legacy_log_id | INT NULL | |\r\n| table_name | VARCHAR(100) | |\r\n| record_id | BIGINT | |\r\n| visit_id | INT | |\r\n| field_name | VARCHAR(100) | |\r\n| old_value | TEXT | |\r\n| new_value | TEXT | |\r\n| changed_at | DATETIME | |\r\n| changed_by | VARCHAR(100) | |\r\n\r\n#### ads\r\nAdvertising/articles.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| ad_id | INT UNSIGNED PK | |\r\n| legacy_ad_id | INT NULL | |\r\n| article_text | TEXT | |\r\n| article_picture | LONGBLOB | |\r\n| site_submitted | VARCHAR(255) | |\r\n| views | INT | Default 0 |\r\n| date_posted | DATETIME | |\r\n\r\n#### staging_* tables\r\nStaging tables for data migration.\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| staging_customer_table_raw | Staging for legacy customer data |\r\n| staging_customer_visits_raw | Staging for legacy visits |\r\n| staging_alteration_items_raw | Staging for items |\r\n\r\n### Additional Backend Tables\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| setmore_appointments | Synced from Setmore |\r\n| backend_conversion_job | Document conversion jobs |\r\n| backend_job_runs | Background job execution log |\r\n| customer_totals | Daily customer totals |\r\n| daily_customer_totals | Daily summary totals |\r\n| customer_payment_transactions | Payment ledger |\r\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\r\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\r\n| backend_appointment_workflow | Appointment state machine |\r\n| backend_no_show_log | No-show tracking |\r\n| backend_error_log | Error logging |\r\n| backend_change_log | Change audit log |\r\n| backend_document | Document storage |\r\n| backend_document_version | Document version history |\r\n| backend_speed_dial | Quick links |\r\n| backend_square_terminal_code | Square terminal pairing codes |\r\n| backend_square_terminal_device | Square device registry |\r\n| cron_job | Scheduled job definitions |\r\n| cron_job_run | Cron execution history |\r\n| app_settings | Global configuration |\r\n| app_users | Login accounts |\r\n| remember_tokens | \"Remember Me\" tokens |\r\n| customer_reports | PDF report index |\r\n\r\n---\r\n\r\n## 4. API Endpoints\r\n\r\n### 4.1 Frontend API (port 8895 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer_get.php** | Get customer by ID |\r\n| **customer_save.php** | Create or update customer |\r\n| **customer_detail.php** | Customer + visits summary |\r\n| **customer_delete.php** | Delete customer |\r\n| **customer_list_hide.php** | Hide customer from list |\r\n| **search_customers.php** | Search by name/phone |\r\n| **visit_get.php** | Get visit + items |\r\n| **visit_save.php** | Create/update visit + items |\r\n| **visit_delete.php** | Delete visit |\r\n| **visit_report_generate.php** | Generate visit report |\r\n| **item_types.php** | List all alteration types |\r\n| **item_type_add.php** | Add new item type |\r\n| **item_type_update.php** | Update item type |\r\n| **item_type_delete.php** | Delete item type |\r\n| **pickups_today.php** | Orders due today |\r\n| **ready.php** | Orders ready for pickup |\r\n| **overdue.php** | Overdue orders |\r\n| **upcoming.php** | Orders due in next N days |\r\n| **workload_7days.php** | 7-day workload |\r\n| **save_signature.php** | Save canvas signature |\r\n| **reports_list.php** | Reports for customer |\r\n| **attach_reports.php** | Index all PDFs to DB |\r\n| **save_signature.php** | Save signature image |\r\n| **today_appointments_list.php** | Today's appointments |\r\n| **settings_get.php** | Get app settings |\r\n| **settings_save.php** | Save app settings |\r\n| **settings_css.php** | Live theme CSS |\r\n| **send_email.php** | Send email via SMTP |\r\n| **send_sms.php** | Send SMS via Twilio |\r\n| **send_reminders.php** | Send appointment reminders |\r\n| **ea_appointments.php** | EasyAppointments REST API |\r\n| **ea_webhook.php** | Receives EA webhook events |\r\n| **setmore.php** | Setmore API proxy |\r\n| **setmore_to_ea_import.php** | Import customers to EA |\r\n| **square_config.php** | Square public config |\r\n| **square_process.php** | Process Square payment |\r\n| **square_customer_log.php** | Customer Square log |\r\n| **square_terminal.php** | Square terminal |\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **auth.php** | Session authentication |\r\n| **config.php** | App config constants |\r\n| **db.php** | Database connection |\r\n| **log_error.php** | Log client-side error |\r\n| **todo_list.php** | Todo list management |\r\n| **send_telegram.php** | Send Telegram messages |\r\n\r\n### 4.2 Management API (port 8894 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **customer_detail.php** | Customer + visits |\r\n| **search_customers.php** | Search customers |\r\n| **update_customer.php** | Update customer |\r\n| **users_list.php** | List all users |\r\n| **user_save.php** | Create/update user |\r\n| **list_report_dirs.php** | Date folders |\r\n| **list_report_files.php** | PDFs in folder |\r\n| **report_file.php** | Access report file |\r\n| **pickups_today.php** | Today's pickups |\r\n| **ready.php** | Ready for pickup |\r\n| **overdue.php** | Overdue |\r\n| **settings_get.php** | Get settings |\r\n| **settings_save.php** | Save settings |\r\n| **settings_css.php** | Live CSS theme |\r\n| **imap_list.php** | List IMAP folders |\r\n| **imap_fetch.php** | Fetch emails |\r\n| **imap_send.php** | Send email via IMAP |\r\n| **imap_action.php** | IMAP actions |\r\n| **sms_send_helper.php** | SMS sending helper |\r\n| **sms_webhook_receive.php** | Receive SMS webhooks |\r\n| **sms_inbox_api.php** | SMS inbox API |\r\n| **slideshow_list.php** | Slideshow images |\r\n| **slideshow_upload.php** | Upload slideshow |\r\n| **slideshow_delete.php** | Delete slideshow |\r\n| **users_list.php** | User management |\r\n| **report_list.php** | Report list |\r\n| **debug_filters.php** | Debug filters |\r\n| **sysadmin.php** | System admin |\r\n| **test.php** | Testing endpoint |\r\n\r\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer.php** | Get customer details |\r\n| **customers.php** | List/search customers |\r\n| **customer_save.php** | Save/update customer |\r\n| **visit.php** | Get visit details |\r\n| **visit_delete.php** | Delete visit |\r\n| **appointments.php** | Unified appointments |\r\n| **appointments_summary.php** | Appointment statistics |\r\n| **setmore_appointments.php** | Setmore appointments |\r\n| **setmore_import.php** | Import from Setmore |\r\n| **module_*.php** | Module loaders |\r\n| **alteration_items.php** | Alteration management |\r\n| **alteration_items_summary.php** | Alteration statistics |\r\n| **gold_silver_*.php** | Gold/silver prices |\r\n| **bank_*.php** | Bank transactions |\r\n| **stocks_summary.php** | Stock data |\r\n| **medication_*.php** | Medication tracking |\r\n| **payroll_import.php** | Payroll import |\r\n| **car_maintenance_*.php** | Vehicle maintenance |\r\n| **duke_*.php** | Duke integration |\r\n| **reports_*.php** | Report generation |\r\n| **totals_import.php** | Customer totals import |\r\n| **documents.php** | Document management |\r\n| **document_upload.php** | File uploads |\r\n| **cust_email_*.php** | Email data |\r\n| **mail_*.php** | Email sending |\r\n| **reminders_create.php** | Create reminders |\r\n| **nightly_reports.php** | Nightly reports |\r\n| **morning_jobs.php** | Morning jobs |\r\n| **appointment_workflow.php** | Workflow management |\r\n| **sysadmin.php** | System admin |\r\n| **reporting_module.php** | Reporting |\r\n| **job_runs.php** | Job tracking |\r\n| **health.php** | Health check |\r\n| **overview.php** | Dashboard data |\r\n\r\n---\r\n\r\n## 5. External Integrations\r\n\r\n### 5.1 Square (Payment Processing)\r\n- **Purpose:** Point-of-sale payments, receipts, terminal management\r\n- **Features:**\r\n - Square Terminal device pairing/management\r\n - Payment processing with tips\r\n - Customer payment logging\r\n - Sandbox and live environments\r\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\r\n\r\n### 5.2 Twilio (SMS)\r\n- **Purpose:** SMS notifications and reminders\r\n- **Features:**\r\n - Send SMS to customers\r\n - Appointment reminders\r\n - SMS consent tracking\r\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\r\n\r\n### 5.3 Setmore (Appointment Booking)\r\n- **Purpose:** Appointment synchronization\r\n- **Features:**\r\n - Pull appointments from Setmore API\r\n - Link to customers/visits\r\n- **Table:** `setmore_appointments`\r\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\r\n\r\n### 5.4 EasyAppointments (Alternative Booking)\r\n- **Purpose:** Full appointment booking platform\r\n- **Location:** C:\\code\\customerdb\\schedule\\\r\n- **Features:**\r\n - Customer/provider management\r\n - Web-based booking widget\r\n - REST API\r\n\r\n### 5.5 Telegram (Notifications)\r\n- **Purpose:** Bot-based notifications\r\n- **Features:**\r\n - Send notifications to channels\r\n - Visit alerts\r\n - Cron job status\r\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\r\n\r\n### 5.6 Ollama (AI - Optional)\r\n- **Purpose:** Local AI for email generation\r\n- **Settings:** `ollama_url`, `ollama_default_model`\r\n\r\n---\r\n\r\n## 6. Scheduled Jobs / Cron\r\n\r\n### Configured Cron Jobs\r\n\r\n| Job Label | Schedule | Purpose |\r\n|----------|----------|---------|\r\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\r\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\r\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\r\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\r\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\r\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\r\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\r\n\r\n### Background Job Scripts (backend/jobs/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **run_morning_jobs.php** | Main morning job runner |\r\n| **smoke_morning_jobs.php** | Smoke test |\r\n| **send_nightly_reports.php** | Nightly report emailer |\r\n| **send_nextday_customer_emails.php** | Next-day reminders |\r\n| **delete_add_name_records.php** | Cleanup job |\r\n| **rebuild_customer_totals.php** | Rebuild totals |\r\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\r\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\r\n| **import_customer_totals_csv.php** | CSV import |\r\n| **backfill_square_customer_log.php** | Square log backfill |\r\n| **run_cron_job.php** | Generic cron runner |\r\n\r\n---\r\n\r\n## 7. Web Applications\r\n\r\n### 7.1 Front Desk App (port 8895)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Main dashboard, search, stats |\r\n| **customer.html** | Customer detail + visit history |\r\n| **visit.html** | Create/edit alteration order |\r\n| **customer_display.html** | Second monitor display |\r\n| **receipt.html** | Printable receipt |\r\n| **reports.html** | Order list by status |\r\n| **upcoming.html** | Next 7 days pickups |\r\n| **schedule.html** | Appointments calendar |\r\n| **login.html** | Login page |\r\n| **item_types.html** | Item type management |\r\n\r\n### 7.2 Management App (port 8894)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Login page |\r\n| **webui.html** | Dashboard |\r\n| **customer.html** | Customer detail view |\r\n| **admin.html** | Reports browser |\r\n| **settings.html** | App settings editor |\r\n| **users.html** | User management |\r\n| **reports.html** | Order reports |\r\n\r\n---\r\n\r\n## 8. Settings & Configuration\r\n\r\n### app_settings Keys\r\n\r\n| Key | Purpose |\r\n|-----|---------|\r\n| theme_bg | Page background color |\r\n| theme_panel | Card/panel color |\r\n| theme_text | Text color |\r\n| theme_accent | Accent/button color |\r\n| theme_accent_deep | Darker accent |\r\n| theme_teal | Teal color |\r\n| theme_font | Font family |\r\n| smtp_host | Email server hostname |\r\n| smtp_port | Email server port |\r\n| smtp_secure | tls or ssl |\r\n| smtp_user | Email login |\r\n| smtp_pass | Email password |\r\n| smtp_from_email | From address |\r\n| smtp_from_name | From display name |\r\n| twilio_sid | Twilio Account SID |\r\n| twilio_token | Twilio Auth Token |\r\n| twilio_from | Twilio phone number |\r\n| imap_host | Incoming email server |\r\n| imap_port | IMAP port |\r\n| imap_ssl | SSL enable |\r\n| imap_user | IMAP login |\r\n| imap_pass | IMAP password |\r\n| square_token | Square API token |\r\n| square_location | Square location ID |\r\n| setmore_staff_key | Setmore API key |\r\n| setmore_refresh | Setmore refresh token |\r\n| require_report | PDF required |\r\n| require_signature | Signature required |\r\n| reports_base_path | Reports disk path |\r\n| reports_base_url | Reports URL |\r\n| ollama_url | Ollama AI URL |\r\n| ollama_default_model | Ollama model |\r\n| telegram_bot_token | Telegram bot token |\r\n| telegram_chat_id | Telegram chat ID |\r\n\r\n---\r\n\r\n## 9. Migration Notes\r\n\r\n### Access to MariaDB Workflow\r\n\r\n1. **Export source data from Access**\r\n - Export ordinary tables to CSV\r\n - Export attachment fields with VBA or DAO Recordset2 logic\r\n - Keep original IDs during migration\r\n\r\n2. **Load into MariaDB staging**\r\n - Use `LOAD DATA LOCAL INFILE` for CSV files\r\n - Load signatures separately into `visit_signatures`\r\n\r\n3. **Normalize**\r\n - `CustomerTable` → `customers`\r\n - `CustomerVisits` → `visits`\r\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\r\n - `2024TaxPayments` → `tax_payments`\r\n - `AlterationItemsChangeLog` → `change_log`\r\n\r\n4. **Signature BLOB path**\r\n - Export Access attachment to temp file\r\n - Convert BMP to PNG\r\n - Insert PNG bytes into `visit_signatures.signature_image`\r\n\r\n5. **Validation**\r\n - Compare source row counts vs target\r\n - Compare distinct legacy IDs\r\n - Compare sample signatures by SHA256\r\n\r\n---\r\n\r\n## 10. Scripts & Tools\r\n\r\n### PowerShell Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\r\n| **sync_backend_mirror.ps1** | Sync backend mirror |\r\n| **check_backend_mirror.ps1** | Check mirror status |\r\n\r\n### Python Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **signature_blob_loader.py** | Load signature blobs |\r\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\r\n| **import_access_v2.py** | Import Access v2 |\r\n| **import_csv_v2.py** | Import CSV v2 |\r\n\r\n### SQL Migrations (migrations/)\r\n\r\n| File | Purpose |\r\n|------|---------|\r\n| **20260407_add_conversion_job.sql** | Add conversion job table |\r\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\r\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\r\n\r\n---\r\n\r\n## File Statistics\r\n\r\n- **Total PHP files:** 200+\r\n- **Total SQL files:** 100+\r\n- **Total HTML pages:** 20+\r\n- **Database tables:** 30+\r\n\r\n---\r\n\r\n*End of Documentation*","content_html":"<h1>Ella's Alterations - Comprehensive System Documentation</h1>\n<p><strong>Generated:</strong> April 17, 2026</p>\n<p><strong>Location:</strong> C:\\code\\customerdb</p>\n<p>---</p>\n<h2>Table of Contents</h2>\n<p>1. [System Overview](#1-system-overview)</p>\n<p>2. [Directory Structure](#2-directory-structure)</p>\n<p>3. [Database Schema](#3-database-schema)</p>\n<p>4. [API Endpoints](#4-api-endpoints)</p>\n<p>5. [External Integrations](#5-external-integrations)</p>\n<p>6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)</p>\n<p>7. [Web Applications](#7-web-applications)</p>\n<p>8. [Settings & Configuration](#8-settings--configuration)</p>\n<p>9. [Migration Notes](#9-migration-notes)</p>\n<p>10. [Scripts & Tools](#10-scripts--tools)</p>\n<p>---</p>\n<h2>1. System Overview</h2>\n<h3>Purpose</h3>\n<p>Ella's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.</p>\n<h3>Server Information</h3>\n<ul>\n<li><strong>Primary Server:</strong> kefa@192.168.7.202</li>\n<li><strong>Mirror Drive:</strong> /mnt/drive2 (mirrors /mnt/drive1)</li>\n<li><strong>Database:</strong> MariaDB - database `ellas_alterations`, user `kefa_admin`</li>\n<li><strong>Web Server:</strong> Apache 2.4 with mod-php</li>\n<li><strong>PHP Version:</strong> 8.3</li>\n</ul>\n<h3>Three Web Applications</h3>\n<p>| App | Local URL | Public URL | Purpose |</p>\n<p>|-----|-----------|------------|---------|</p>\n<p>| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |</p>\n<p>| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |</p>\n<p>| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |</p>\n<p>---</p>\n<h2>2. Directory Structure</h2>\n<p>```</p>\n<p>customerdb/</p>\n<p>├── backend/ # Main PHP backend API and services</p>\n<p>│ ├── api/ # REST API endpoints (~57 files)</p>\n<p>│ ├── bin/ # Binary/executable scripts</p>\n<p>│ ├── backup/ # Backup versions of services</p>\n<p>│ └── jobs/ # Background job scripts</p>\n<p>├── frontend/ # Front Desk app (port 8895)</p>\n<p>│ ├── *.html # HTML pages</p>\n<p>│ ├── api/ # PHP API endpoints</p>\n<p>│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)</p>\n<p>├── webui/ # Management app (port 8894)</p>\n<p>│ ├── *.html # HTML pages</p>\n<p>│ ├── api/ # PHP API endpoints</p>\n<p>│ ├── backend/ # Backend API copy</p>\n<p>│ └── email/ # Email templates</p>\n<p>├── schedule/ # Easy!Appointments installation</p>\n<p>├── schema/ # Database schema files</p>\n<p>├── migrations/ # SQL migrations</p>\n<p>├── scripts/ # PowerShell/Python import scripts</p>\n<p>├── docs/ # Documentation</p>\n<p>├── blog/ # Blog system and reengineering</p>\n<p>├── csv/ # CSV data files</p>\n<p>├── backups/ # Backup archives</p>\n<p>├── Topaz/ # Topaz signature pad configs</p>\n<p>├── webui/ # Second web interface</p>\n<p>│ ├── api/</p>\n<p>│ ├── backend/</p>\n<p>│ └── frontend/</p>\n<p>├── notused/ # Deprecated/unused files</p>\n<p>└── wordpress-plugin/ # WordPress integration</p>\n<p>```</p>\n<p>---</p>\n<h2>3. Database Schema</h2>\n<h3>Core Tables (from mariadb_core_schema.sql)</h3>\n<p>#### customers</p>\n<p>Main customer registry.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| customer_id | INT UNSIGNED PK | Auto-increment |</p>\n<p>| legacy_customer_id | INT NULL | Original Access DB ID |</p>\n<p>| created_at | DATETIME | Record creation time |</p>\n<p>| full_name | VARCHAR(200) | Customer full name |</p>\n<p>| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |</p>\n<p>| email | VARCHAR(255) | Email address |</p>\n<p>| pickup_due_at | DATETIME | Next pickup date (synced from EA) |</p>\n<p>| description | TEXT | Description |</p>\n<p>| notes | TEXT | Internal notes |</p>\n<p>| is_new_customer | TINYINT(1) | 1 if flagged as new |</p>\n<p>#### visits</p>\n<p>One row per alteration job/order.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| visit_id | INT UNSIGNED PK | |</p>\n<p>| legacy_visit_id | INT NULL | Original Access DB ID |</p>\n<p>| customer_id | INT UNSIGNED FK | → customers |</p>\n<p>| visit_date | DATETIME | Drop-off date |</p>\n<p>| pickup_date | DATETIME | Expected pickup |</p>\n<p>| notes | TEXT | General visit notes |</p>\n<p>| customer_name_snapshot | VARCHAR(200) | |</p>\n<p>| phone_snapshot | VARCHAR(30) | |</p>\n<p>| email_snapshot | VARCHAR(255) | |</p>\n<p>| color | VARCHAR(100) | Garment color |</p>\n<p>| alterations_needed | TEXT | |</p>\n<p>| alterations_notes | TEXT | Detailed alteration instructions |</p>\n<p>| alterations_price | DECIMAL(10,2) | Manual price field |</p>\n<p>| total_alteration_price | DECIMAL(10,2) | Calculated from items |</p>\n<p>| expedited_fee | DECIMAL(10,2) | |</p>\n<p>| paid_date | DATETIME | When paid |</p>\n<p>| signature_text | TEXT | Text signature (legacy) |</p>\n<p>| signature_date | DATETIME | |</p>\n<p>| paid_flag | TINYINT(1) | 1 = paid |</p>\n<p>| source_key_value | VARCHAR(100) | |</p>\n<p>#### visit_signatures</p>\n<p>Full resolution signature images per visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| signature_id | BIGINT UNSIGNED PK | |</p>\n<p>| visit_id | INT UNIQUE | One signature per visit |</p>\n<p>| signature_date | DATETIME | |</p>\n<p>| image_format | VARCHAR(50) | Default 'image/png' |</p>\n<p>| original_filename | VARCHAR(255) | |</p>\n<p>| original_extension | VARCHAR(20) | |</p>\n<p>| file_size_bytes | BIGINT | |</p>\n<p>| sha256_hash | CHAR(64) | Hash for verification |</p>\n<p>| signature_image | LONGBLOB | Raw PNG binary |</p>\n<p>| created_at | DATETIME | |</p>\n<p>#### item_types</p>\n<p>Master list of alteration types (e.g., Hem, Zipper, Taper).</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| item_type_id | INT UNSIGNED PK | |</p>\n<p>| type_name | VARCHAR(100) UNIQUE | Display name |</p>\n<p>#### orders</p>\n<p>Orders per visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_id | BIGINT UNSIGNED PK | |</p>\n<p>| visit_id | INT UNSIGNED FK | → visits |</p>\n<p>| customer_id | INT UNSIGNED FK | → customers |</p>\n<p>| created_at | DATETIME | |</p>\n<p>| no_tax | TINYINT(1) | Tax exempt |</p>\n<p>| picked_up | TINYINT(1) | |</p>\n<p>| source_table | VARCHAR(50) | |</p>\n<p>#### order_items</p>\n<p>Line items for each alteration within a visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_item_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_id | BIGINT UNSIGNED FK | → orders |</p>\n<p>| legacy_alteration_id | INT NULL | |</p>\n<p>| item_type_id | INT FK | → item_types |</p>\n<p>| item_type_name | VARCHAR(100) | |</p>\n<p>| description | TEXT | |</p>\n<p>| quantity | INT | Default 1 |</p>\n<p>| unit_price | DECIMAL(10,2) | |</p>\n<p>| total_price | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | Per-item notes |</p>\n<p>| color | VARCHAR(100) | Garment color |</p>\n<p>| no_tax | TINYINT(1) | Tax exempt |</p>\n<p>| is_range_price | TINYINT(1) | |</p>\n<p>| range_price_label | VARCHAR(100) | |</p>\n<p>| paid_flag | TINYINT(1) | |</p>\n<p>| paid_type | VARCHAR(100) | |</p>\n<p>| picked_up | TINYINT(1) | |</p>\n<p>| created_at | DATETIME | |</p>\n<p>#### order_item_type_links</p>\n<p>Many-to-many links between order items and item types.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_item_type_link_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_item_id | BIGINT UNSIGNED FK | → order_items |</p>\n<p>| item_type_id | INT UNSIGNED FK | → item_types |</p>\n<p>| slot_number | TINYINT UNSIGNED | |</p>\n<p>#### payments</p>\n<p>Payment records per order.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| payment_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_id | BIGINT UNSIGNED FK | → orders |</p>\n<p>| payment_date | DATETIME | |</p>\n<p>| payment_type | VARCHAR(100) | |</p>\n<p>| payment_method | VARCHAR(100) | |</p>\n<p>| amount | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | |</p>\n<p>#### tax_payments</p>\n<p>Tax payment records.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| tax_payment_id | INT UNSIGNED PK | |</p>\n<p>| legacy_tax_payment_id | INT NULL | |</p>\n<p>| payment_date | DATETIME | |</p>\n<p>| payment_type | VARCHAR(100) | |</p>\n<p>| payment_method | VARCHAR(100) | |</p>\n<p>| amount | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | |</p>\n<p>| receipt_blob | LONGBLOB | |</p>\n<p>#### change_log</p>\n<p>Audit log for changes.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| log_id | BIGINT UNSIGNED PK | |</p>\n<p>| legacy_log_id | INT NULL | |</p>\n<p>| table_name | VARCHAR(100) | |</p>\n<p>| record_id | BIGINT | |</p>\n<p>| visit_id | INT | |</p>\n<p>| field_name | VARCHAR(100) | |</p>\n<p>| old_value | TEXT | |</p>\n<p>| new_value | TEXT | |</p>\n<p>| changed_at | DATETIME | |</p>\n<p>| changed_by | VARCHAR(100) | |</p>\n<p>#### ads</p>\n<p>Advertising/articles.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| ad_id | INT UNSIGNED PK | |</p>\n<p>| legacy_ad_id | INT NULL | |</p>\n<p>| article_text | TEXT | |</p>\n<p>| article_picture | LONGBLOB | |</p>\n<p>| site_submitted | VARCHAR(255) | |</p>\n<p>| views | INT | Default 0 |</p>\n<p>| date_posted | DATETIME | |</p>\n<p>#### staging_* tables</p>\n<p>Staging tables for data migration.</p>\n<p>| Table Name | Purpose |</p>\n<p>|------------|---------|</p>\n<p>| staging_customer_table_raw | Staging for legacy customer data |</p>\n<p>| staging_customer_visits_raw | Staging for legacy visits |</p>\n<p>| staging_alteration_items_raw | Staging for items |</p>\n<h3>Additional Backend Tables</h3>\n<p>| Table Name | Purpose |</p>\n<p>|------------|---------|</p>\n<p>| setmore_appointments | Synced from Setmore |</p>\n<p>| backend_conversion_job | Document conversion jobs |</p>\n<p>| backend_job_runs | Background job execution log |</p>\n<p>| customer_totals | Daily customer totals |</p>\n<p>| daily_customer_totals | Daily summary totals |</p>\n<p>| customer_payment_transactions | Payment ledger |</p>\n<p>| legacy_customer_totals_monthly | Monthly totals (legacy) |</p>\n<p>| legacy_customer_totals_irs_monthly | IRS monthly reports |</p>\n<p>| backend_appointment_workflow | Appointment state machine |</p>\n<p>| backend_no_show_log | No-show tracking |</p>\n<p>| backend_error_log | Error logging |</p>\n<p>| backend_change_log | Change audit log |</p>\n<p>| backend_document | Document storage |</p>\n<p>| backend_document_version | Document version history |</p>\n<p>| backend_speed_dial | Quick links |</p>\n<p>| backend_square_terminal_code | Square terminal pairing codes |</p>\n<p>| backend_square_terminal_device | Square device registry |</p>\n<p>| cron_job | Scheduled job definitions |</p>\n<p>| cron_job_run | Cron execution history |</p>\n<p>| app_settings | Global configuration |</p>\n<p>| app_users | Login accounts |</p>\n<p>| remember_tokens | "Remember Me" tokens |</p>\n<p>| customer_reports | PDF report index |</p>\n<p>---</p>\n<h2>4. API Endpoints</h2>\n<h3>4.1 Frontend API (port 8895 /api/)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>customer_get.php</strong> | Get customer by ID |</p>\n<p>| <strong>customer_save.php</strong> | Create or update customer |</p>\n<p>| <strong>customer_detail.php</strong> | Customer + visits summary |</p>\n<p>| <strong>customer_delete.php</strong> | Delete customer |</p>\n<p>| <strong>customer_list_hide.php</strong> | Hide customer from list |</p>\n<p>| <strong>search_customers.php</strong> | Search by name/phone |</p>\n<p>| <strong>visit_get.php</strong> | Get visit + items |</p>\n<p>| <strong>visit_save.php</strong> | Create/update visit + items |</p>\n<p>| <strong>visit_delete.php</strong> | Delete visit |</p>\n<p>| <strong>visit_report_generate.php</strong> | Generate visit report |</p>\n<p>| <strong>item_types.php</strong> | List all alteration types |</p>\n<p>| <strong>item_type_add.php</strong> | Add new item type |</p>\n<p>| <strong>item_type_update.php</strong> | Update item type |</p>\n<p>| <strong>item_type_delete.php</strong> | Delete item type |</p>\n<p>| <strong>pickups_today.php</strong> | Orders due today |</p>\n<p>| <strong>ready.php</strong> | Orders ready for pickup |</p>\n<p>| <strong>overdue.php</strong> | Overdue orders |</p>\n<p>| <strong>upcoming.php</strong> | Orders due in next N days |</p>\n<p>| <strong>workload_7days.php</strong> | 7-day workload |</p>\n<p>| <strong>save_signature.php</strong> | Save canvas signature |</p>\n<p>| <strong>reports_list.php</strong> | Reports for customer |</p>\n<p>| <strong>attach_reports.php</strong> | Index all PDFs to DB |</p>\n<p>| <strong>save_signature.php</strong> | Save signature image |</p>\n<p>| <strong>today_appointments_list.php</strong> | Today's appointments |</p>\n<p>| <strong>settings_get.php</strong> | Get app settings |</p>\n<p>| <strong>settings_save.php</strong> | Save app settings |</p>\n<p>| <strong>settings_css.php</strong> | Live theme CSS |</p>\n<p>| <strong>send_email.php</strong> | Send email via SMTP |</p>\n<p>| <strong>send_sms.php</strong> | Send SMS via Twilio |</p>\n<p>| <strong>send_reminders.php</strong> | Send appointment reminders |</p>\n<p>| <strong>ea_appointments.php</strong> | EasyAppointments REST API |</p>\n<p>| <strong>ea_webhook.php</strong> | Receives EA webhook events |</p>\n<p>| <strong>setmore.php</strong> | Setmore API proxy |</p>\n<p>| <strong>setmore_to_ea_import.php</strong> | Import customers to EA |</p>\n<p>| <strong>square_config.php</strong> | Square public config |</p>\n<p>| <strong>square_process.php</strong> | Process Square payment |</p>\n<p>| <strong>square_customer_log.php</strong> | Customer Square log |</p>\n<p>| <strong>square_terminal.php</strong> | Square terminal |</p>\n<p>| <strong>login.php</strong> | Authenticate |</p>\n<p>| <strong>logout.php</strong> | Clear session |</p>\n<p>| <strong>session_check.php</strong> | Check if logged in |</p>\n<p>| <strong>auth.php</strong> | Session authentication |</p>\n<p>| <strong>config.php</strong> | App config constants |</p>\n<p>| <strong>db.php</strong> | Database connection |</p>\n<p>| <strong>log_error.php</strong> | Log client-side error |</p>\n<p>| <strong>todo_list.php</strong> | Todo list management |</p>\n<p>| <strong>send_telegram.php</strong> | Send Telegram messages |</p>\n<h3>4.2 Management API (port 8894 /api/)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>login.php</strong> | Authenticate |</p>\n<p>| <strong>logout.php</strong> | Clear session |</p>\n<p>| <strong>session_check.php</strong> | Check if logged in |</p>\n<p>| <strong>customer_detail.php</strong> | Customer + visits |</p>\n<p>| <strong>search_customers.php</strong> | Search customers |</p>\n<p>| <strong>update_customer.php</strong> | Update customer |</p>\n<p>| <strong>users_list.php</strong> | List all users |</p>\n<p>| <strong>user_save.php</strong> | Create/update user |</p>\n<p>| <strong>list_report_dirs.php</strong> | Date folders |</p>\n<p>| <strong>list_report_files.php</strong> | PDFs in folder |</p>\n<p>| <strong>report_file.php</strong> | Access report file |</p>\n<p>| <strong>pickups_today.php</strong> | Today's pickups |</p>\n<p>| <strong>ready.php</strong> | Ready for pickup |</p>\n<p>| <strong>overdue.php</strong> | Overdue |</p>\n<p>| <strong>settings_get.php</strong> | Get settings |</p>\n<p>| <strong>settings_save.php</strong> | Save settings |</p>\n<p>| <strong>settings_css.php</strong> | Live CSS theme |</p>\n<p>| <strong>imap_list.php</strong> | List IMAP folders |</p>\n<p>| <strong>imap_fetch.php</strong> | Fetch emails |</p>\n<p>| <strong>imap_send.php</strong> | Send email via IMAP |</p>\n<p>| <strong>imap_action.php</strong> | IMAP actions |</p>\n<p>| <strong>sms_send_helper.php</strong> | SMS sending helper |</p>\n<p>| <strong>sms_webhook_receive.php</strong> | Receive SMS webhooks |</p>\n<p>| <strong>sms_inbox_api.php</strong> | SMS inbox API |</p>\n<p>| <strong>slideshow_list.php</strong> | Slideshow images |</p>\n<p>| <strong>slideshow_upload.php</strong> | Upload slideshow |</p>\n<p>| <strong>slideshow_delete.php</strong> | Delete slideshow |</p>\n<p>| <strong>users_list.php</strong> | User management |</p>\n<p>| <strong>report_list.php</strong> | Report list |</p>\n<p>| <strong>debug_filters.php</strong> | Debug filters |</p>\n<p>| <strong>sysadmin.php</strong> | System admin |</p>\n<p>| <strong>test.php</strong> | Testing endpoint |</p>\n<h3>4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>customer.php</strong> | Get customer details |</p>\n<p>| <strong>customers.php</strong> | List/search customers |</p>\n<p>| <strong>customer_save.php</strong> | Save/update customer |</p>\n<p>| <strong>visit.php</strong> | Get visit details |</p>\n<p>| <strong>visit_delete.php</strong> | Delete visit |</p>\n<p>| <strong>appointments.php</strong> | Unified appointments |</p>\n<p>| <strong>appointments_summary.php</strong> | Appointment statistics |</p>\n<p>| <strong>setmore_appointments.php</strong> | Setmore appointments |</p>\n<p>| <strong>setmore_import.php</strong> | Import from Setmore |</p>\n<p>| <strong>module_*.php</strong> | Module loaders |</p>\n<p>| <strong>alteration_items.php</strong> | Alteration management |</p>\n<p>| <strong>alteration_items_summary.php</strong> | Alteration statistics |</p>\n<p>| <strong>gold_silver_*.php</strong> | Gold/silver prices |</p>\n<p>| <strong>bank_*.php</strong> | Bank transactions |</p>\n<p>| <strong>stocks_summary.php</strong> | Stock data |</p>\n<p>| <strong>medication_*.php</strong> | Medication tracking |</p>\n<p>| <strong>payroll_import.php</strong> | Payroll import |</p>\n<p>| <strong>car_maintenance_*.php</strong> | Vehicle maintenance |</p>\n<p>| <strong>duke_*.php</strong> | Duke integration |</p>\n<p>| <strong>reports_*.php</strong> | Report generation |</p>\n<p>| <strong>totals_import.php</strong> | Customer totals import |</p>\n<p>| <strong>documents.php</strong> | Document management |</p>\n<p>| <strong>document_upload.php</strong> | File uploads |</p>\n<p>| <strong>cust_email_*.php</strong> | Email data |</p>\n<p>| <strong>mail_*.php</strong> | Email sending |</p>\n<p>| <strong>reminders_create.php</strong> | Create reminders |</p>\n<p>| <strong>nightly_reports.php</strong> | Nightly reports |</p>\n<p>| <strong>morning_jobs.php</strong> | Morning jobs |</p>\n<p>| <strong>appointment_workflow.php</strong> | Workflow management |</p>\n<p>| <strong>sysadmin.php</strong> | System admin |</p>\n<p>| <strong>reporting_module.php</strong> | Reporting |</p>\n<p>| <strong>job_runs.php</strong> | Job tracking |</p>\n<p>| <strong>health.php</strong> | Health check |</p>\n<p>| <strong>overview.php</strong> | Dashboard data |</p>\n<p>---</p>\n<h2>5. External Integrations</h2>\n<h3>5.1 Square (Payment Processing)</h3>\n<ul>\n<li><strong>Purpose:</strong> Point-of-sale payments, receipts, terminal management</li>\n<li><strong>Features:</strong></li>\n<li>Square Terminal device pairing/management</li>\n<li>Payment processing with tips</li>\n<li>Customer payment logging</li>\n<li>Sandbox and live environments</li>\n<li><strong>Tables:</strong> `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`</li>\n</ul>\n<h3>5.2 Twilio (SMS)</h3>\n<ul>\n<li><strong>Purpose:</strong> SMS notifications and reminders</li>\n<li><strong>Features:</strong></li>\n<li>Send SMS to customers</li>\n<li>Appointment reminders</li>\n<li>SMS consent tracking</li>\n<li><strong>Settings:</strong> `twilio_sid`, `twilio_token`, `twilio_from`</li>\n</ul>\n<h3>5.3 Setmore (Appointment Booking)</h3>\n<ul>\n<li><strong>Purpose:</strong> Appointment synchronization</li>\n<li><strong>Features:</strong></li>\n<li>Pull appointments from Setmore API</li>\n<li>Link to customers/visits</li>\n<li><strong>Table:</strong> `setmore_appointments`</li>\n<li><strong>Settings:</strong> `setmore_refresh_token`, `setmore_staff_key`</li>\n</ul>\n<h3>5.4 EasyAppointments (Alternative Booking)</h3>\n<ul>\n<li><strong>Purpose:</strong> Full appointment booking platform</li>\n<li><strong>Location:</strong> C:\\code\\customerdb\\schedule\\</li>\n<li><strong>Features:</strong></li>\n<li>Customer/provider management</li>\n<li>Web-based booking widget</li>\n<li>REST API</li>\n</ul>\n<h3>5.5 Telegram (Notifications)</h3>\n<ul>\n<li><strong>Purpose:</strong> Bot-based notifications</li>\n<li><strong>Features:</strong></li>\n<li>Send notifications to channels</li>\n<li>Visit alerts</li>\n<li>Cron job status</li>\n<li><strong>Settings:</strong> `telegram_bot_token`, `telegram_chat_id`</li>\n</ul>\n<h3>5.6 Ollama (AI - Optional)</h3>\n<ul>\n<li><strong>Purpose:</strong> Local AI for email generation</li>\n<li><strong>Settings:</strong> `ollama_url`, `ollama_default_model`</li>\n</ul>\n<p>---</p>\n<h2>6. Scheduled Jobs / Cron</h2>\n<h3>Configured Cron Jobs</h3>\n<p>| Job Label | Schedule | Purpose |</p>\n<p>|----------|----------|---------|</p>\n<p>| <strong>Next-Day Customer Emails</strong> | `0 20 <em> </em> *` (8 PM) | Pickup reminder emails |</p>\n<p>| <strong>Morning Import</strong> | `0 6 <em> </em> *` (6 AM) | Morning data import |</p>\n<p>| <strong>Morning Jobs</strong> | `0 9 <em> </em> *` (9 AM) | Appointment sync, workflow |</p>\n<p>| <strong>Nightly Reports</strong> | `0 20 <em> </em> *` (8 PM) | Generate nightly summaries |</p>\n<p>| <strong>Database Backup</strong> | `0 2 <em> </em> *` (2 AM) | Full MariaDB backup |</p>\n<p>| <strong>Daily Cleanup</strong> | `0 4 <em> </em> *` (4 AM) | Temp files, old logs |</p>\n<p>| <strong>Auto Delete</strong> | `15 4 <em> </em> *` (4:15 AM) | Clean placeholder records |</p>\n<h3>Background Job Scripts (backend/jobs/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>run_morning_jobs.php</strong> | Main morning job runner |</p>\n<p>| <strong>smoke_morning_jobs.php</strong> | Smoke test |</p>\n<p>| <strong>send_nightly_reports.php</strong> | Nightly report emailer |</p>\n<p>| <strong>send_nextday_customer_emails.php</strong> | Next-day reminders |</p>\n<p>| <strong>delete_add_name_records.php</strong> | Cleanup job |</p>\n<p>| <strong>rebuild_customer_totals.php</strong> | Rebuild totals |</p>\n<p>| <strong>rebuild_daily_customer_totals.php</strong> | Rebuild daily totals |</p>\n<p>| <strong>rebuild_payment_ledger.php</strong> | Rebuild payment ledger |</p>\n<p>| <strong>import_customer_totals_csv.php</strong> | CSV import |</p>\n<p>| <strong>backfill_square_customer_log.php</strong> | Square log backfill |</p>\n<p>| <strong>run_cron_job.php</strong> | Generic cron runner |</p>\n<p>---</p>\n<h2>7. Web Applications</h2>\n<h3>7.1 Front Desk App (port 8895)</h3>\n<p>| Page | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>index.html</strong> | Main dashboard, search, stats |</p>\n<p>| <strong>customer.html</strong> | Customer detail + visit history |</p>\n<p>| <strong>visit.html</strong> | Create/edit alteration order |</p>\n<p>| <strong>customer_display.html</strong> | Second monitor display |</p>\n<p>| <strong>receipt.html</strong> | Printable receipt |</p>\n<p>| <strong>reports.html</strong> | Order list by status |</p>\n<p>| <strong>upcoming.html</strong> | Next 7 days pickups |</p>\n<p>| <strong>schedule.html</strong> | Appointments calendar |</p>\n<p>| <strong>login.html</strong> | Login page |</p>\n<p>| <strong>item_types.html</strong> | Item type management |</p>\n<h3>7.2 Management App (port 8894)</h3>\n<p>| Page | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>index.html</strong> | Login page |</p>\n<p>| <strong>webui.html</strong> | Dashboard |</p>\n<p>| <strong>customer.html</strong> | Customer detail view |</p>\n<p>| <strong>admin.html</strong> | Reports browser |</p>\n<p>| <strong>settings.html</strong> | App settings editor |</p>\n<p>| <strong>users.html</strong> | User management |</p>\n<p>| <strong>reports.html</strong> | Order reports |</p>\n<p>---</p>\n<h2>8. Settings & Configuration</h2>\n<h3>app_settings Keys</h3>\n<p>| Key | Purpose |</p>\n<p>|-----|---------|</p>\n<p>| theme_bg | Page background color |</p>\n<p>| theme_panel | Card/panel color |</p>\n<p>| theme_text | Text color |</p>\n<p>| theme_accent | Accent/button color |</p>\n<p>| theme_accent_deep | Darker accent |</p>\n<p>| theme_teal | Teal color |</p>\n<p>| theme_font | Font family |</p>\n<p>| smtp_host | Email server hostname |</p>\n<p>| smtp_port | Email server port |</p>\n<p>| smtp_secure | tls or ssl |</p>\n<p>| smtp_user | Email login |</p>\n<p>| smtp_pass | Email password |</p>\n<p>| smtp_from_email | From address |</p>\n<p>| smtp_from_name | From display name |</p>\n<p>| twilio_sid | Twilio Account SID |</p>\n<p>| twilio_token | Twilio Auth Token |</p>\n<p>| twilio_from | Twilio phone number |</p>\n<p>| imap_host | Incoming email server |</p>\n<p>| imap_port | IMAP port |</p>\n<p>| imap_ssl | SSL enable |</p>\n<p>| imap_user | IMAP login |</p>\n<p>| imap_pass | IMAP password |</p>\n<p>| square_token | Square API token |</p>\n<p>| square_location | Square location ID |</p>\n<p>| setmore_staff_key | Setmore API key |</p>\n<p>| setmore_refresh | Setmore refresh token |</p>\n<p>| require_report | PDF required |</p>\n<p>| require_signature | Signature required |</p>\n<p>| reports_base_path | Reports disk path |</p>\n<p>| reports_base_url | Reports URL |</p>\n<p>| ollama_url | Ollama AI URL |</p>\n<p>| ollama_default_model | Ollama model |</p>\n<p>| telegram_bot_token | Telegram bot token |</p>\n<p>| telegram_chat_id | Telegram chat ID |</p>\n<p>---</p>\n<h2>9. Migration Notes</h2>\n<h3>Access to MariaDB Workflow</h3>\n<p>1. <strong>Export source data from Access</strong></p>\n<ul>\n<li>Export ordinary tables to CSV</li>\n<li>Export attachment fields with VBA or DAO Recordset2 logic</li>\n<li>Keep original IDs during migration</li>\n</ul>\n<p>2. <strong>Load into MariaDB staging</strong></p>\n<ul>\n<li>Use `LOAD DATA LOCAL INFILE` for CSV files</li>\n<li>Load signatures separately into `visit_signatures`</li>\n</ul>\n<p>3. <strong>Normalize</strong></p>\n<ul>\n<li>`CustomerTable` → `customers`</li>\n<li>`CustomerVisits` → `visits`</li>\n<li>`AlterationItems` → `orders`, `order_items`, `order_item_type_links`</li>\n<li>`2024TaxPayments` → `tax_payments`</li>\n<li>`AlterationItemsChangeLog` → `change_log`</li>\n</ul>\n<p>4. <strong>Signature BLOB path</strong></p>\n<ul>\n<li>Export Access attachment to temp file</li>\n<li>Convert BMP to PNG</li>\n<li>Insert PNG bytes into `visit_signatures.signature_image`</li>\n</ul>\n<p>5. <strong>Validation</strong></p>\n<ul>\n<li>Compare source row counts vs target</li>\n<li>Compare distinct legacy IDs</li>\n<li>Compare sample signatures by SHA256</li>\n</ul>\n<p>---</p>\n<h2>10. Scripts & Tools</h2>\n<h3>PowerShell Scripts (scripts/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>import_legacy_totals_from_excel.ps1</strong> | Import totals from Excel |</p>\n<p>| <strong>sync_backend_mirror.ps1</strong> | Sync backend mirror |</p>\n<p>| <strong>check_backend_mirror.ps1</strong> | Check mirror status |</p>\n<h3>Python Scripts (scripts/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>signature_blob_loader.py</strong> | Load signature blobs |</p>\n<p>| <strong>access_to_mariadb_parser.py</strong> | Parse Access to MariaDB |</p>\n<p>| <strong>import_access_v2.py</strong> | Import Access v2 |</p>\n<p>| <strong>import_csv_v2.py</strong> | Import CSV v2 |</p>\n<h3>SQL Migrations (migrations/)</h3>\n<p>| File | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>20260407_add_conversion_job.sql</strong> | Add conversion job table |</p>\n<p>| <strong>20260411_order_items_item_type_name_no_fk.sql</strong> | Order items no FK |</p>\n<p>| <strong>20260416_blog_prompt_templates.sql</strong> | Blog prompt templates |</p>\n<p>---</p>\n<h2>File Statistics</h2>\n<ul>\n<li><strong>Total PHP files:</strong> 200+</li>\n<li><strong>Total SQL files:</strong> 100+</li>\n<li><strong>Total HTML pages:</strong> 20+</li>\n<li><strong>Database tables:</strong> 30+</li>\n</ul>\n<p>---</p>\n<p><em>End of Documentation</em></p>","file_name":"COMPREHENSIVE_DOCUMENTATION.md","stored_name":"20260417-160828-6006d4f1.md","mime_type":"application/octet-stream","file_size_bytes":"22313","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-160828-6006d4f1.md","is_deleted":"0","created_at":"2026-04-17 12:08:29","updated_at":"2026-04-17 12:08:52","editor_content":"# Ella's Alterations - Comprehensive System Documentation\r\n\r\n**Generated:** April 17, 2026\r\n**Location:** C:\\code\\customerdb\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n1. [System Overview](#1-system-overview)\r\n2. [Directory Structure](#2-directory-structure)\r\n3. [Database Schema](#3-database-schema)\r\n4. [API Endpoints](#4-api-endpoints)\r\n5. [External Integrations](#5-external-integrations)\r\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\r\n7. [Web Applications](#7-web-applications)\r\n8. [Settings & Configuration](#8-settings--configuration)\r\n9. [Migration Notes](#9-migration-notes)\r\n10. [Scripts & Tools](#10-scripts--tools)\r\n\r\n---\r\n\r\n## 1. System Overview\r\n\r\n### Purpose\r\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\r\n\r\n### Server Information\r\n- **Primary Server:** kefa@192.168.7.202\r\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\r\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\r\n- **Web Server:** Apache 2.4 with mod-php\r\n- **PHP Version:** 8.3\r\n\r\n### Three Web Applications\r\n\r\n| App | Local URL | Public URL | Purpose |\r\n|-----|-----------|------------|---------|\r\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\r\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\r\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\r\n\r\n---\r\n\r\n## 2. Directory Structure\r\n\r\n```\r\ncustomerdb/\r\n├── backend/ # Main PHP backend API and services\r\n│ ├── api/ # REST API endpoints (~57 files)\r\n│ ├── bin/ # Binary/executable scripts\r\n│ ├── backup/ # Backup versions of services\r\n│ └── jobs/ # Background job scripts\r\n├── frontend/ # Front Desk app (port 8895)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\r\n├── webui/ # Management app (port 8894)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ ├── backend/ # Backend API copy\r\n│ └── email/ # Email templates\r\n├── schedule/ # Easy!Appointments installation\r\n├── schema/ # Database schema files\r\n├── migrations/ # SQL migrations\r\n├── scripts/ # PowerShell/Python import scripts\r\n├── docs/ # Documentation\r\n├── blog/ # Blog system and reengineering\r\n├── csv/ # CSV data files\r\n├── backups/ # Backup archives\r\n├── Topaz/ # Topaz signature pad configs\r\n├── webui/ # Second web interface\r\n│ ├── api/\r\n│ ├── backend/\r\n│ └── frontend/\r\n├── notused/ # Deprecated/unused files\r\n└── wordpress-plugin/ # WordPress integration\r\n```\r\n\r\n---\r\n\r\n## 3. Database Schema\r\n\r\n### Core Tables (from mariadb_core_schema.sql)\r\n\r\n#### customers\r\nMain customer registry.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| customer_id | INT UNSIGNED PK | Auto-increment |\r\n| legacy_customer_id | INT NULL | Original Access DB ID |\r\n| created_at | DATETIME | Record creation time |\r\n| full_name | VARCHAR(200) | Customer full name |\r\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\r\n| email | VARCHAR(255) | Email address |\r\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\r\n| description | TEXT | Description |\r\n| notes | TEXT | Internal notes |\r\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\r\n\r\n#### visits\r\nOne row per alteration job/order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| visit_id | INT UNSIGNED PK | |\r\n| legacy_visit_id | INT NULL | Original Access DB ID |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| visit_date | DATETIME | Drop-off date |\r\n| pickup_date | DATETIME | Expected pickup |\r\n| notes | TEXT | General visit notes |\r\n| customer_name_snapshot | VARCHAR(200) | |\r\n| phone_snapshot | VARCHAR(30) | |\r\n| email_snapshot | VARCHAR(255) | |\r\n| color | VARCHAR(100) | Garment color |\r\n| alterations_needed | TEXT | |\r\n| alterations_notes | TEXT | Detailed alteration instructions |\r\n| alterations_price | DECIMAL(10,2) | Manual price field |\r\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\r\n| expedited_fee | DECIMAL(10,2) | |\r\n| paid_date | DATETIME | When paid |\r\n| signature_text | TEXT | Text signature (legacy) |\r\n| signature_date | DATETIME | |\r\n| paid_flag | TINYINT(1) | 1 = paid |\r\n| source_key_value | VARCHAR(100) | |\r\n\r\n#### visit_signatures\r\nFull resolution signature images per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| signature_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNIQUE | One signature per visit |\r\n| signature_date | DATETIME | |\r\n| image_format | VARCHAR(50) | Default 'image/png' |\r\n| original_filename | VARCHAR(255) | |\r\n| original_extension | VARCHAR(20) | |\r\n| file_size_bytes | BIGINT | |\r\n| sha256_hash | CHAR(64) | Hash for verification |\r\n| signature_image | LONGBLOB | Raw PNG binary |\r\n| created_at | DATETIME | |\r\n\r\n#### item_types\r\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| item_type_id | INT UNSIGNED PK | |\r\n| type_name | VARCHAR(100) UNIQUE | Display name |\r\n\r\n#### orders\r\nOrders per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNSIGNED FK | → visits |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| created_at | DATETIME | |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| picked_up | TINYINT(1) | |\r\n| source_table | VARCHAR(50) | |\r\n\r\n#### order_items\r\nLine items for each alteration within a visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| legacy_alteration_id | INT NULL | |\r\n| item_type_id | INT FK | → item_types |\r\n| item_type_name | VARCHAR(100) | |\r\n| description | TEXT | |\r\n| quantity | INT | Default 1 |\r\n| unit_price | DECIMAL(10,2) | |\r\n| total_price | DECIMAL(10,2) | |\r\n| notes | TEXT | Per-item notes |\r\n| color | VARCHAR(100) | Garment color |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| is_range_price | TINYINT(1) | |\r\n| range_price_label | VARCHAR(100) | |\r\n| paid_flag | TINYINT(1) | |\r\n| paid_type | VARCHAR(100) | |\r\n| picked_up | TINYINT(1) | |\r\n| created_at | DATETIME | |\r\n\r\n#### order_item_type_links\r\nMany-to-many links between order items and item types.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\r\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\r\n| item_type_id | INT UNSIGNED FK | → item_types |\r\n| slot_number | TINYINT UNSIGNED | |\r\n\r\n#### payments\r\nPayment records per order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| payment_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n\r\n#### tax_payments\r\nTax payment records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| tax_payment_id | INT UNSIGNED PK | |\r\n| legacy_tax_payment_id | INT NULL | |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n| receipt_blob | LONGBLOB | |\r\n\r\n#### change_log\r\nAudit log for changes.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| log_id | BIGINT UNSIGNED PK | |\r\n| legacy_log_id | INT NULL | |\r\n| table_name | VARCHAR(100) | |\r\n| record_id | BIGINT | |\r\n| visit_id | INT | |\r\n| field_name | VARCHAR(100) | |\r\n| old_value | TEXT | |\r\n| new_value | TEXT | |\r\n| changed_at | DATETIME | |\r\n| changed_by | VARCHAR(100) | |\r\n\r\n#### ads\r\nAdvertising/articles.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| ad_id | INT UNSIGNED PK | |\r\n| legacy_ad_id | INT NULL | |\r\n| article_text | TEXT | |\r\n| article_picture | LONGBLOB | |\r\n| site_submitted | VARCHAR(255) | |\r\n| views | INT | Default 0 |\r\n| date_posted | DATETIME | |\r\n\r\n#### staging_* tables\r\nStaging tables for data migration.\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| staging_customer_table_raw | Staging for legacy customer data |\r\n| staging_customer_visits_raw | Staging for legacy visits |\r\n| staging_alteration_items_raw | Staging for items |\r\n\r\n### Additional Backend Tables\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| setmore_appointments | Synced from Setmore |\r\n| backend_conversion_job | Document conversion jobs |\r\n| backend_job_runs | Background job execution log |\r\n| customer_totals | Daily customer totals |\r\n| daily_customer_totals | Daily summary totals |\r\n| customer_payment_transactions | Payment ledger |\r\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\r\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\r\n| backend_appointment_workflow | Appointment state machine |\r\n| backend_no_show_log | No-show tracking |\r\n| backend_error_log | Error logging |\r\n| backend_change_log | Change audit log |\r\n| backend_document | Document storage |\r\n| backend_document_version | Document version history |\r\n| backend_speed_dial | Quick links |\r\n| backend_square_terminal_code | Square terminal pairing codes |\r\n| backend_square_terminal_device | Square device registry |\r\n| cron_job | Scheduled job definitions |\r\n| cron_job_run | Cron execution history |\r\n| app_settings | Global configuration |\r\n| app_users | Login accounts |\r\n| remember_tokens | \"Remember Me\" tokens |\r\n| customer_reports | PDF report index |\r\n\r\n---\r\n\r\n## 4. API Endpoints\r\n\r\n### 4.1 Frontend API (port 8895 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer_get.php** | Get customer by ID |\r\n| **customer_save.php** | Create or update customer |\r\n| **customer_detail.php** | Customer + visits summary |\r\n| **customer_delete.php** | Delete customer |\r\n| **customer_list_hide.php** | Hide customer from list |\r\n| **search_customers.php** | Search by name/phone |\r\n| **visit_get.php** | Get visit + items |\r\n| **visit_save.php** | Create/update visit + items |\r\n| **visit_delete.php** | Delete visit |\r\n| **visit_report_generate.php** | Generate visit report |\r\n| **item_types.php** | List all alteration types |\r\n| **item_type_add.php** | Add new item type |\r\n| **item_type_update.php** | Update item type |\r\n| **item_type_delete.php** | Delete item type |\r\n| **pickups_today.php** | Orders due today |\r\n| **ready.php** | Orders ready for pickup |\r\n| **overdue.php** | Overdue orders |\r\n| **upcoming.php** | Orders due in next N days |\r\n| **workload_7days.php** | 7-day workload |\r\n| **save_signature.php** | Save canvas signature |\r\n| **reports_list.php** | Reports for customer |\r\n| **attach_reports.php** | Index all PDFs to DB |\r\n| **save_signature.php** | Save signature image |\r\n| **today_appointments_list.php** | Today's appointments |\r\n| **settings_get.php** | Get app settings |\r\n| **settings_save.php** | Save app settings |\r\n| **settings_css.php** | Live theme CSS |\r\n| **send_email.php** | Send email via SMTP |\r\n| **send_sms.php** | Send SMS via Twilio |\r\n| **send_reminders.php** | Send appointment reminders |\r\n| **ea_appointments.php** | EasyAppointments REST API |\r\n| **ea_webhook.php** | Receives EA webhook events |\r\n| **setmore.php** | Setmore API proxy |\r\n| **setmore_to_ea_import.php** | Import customers to EA |\r\n| **square_config.php** | Square public config |\r\n| **square_process.php** | Process Square payment |\r\n| **square_customer_log.php** | Customer Square log |\r\n| **square_terminal.php** | Square terminal |\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **auth.php** | Session authentication |\r\n| **config.php** | App config constants |\r\n| **db.php** | Database connection |\r\n| **log_error.php** | Log client-side error |\r\n| **todo_list.php** | Todo list management |\r\n| **send_telegram.php** | Send Telegram messages |\r\n\r\n### 4.2 Management API (port 8894 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **customer_detail.php** | Customer + visits |\r\n| **search_customers.php** | Search customers |\r\n| **update_customer.php** | Update customer |\r\n| **users_list.php** | List all users |\r\n| **user_save.php** | Create/update user |\r\n| **list_report_dirs.php** | Date folders |\r\n| **list_report_files.php** | PDFs in folder |\r\n| **report_file.php** | Access report file |\r\n| **pickups_today.php** | Today's pickups |\r\n| **ready.php** | Ready for pickup |\r\n| **overdue.php** | Overdue |\r\n| **settings_get.php** | Get settings |\r\n| **settings_save.php** | Save settings |\r\n| **settings_css.php** | Live CSS theme |\r\n| **imap_list.php** | List IMAP folders |\r\n| **imap_fetch.php** | Fetch emails |\r\n| **imap_send.php** | Send email via IMAP |\r\n| **imap_action.php** | IMAP actions |\r\n| **sms_send_helper.php** | SMS sending helper |\r\n| **sms_webhook_receive.php** | Receive SMS webhooks |\r\n| **sms_inbox_api.php** | SMS inbox API |\r\n| **slideshow_list.php** | Slideshow images |\r\n| **slideshow_upload.php** | Upload slideshow |\r\n| **slideshow_delete.php** | Delete slideshow |\r\n| **users_list.php** | User management |\r\n| **report_list.php** | Report list |\r\n| **debug_filters.php** | Debug filters |\r\n| **sysadmin.php** | System admin |\r\n| **test.php** | Testing endpoint |\r\n\r\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer.php** | Get customer details |\r\n| **customers.php** | List/search customers |\r\n| **customer_save.php** | Save/update customer |\r\n| **visit.php** | Get visit details |\r\n| **visit_delete.php** | Delete visit |\r\n| **appointments.php** | Unified appointments |\r\n| **appointments_summary.php** | Appointment statistics |\r\n| **setmore_appointments.php** | Setmore appointments |\r\n| **setmore_import.php** | Import from Setmore |\r\n| **module_*.php** | Module loaders |\r\n| **alteration_items.php** | Alteration management |\r\n| **alteration_items_summary.php** | Alteration statistics |\r\n| **gold_silver_*.php** | Gold/silver prices |\r\n| **bank_*.php** | Bank transactions |\r\n| **stocks_summary.php** | Stock data |\r\n| **medication_*.php** | Medication tracking |\r\n| **payroll_import.php** | Payroll import |\r\n| **car_maintenance_*.php** | Vehicle maintenance |\r\n| **duke_*.php** | Duke integration |\r\n| **reports_*.php** | Report generation |\r\n| **totals_import.php** | Customer totals import |\r\n| **documents.php** | Document management |\r\n| **document_upload.php** | File uploads |\r\n| **cust_email_*.php** | Email data |\r\n| **mail_*.php** | Email sending |\r\n| **reminders_create.php** | Create reminders |\r\n| **nightly_reports.php** | Nightly reports |\r\n| **morning_jobs.php** | Morning jobs |\r\n| **appointment_workflow.php** | Workflow management |\r\n| **sysadmin.php** | System admin |\r\n| **reporting_module.php** | Reporting |\r\n| **job_runs.php** | Job tracking |\r\n| **health.php** | Health check |\r\n| **overview.php** | Dashboard data |\r\n\r\n---\r\n\r\n## 5. External Integrations\r\n\r\n### 5.1 Square (Payment Processing)\r\n- **Purpose:** Point-of-sale payments, receipts, terminal management\r\n- **Features:**\r\n - Square Terminal device pairing/management\r\n - Payment processing with tips\r\n - Customer payment logging\r\n - Sandbox and live environments\r\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\r\n\r\n### 5.2 Twilio (SMS)\r\n- **Purpose:** SMS notifications and reminders\r\n- **Features:**\r\n - Send SMS to customers\r\n - Appointment reminders\r\n - SMS consent tracking\r\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\r\n\r\n### 5.3 Setmore (Appointment Booking)\r\n- **Purpose:** Appointment synchronization\r\n- **Features:**\r\n - Pull appointments from Setmore API\r\n - Link to customers/visits\r\n- **Table:** `setmore_appointments`\r\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\r\n\r\n### 5.4 EasyAppointments (Alternative Booking)\r\n- **Purpose:** Full appointment booking platform\r\n- **Location:** C:\\code\\customerdb\\schedule\\\r\n- **Features:**\r\n - Customer/provider management\r\n - Web-based booking widget\r\n - REST API\r\n\r\n### 5.5 Telegram (Notifications)\r\n- **Purpose:** Bot-based notifications\r\n- **Features:**\r\n - Send notifications to channels\r\n - Visit alerts\r\n - Cron job status\r\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\r\n\r\n### 5.6 Ollama (AI - Optional)\r\n- **Purpose:** Local AI for email generation\r\n- **Settings:** `ollama_url`, `ollama_default_model`\r\n\r\n---\r\n\r\n## 6. Scheduled Jobs / Cron\r\n\r\n### Configured Cron Jobs\r\n\r\n| Job Label | Schedule | Purpose |\r\n|----------|----------|---------|\r\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\r\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\r\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\r\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\r\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\r\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\r\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\r\n\r\n### Background Job Scripts (backend/jobs/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **run_morning_jobs.php** | Main morning job runner |\r\n| **smoke_morning_jobs.php** | Smoke test |\r\n| **send_nightly_reports.php** | Nightly report emailer |\r\n| **send_nextday_customer_emails.php** | Next-day reminders |\r\n| **delete_add_name_records.php** | Cleanup job |\r\n| **rebuild_customer_totals.php** | Rebuild totals |\r\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\r\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\r\n| **import_customer_totals_csv.php** | CSV import |\r\n| **backfill_square_customer_log.php** | Square log backfill |\r\n| **run_cron_job.php** | Generic cron runner |\r\n\r\n---\r\n\r\n## 7. Web Applications\r\n\r\n### 7.1 Front Desk App (port 8895)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Main dashboard, search, stats |\r\n| **customer.html** | Customer detail + visit history |\r\n| **visit.html** | Create/edit alteration order |\r\n| **customer_display.html** | Second monitor display |\r\n| **receipt.html** | Printable receipt |\r\n| **reports.html** | Order list by status |\r\n| **upcoming.html** | Next 7 days pickups |\r\n| **schedule.html** | Appointments calendar |\r\n| **login.html** | Login page |\r\n| **item_types.html** | Item type management |\r\n\r\n### 7.2 Management App (port 8894)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Login page |\r\n| **webui.html** | Dashboard |\r\n| **customer.html** | Customer detail view |\r\n| **admin.html** | Reports browser |\r\n| **settings.html** | App settings editor |\r\n| **users.html** | User management |\r\n| **reports.html** | Order reports |\r\n\r\n---\r\n\r\n## 8. Settings & Configuration\r\n\r\n### app_settings Keys\r\n\r\n| Key | Purpose |\r\n|-----|---------|\r\n| theme_bg | Page background color |\r\n| theme_panel | Card/panel color |\r\n| theme_text | Text color |\r\n| theme_accent | Accent/button color |\r\n| theme_accent_deep | Darker accent |\r\n| theme_teal | Teal color |\r\n| theme_font | Font family |\r\n| smtp_host | Email server hostname |\r\n| smtp_port | Email server port |\r\n| smtp_secure | tls or ssl |\r\n| smtp_user | Email login |\r\n| smtp_pass | Email password |\r\n| smtp_from_email | From address |\r\n| smtp_from_name | From display name |\r\n| twilio_sid | Twilio Account SID |\r\n| twilio_token | Twilio Auth Token |\r\n| twilio_from | Twilio phone number |\r\n| imap_host | Incoming email server |\r\n| imap_port | IMAP port |\r\n| imap_ssl | SSL enable |\r\n| imap_user | IMAP login |\r\n| imap_pass | IMAP password |\r\n| square_token | Square API token |\r\n| square_location | Square location ID |\r\n| setmore_staff_key | Setmore API key |\r\n| setmore_refresh | Setmore refresh token |\r\n| require_report | PDF required |\r\n| require_signature | Signature required |\r\n| reports_base_path | Reports disk path |\r\n| reports_base_url | Reports URL |\r\n| ollama_url | Ollama AI URL |\r\n| ollama_default_model | Ollama model |\r\n| telegram_bot_token | Telegram bot token |\r\n| telegram_chat_id | Telegram chat ID |\r\n\r\n---\r\n\r\n## 9. Migration Notes\r\n\r\n### Access to MariaDB Workflow\r\n\r\n1. **Export source data from Access**\r\n - Export ordinary tables to CSV\r\n - Export attachment fields with VBA or DAO Recordset2 logic\r\n - Keep original IDs during migration\r\n\r\n2. **Load into MariaDB staging**\r\n - Use `LOAD DATA LOCAL INFILE` for CSV files\r\n - Load signatures separately into `visit_signatures`\r\n\r\n3. **Normalize**\r\n - `CustomerTable` → `customers`\r\n - `CustomerVisits` → `visits`\r\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\r\n - `2024TaxPayments` → `tax_payments`\r\n - `AlterationItemsChangeLog` → `change_log`\r\n\r\n4. **Signature BLOB path**\r\n - Export Access attachment to temp file\r\n - Convert BMP to PNG\r\n - Insert PNG bytes into `visit_signatures.signature_image`\r\n\r\n5. **Validation**\r\n - Compare source row counts vs target\r\n - Compare distinct legacy IDs\r\n - Compare sample signatures by SHA256\r\n\r\n---\r\n\r\n## 10. Scripts & Tools\r\n\r\n### PowerShell Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\r\n| **sync_backend_mirror.ps1** | Sync backend mirror |\r\n| **check_backend_mirror.ps1** | Check mirror status |\r\n\r\n### Python Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **signature_blob_loader.py** | Load signature blobs |\r\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\r\n| **import_access_v2.py** | Import Access v2 |\r\n| **import_csv_v2.py** | Import CSV v2 |\r\n\r\n### SQL Migrations (migrations/)\r\n\r\n| File | Purpose |\r\n|------|---------|\r\n| **20260407_add_conversion_job.sql** | Add conversion job table |\r\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\r\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\r\n\r\n---\r\n\r\n## File Statistics\r\n\r\n- **Total PHP files:** 200+\r\n- **Total SQL files:** 100+\r\n- **Total HTML pages:** 20+\r\n- **Database tables:** 30+\r\n\r\n---\r\n\r\n*End of Documentation*","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"20","document_type":"upload","title":"Speed Dial Contents","slug":"speed-dial-contents","summary_text":"Speed Dial Contents","content_markdown":"Here are the names and URLs extracted from the provided bookmarks list:\r\n\r\nGTE Mobile Banking: https://m.gtefcu.org/mobile/Default.aspx?ReturnUrl=%2fmobile%2fMember%2fAccounts%2fTransactionHistory.aspx%3fR6ldQ2Rt%252bjUGc0%252f3%252fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%253d-YuyNrzQIfzg%253d&R6ldQ2Rt%2bjUGc0%2f3%2fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%3d-YuyNrzQIfzg%3d\r\n\r\nView All Photos | 100 Easy Chicken Recipes | Cooking Light: http://www.cookinglight.com/food/quick-healthy/easy-chicken-recipes/view-all\r\n\r\nYahoo! Mail: https://m.mg.mail.yahoo.com/hg/?.intl=us\r\n\r\nWeather Station History | Weather Underground: http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=ITARLACC2\r\n\r\nAbout Shalom Sesame – Shalom Sesame: http://www.shalomsesame.org/about2.html\r\n\r\nAMAZING FACTS: http://www.amazingfacts.org/store/entity/tabid/267/entityname/category/categoryid/126/sename/character-studies/default.aspx\r\n\r\nAncient Hebrew Research Center - Home Page: http://www.ancient-hebrew.org/\r\n\r\nAnthem BlueCross BlueShield - Verizon Home: http://www.anthem.com/verizon/\r\n\r\nAtheist Central -- Ray Comfort's Blog: http://raycomfortfood.blogspot.com/\r\n\r\nAudio Lessons « Think Hebrew: http://thinkhebrew.wordpress.com/audio-lessons/\r\n\r\nBarnes & Noble.com - eBooks Search Orthodox Jewish bible: http://productsearch.barnesandnoble.com/search/results.aspx?store=EBOOK&WRD=Orthodox+Jewish+bible\r\n\r\nBeginning Hebrew Class Tim Hegg: http://www.torahresource.com/Hebrew.html\r\n\r\nBehind The Scenes- 5-9-11: http://www.livingwaters.com/index.php?option=com_k2&view=item&id=2753&Itemid=314&lang=en\r\n\r\nBeit Tehila Congregation - Shalom: http://www.topraise.net/pages/about-us/history.htm\r\n\r\nBeth Messiah Messianic Synagogue Sarasota, Florida USA Rabbi Judah Hungerman: http://www.bethmessiahfla.com/\r\n\r\nBible and Mishneh Torah for All - Jews and Gentiles - Mechon Mamre: http://www.mechon-mamre.org/\r\n\r\nbible passages: http://www.openbible.info/topics/eating_fish\r\n\r\nBible University: http://www.bibleuniversity.com/University/Welcome.asp\r\n\r\nBiblical Hebrew Blog - By eTeacherBiblical.com Learn Biblical Hebrew online: http://blog.eteacherbiblical.com/\r\n\r\nBiblical Hermeneutics BiblicalTraining.org: http://www.biblicaltraining.org/biblical-hermeneutics/robert-stein\r\n\r\nBiblicalDiscipleship.org: http://biblicaldiscipleship.org/\r\n\r\nBirth Issues Yeshua in Context: http://yeshuaincontext.com/2011/03/birth-issues/\r\n\r\nBlogEngine.NET: http://blogengine.codeplex.com/discussions?prog_id=register_cheap_domain_names_cheap_web_hosting&ProjectName=blogengine\r\n\r\nBukharin Yarmulke Pattern k2p2.net Blog: http://k2p2.net/blog2/2006/08/31/bukharin-yarmulke-pattern/\r\n\r\nBuy Jewish Gifts Online - Jewelry - Kippah - Mezuzah - Star of David - Kabbalah Red String Bracelets - Evil Eye Charm: http://www.yourholylandstore.com/\r\n\r\nChabad Jewish Holidays: http://www.chabad.org/library/article_cdo/aid/102698/jewish/Tevet-10.htm\r\n\r\nCHANUKAH ...History & Prophetic End Time Implications: http://www.graftedforlife.org/blogs/index.php/2010/12/03/chanukah-history-aamp-prophetic-end-time-implications?blog=9\r\n\r\nChofetz Chaim Laws of Speech Naaleh Torah Video Online: http://www.naaleh.com/search/class/148\r\n\r\nContact Us: http://www.chabadcountyline.com/index.php?option=com_facileforms&act=quickmode&task=edit&formName=ContactUs&Itemid=152\r\n\r\nCount Today's Omer - Sefirat HaOmer: http://www.chabad.org/holidays/sefirah/omer-count.asp?date=4/19/2011\r\n\r\nCredit Bureau Reporting: http://www.thelpa.com/lpa/credit-reporting.html\r\n\r\nDeuteronomy 1622 Bible Lexicon: http://biblelexicon.org/deuteronomy/16-22.htm\r\n\r\nDidache: http://www.earlychristianwritings.com/didache.html\r\n\r\ndirectoryBedini SG - PESWiki Tesla Generator: http://peswiki.com/energy/Directory:Bedini_SG\r\n\r\nDr. Janice Hughes, Clinical Social Work-Therapist, Cape Coral, FL 33914 Psychology Today's Therapy Directory: http://therapists.psychologytoday.com/rms/prof_detail.php?profid=73368&sid=1281461131.0405_1063&city=Fort+Myers&county=Lee&state=Florida&spec=166\r\n\r\nD'var Torah on the Parashah - Torah Portion of the Week: http://www.artscroll.com/parashah/current.html\r\n\r\nEarly Christian Writings New Testament, Apocrypha, Gnostics, Church Fathers: http://www.earlychristianwritings.com/index.html\r\n\r\nEarly Jewish Writings: http://www.earlyjewishwritings.com/\r\n\r\nEasiest Online Backup Service - Backblaze: http://www.backblaze.com/\r\n\r\nebay Seller Tools File Exchange: http://pages.ebay.com/file_exchange/\r\n\r\nEssays and Articles: http://therefinersfire.org/ea.htm\r\n\r\nEssenes, Essene Teachings and Essene Theology: http://www.thenazareneway.com/\r\n\r\nFilipino Audio Lessons: http://mylanguages.org/filipino_audio.php\r\n\r\nFirm Foundation Guitar Tabs, Chords & Lyrics by Jamie Harvill & Nancy Gordon: http://www.worshiparchive.com/song/firm-foundation\r\n\r\nFirst Fruits Core [Beta]: https://ffoz.org/members/member_cp/index.php\r\n\r\nFlorida Lotto: http://www.flalottery.com/inet/lottoMain.do\r\n\r\nFoundationStone - a free and easy way to learn Hebrew: http://foundationstone.com.au/FoundationStone.html?./OnlineHebrewTutorial.html\r\n\r\nFree Lessons Learn Biblical Hebrew with eTeacher: http://eteacherbiblical.com/free-lessons\r\n\r\nFree Online Hebrew Dictionary. Type in Hebrew-English. Translate Hebrew or Phonetic Hebrew.: http://www.doitinhebrew.com/Translate/default.aspx?kb=IL%20Hebrew%20Phonetic\r\n\r\nFront Page www.jewishpresstampa.com Jewish Press of Tampa: http://www.jewishpresstampa.com/\r\n\r\nG-DCAST: http://www.g-dcast.com/\r\n\r\nGoogle Translate: http://translate.google.com/#\r\n\r\nHanukkah - Chanukah 2010 - Menorah, Dreidels, Latkes, Recipes, Games and more: http://www.chabad.org/holidays/chanukah/default_cdo/jewish/Hanukkah.htm\r\n\r\nHebrew Lessons — Ten Commandments: http://www.levitt.com/hebrew/commandments.html\r\n\r\nHome Yarnell: http://www.yarnellschool.com/online/yarnell/index.html\r\n\r\nInformation Technology Students and Faculty: http://msdn01.e-academy.com/elms/Storefront/Home.aspx?campus=sfc_it\r\n\r\nIntroduction to Islam BiblicalTraining.org: http://www.biblicaltraining.org/introduction-islam/timothy-tennent\r\n\r\niTanakh: http://www.itanakh.org/\r\n\r\nJewish Community, Jewish Synagogue, Kosher Food, Makati, Philippines: http://jewishphilippines.net/newsletter.php#\r\n\r\nJewish Genealogy By Genetics - JewishGen: http://www.jewishgen.org/dna/genbygen.html\r\n\r\nJewish Roots Learning Center Haydid: http://www.haydid.org/\r\n\r\nJewish Studies Org: http://www.jewishstudies.org/\r\n\r\nJourney To Jerusalem Home Page: http://www.journeytojerusalem.com/\r\n\r\nJudaism and Jewish Resources - Andrew Tannenbaum: http://shamash.org/trb/judaism.html#learning\r\n\r\nJVIMS Courses: http://www.jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64&vmcchk=1&Itemid=64\r\n\r\nJVIMS Rabbi Courses: http://jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64\r\n\r\nKidsAstronomy.com Online Astronomy Academy: http://www.kidsastronomy.com/academy/index.htm\r\n\r\nLearn Hebrew and Jewish prayer with our user friendly Software, Book and Audio tutorials and products: http://www.hebrewworld.com/specialOffer.html\r\n\r\nLighting Up Chanukah: http://www.betemunah.org/chanukah.html\r\n\r\nListen to Prayers Bet Am Shalom - A Reconstructionist Synagogue, White Plains, NY: http://betamshalom.org/prayers\r\n\r\nLiturgy: http://www.cyber-synagogue.com/Liturgy.htm\r\n\r\nMahal - Volunteer for the IDF - Join the Israeli Army: http://www.mahal-idf-volunteers.org/support/linkus.htm\r\n\r\nMessianic Jewish Rabbinical Council: http://ourrabbis.org/main/\r\n\r\nMessianic Jewish Radio - San Francisco, CA - Listen Online: http://radiotime.com/station/s_111593/Messianic_Jewish_Radio.aspx\r\n\r\nMessianic Symbol Hebraic Roots Banner-Red: https://www.storesonline.com/site/742858/product/SFA-32133\r\n\r\nMoney Transfer - Send Money Online: https://www.xoom.com/sendmoneynow/home2/security\r\n\r\nMount Olive Press: http://mountolivepress.com/Mount_Olive_Press/Home.html\r\n\r\nMovie Showtimes and Tickets for 33982 - Yahoo! Movies: http://movies.yahoo.com/showtimes-tickets/?location=33982&r=sim\r\n\r\nMy Account Login: https://idp.securepaynet.net/login.aspx?isc=wwbb53&prog%5Fid=register%5Fcheap%5Fdomain%5Fnames%5Fcheap%5Fweb%5Fhosting&spkey=SPSWB166&login=9492585&target=myportal%2Fdomainren%2Easp\r\n\r\nMy order history BiblicalTraining.org: http://www.biblicaltraining.org/user/39652/orders\r\n\r\nMy Wish List Decorative Painting Bookstore: http://www.decorativepaintingbookstore.com/mm5/merchant.mvc?\r\n\r\nNazarene Media - Home: http://www.nazarenemedia.net/\r\n\r\nNazerine books and school: http://stores.lulu.com/nazarene\r\n\r\nNot Subject to the Law of God: http://www.yashanet.com/library/underlaw.htm\r\n\r\nOf His Kingdom There Shall Be No End Mashiyach Mashiyach Mashiyach: http://www.mashiyach.com/index.htm\r\n\r\nohr chadash: http://www.ohrchadash.org/contact.htm\r\n\r\nOld Testament Studies: http://www.torahclass.com/old-testament-studies\r\n\r\nOperation Wake Up Call - Nazarene Space: http://nazarenespace.com/group/operationwakeupcall\r\n\r\norderConfirmation: https://www.rewardsuite.com/rewardsuite/Catalog/View/orderConfirmation.aspx?i=dzOj5blTL2o%3d\r\n\r\npage 3: http://www.globalprebuilder.com/page3-v2.php\r\n\r\nPasco County Library System Program Home Page: http://evanced.info/pasco/sr/homepage.asp?ProgramID=27\r\n\r\nPeshitta.org • View forum - Nazarene-Messianic Judaism Forum: http://www.peshitta.org/forum/viewforum.php?f=6\r\n\r\nPrinciples of Christian Dream Interpretation: http://www.cwgministries.org/Principles-of-Christian-Dream-Interpretation\r\n\r\nprophecyrevealed - I Have A Friend Who's Jewish Book: http://prophecyrevealed.com/\r\n\r\nPsalms 23 - Hebrew - English Bible - Mechon-Mamre: http://www.mechon-mamre.org/p/pt/pt2623.htm\r\n\r\nRabbi Eric Levy and Family: http://www.ericlevy.com/\r\n\r\nRevelation 11-6: http://www.yashanet.com/studies/revstudy/text/r1_1-6.htm\r\n\r\nRevelation Study Text Index: http://www.yashanet.com/studies/revstudy/text/index.htm\r\n\r\nRick Recht Online Store -: http://store.jewishrockrecords.com/store/listings.cfm?cat=12\r\n\r\nSAP Support Portal - Homepage: https://websmp202.sap-ag.de/support\r\n\r\nSchool of Biblical Evangelism: http://www.biblicalevangelism.com/register\r\n\r\nShaarei halachah a summary of laws for Jewish living Online Free Books: http://online-free-books.com/title/Txt4EidAVFEC.html\r\n\r\nShoresh David Messianic Jewish Synagogues (Tampa, Lakeland, & Wesley Chapel, FL): http://www.shoreshdavid.org/\r\n\r\nSilver Jerusalem design Hand Embroidered Hat: http://www.ajudaica.com/item/1494_324/Silver+Jerusalem+design+Hand+Embroidered+Hat\r\n\r\nSmile of a Child TV -- Television Program Schedule: http://www.smileofachildtv.org/watch/schedule_weekview.php\r\n\r\nSounds of Shalom: http://www.soundsofshalom.com/\r\n\r\nSubscription Donations; Messianic Christianity: http://www.messianicchristianity.org/subscription_donations.html\r\n\r\nTable of Contents - Torah 101 - Mechon Mamre: http://www.mechon-mamre.org/jewfaq/index.htm\r\n\r\nTanach Index Hebrew with Vowels: http://www.sacred-texts.com/bib/tan/index.htm\r\n\r\nTemple Aron Hakodesh: http://www.tak.org/services.htm\r\n\r\nTemple Israel NH » Prayer Recordings: http://www.templeisraelnh.org/education/prayer-recordings/\r\n\r\nTES - Largest Distributor of Jewish Software, Hebrew Language and Bible Software (0): http://www.jewishsoftware.com/default.asp?page=category&id=4\r\n\r\nTexts of Judaism: http://www.sacred-texts.com/jud/\r\n\r\nThe Aramaic origin of the Fourth gospel - Google Books: http://books.google.com/ebooks/reader?id=2WhAAAAAIAAJ&printsec=frontcover&output=reader\r\n\r\nThe Book of Zohar Books Michael Laitman Kabbalah Library - Bnei Baruch Kabbalah Education & Research Institute: http://www.kabbalah.info/eng/content/view/frame/2842?/eng/content/view/full_list/2842&main\r\n\r\nThe Evolution of a Creationist Jobe Martin Creation vs. Evolution: http://evolutionofacreationist.com/\r\n\r\nThe Foreign Service of the Philippines - Manila Forwarders: http://www.manilaforwarder.com/immigrantvisa_pg2.html\r\n\r\nThe Lotto Black Book! Secrets Exposed...: http://lottoblackbooks.co.cc/aff.php\r\n\r\nThe Nazarite Site: http://www.nazarite.net/home.html\r\n\r\nThe Poetry of Genesis Chapter One: http://www.ancient-hebrew.org/23_genesis_1.html\r\n\r\nThe Revelation of Y'shua HaMoshiach Thus Say The Prophets!: http://jewtoo.org/prophecy/\r\n\r\nThe Shul of the Web: http://www.heartofisrael.net/\r\n\r\nThe Worldwide Nazarene Assembly of Elohim - Directory of WNAE Assemblies: http://www.wnae.org/assemblies.htm\r\n\r\nTHE WORSHIP OF IMAGES catholism: http://www.justforcatholics.org/images.htm\r\n\r\nTim Hegg Shabbat Shalom Page: http://www.torahresource.com/\r\n\r\nTorah (Teachings) of Yahshua Messiah (Jesus, Yeshua) - rebuking the Trinity and Lawlessness: http://www.torahofmessiah.com/trinity_art.html\r\n\r\nTorah Institute FREE PDF DOWNLOADS: http://www.torahzone.net/FREE-PDF-DOWNLOADS/?page=3\r\n\r\ntorah institute: http://www.fossilizedcustoms.com/\r\n\r\nTorah on the Web - Virtual Beit Midrash - Torah: http://www.vbm-torah.org/moadim.html\r\n\r\nTorah on the Web - Virtual Beit Midrash: http://vbm-torah.org/\r\n\r\nTotShabbat.com: http://www.totshabbat.com/\r\n\r\nTwebrew School: http://www.twebrewschool.org/\r\n\r\nTwinkle, twinkle little star: http://www.storytimesongs.com/guitarchords/Twinkle.html\r\n\r\nVictorious Homeschoolers: https://www.homeschool-life.com/sysfiles/member/index_public.cfm?memberid=693\r\n\r\nVirtual Jerusalem-The Place Where Jews Click: http://www.virtualjerusalem.com/\r\n\r\nvirtualyeshiva.com Torah Learning on the Net: http://www.virtualyeshiva.com/\r\n\r\nWalking in Torah Teachings Hebrew Nation: http://walkingintorah.com/Teachings.html\r\n\r\nway of the master radio: http://www.oneplace.com/ministries/way-of-the-master/\r\n\r\nWearing the Tallit Katan: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Tallit_Katan/tallit_katan.html\r\n\r\nWelcome kabbalah course: http://edu.kabbalah.info/group/lc/home\r\n\r\nWelcome to biblediagrams.com: http://biblediagrams.com/diagrams/index.htm\r\n\r\nWelcome to MessianicJewish.net: http://www.messianicjewish.net/\r\n\r\nWelcome to the JewToo.org Blog!: http://jewtoo.multiply.com/\r\n\r\nWelcome to your Tampa Jewish Community Online!: http://www.jewishtampa.com/calendar/index.aspx?startdate=3/9/2011%206:30:00%20PM&enddate=3/9/2011%207:45:00%20PM&id=20&eventid=510947&recurrenceid=11922\r\n\r\nWINK News: http://www.winknews.com/weather/doppler\r\n\r\nWitnessing Resources -: http://www.nazarenemedia.net/witnessing-resources.html\r\n\r\nworship song guitar chords: http://www.worshiparchive.com/\r\n\r\nwww.wstw.fm listen: http://www.wstw.fm/listen%20live.html\r\n\r\nYashaNet HomePage: http://www.yashanet.com/index.htm\r\n\r\nYeshiva Beth HaShem Bible 101: http://yeshivabethhashem.org/yeshiva/1lesson4.html\r\n\r\nYotzer Hame'orot - the Morning Blessing: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Yotzer_Hame_orot/yotzer_hame_orot.html\r\n\r\nThe Straight Way Ministry Home Page: http://thestraightway.org/\r\n\r\nifcj Journey Through the Scriptures: http://www.ifcj.org/site/PageNavigator/eng/jtts\r\n\r\nTanya - The Book of Tanya Translated into English - Text of the Tanya: http://www.chabad.org/library/tanya/tanya_cdo/aid/1028862/jewish/Tanya.htm\r\n\r\nJewish Holidays, Torah, Jewish Jobs, Tuition Affordability, Kosher Certification, Jewish Teens: http://www.ou.org/\r\n\r\nEsynagogue.org - Home: http://www.esynagogue.org/\r\n\r\nKeep It!: javascript:(a%20=%20(b%20=%20document).createElement('script')).src%20=%20'http://keepvid.com/js/bm.js',%20b.body.appendChild(a);void(0;\r\n\r\nwww.VirtualCantor.com: http://virtualcantor.com/CDOrder.htm\r\n\r\nTRI Classes: http://www.triclasses.com/\r\n\r\nAggressive Christianity Library: http://www.aggressivechristianity.net/library.htm\r\n\r\nDictionary of the Targumim, the Talmud Babli and Yerushalmi, and the Midrashic Literature by Marcus Jastrow: [suspicious link removed]\r\n\r\nHalakhah.com Babylonian Talmud Online in English: http://halakhah.com/\r\n\r\nKhabouris Codex Aramaic Peshitta New Testament Manuscript: http://www.aramaicpeshitta.com/AramaicNTtools/khabouris.htm\r\n\r\nJacob Richman's Hot Sites - Jewish - Learn Hebrew Sites: http://www.jr.co.il/hotsites/j-hebrew.htm\r\n\r\nIndex for Part 1 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index1.html\r\n\r\nIndex for Part 2 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index2.html\r\n\r\nBiblia Hebraica (Biblical Hebrew - Home): http://www.bible101.org/hebrew/home.html","content_html":"<p>Here are the names and URLs extracted from the provided bookmarks list:</p>\n<p>GTE Mobile Banking: https://m.gtefcu.org/mobile/Default.aspx?ReturnUrl=%2fmobile%2fMember%2fAccounts%2fTransactionHistory.aspx%3fR6ldQ2Rt%252bjUGc0%252f3%252fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%253d-YuyNrzQIfzg%253d&R6ldQ2Rt%2bjUGc0%2f3%2fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%3d-YuyNrzQIfzg%3d</p>\n<p>View All Photos | 100 Easy Chicken Recipes | Cooking Light: http://www.cookinglight.com/food/quick-healthy/easy-chicken-recipes/view-all</p>\n<p>Yahoo! Mail: https://m.mg.mail.yahoo.com/hg/?.intl=us</p>\n<p>Weather Station History | Weather Underground: http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=ITARLACC2</p>\n<p>About Shalom Sesame – Shalom Sesame: http://www.shalomsesame.org/about2.html</p>\n<p>AMAZING FACTS: http://www.amazingfacts.org/store/entity/tabid/267/entityname/category/categoryid/126/sename/character-studies/default.aspx</p>\n<p>Ancient Hebrew Research Center - Home Page: http://www.ancient-hebrew.org/</p>\n<p>Anthem BlueCross BlueShield - Verizon Home: http://www.anthem.com/verizon/</p>\n<p>Atheist Central -- Ray Comfort's Blog: http://raycomfortfood.blogspot.com/</p>\n<p>Audio Lessons « Think Hebrew: http://thinkhebrew.wordpress.com/audio-lessons/</p>\n<p>Barnes & Noble.com - eBooks Search Orthodox Jewish bible: http://productsearch.barnesandnoble.com/search/results.aspx?store=EBOOK&WRD=Orthodox+Jewish+bible</p>\n<p>Beginning Hebrew Class Tim Hegg: http://www.torahresource.com/Hebrew.html</p>\n<p>Behind The Scenes- 5-9-11: http://www.livingwaters.com/index.php?option=com_k2&view=item&id=2753&Itemid=314&lang=en</p>\n<p>Beit Tehila Congregation - Shalom: http://www.topraise.net/pages/about-us/history.htm</p>\n<p>Beth Messiah Messianic Synagogue Sarasota, Florida USA Rabbi Judah Hungerman: http://www.bethmessiahfla.com/</p>\n<p>Bible and Mishneh Torah for All - Jews and Gentiles - Mechon Mamre: http://www.mechon-mamre.org/</p>\n<p>bible passages: http://www.openbible.info/topics/eating_fish</p>\n<p>Bible University: http://www.bibleuniversity.com/University/Welcome.asp</p>\n<p>Biblical Hebrew Blog - By eTeacherBiblical.com Learn Biblical Hebrew online: http://blog.eteacherbiblical.com/</p>\n<p>Biblical Hermeneutics BiblicalTraining.org: http://www.biblicaltraining.org/biblical-hermeneutics/robert-stein</p>\n<p>BiblicalDiscipleship.org: http://biblicaldiscipleship.org/</p>\n<p>Birth Issues Yeshua in Context: http://yeshuaincontext.com/2011/03/birth-issues/</p>\n<p>BlogEngine.NET: http://blogengine.codeplex.com/discussions?prog_id=register_cheap_domain_names_cheap_web_hosting&ProjectName=blogengine</p>\n<p>Bukharin Yarmulke Pattern k2p2.net Blog: http://k2p2.net/blog2/2006/08/31/bukharin-yarmulke-pattern/</p>\n<p>Buy Jewish Gifts Online - Jewelry - Kippah - Mezuzah - Star of David - Kabbalah Red String Bracelets - Evil Eye Charm: http://www.yourholylandstore.com/</p>\n<p>Chabad Jewish Holidays: http://www.chabad.org/library/article_cdo/aid/102698/jewish/Tevet-10.htm</p>\n<p>CHANUKAH ...History & Prophetic End Time Implications: http://www.graftedforlife.org/blogs/index.php/2010/12/03/chanukah-history-aamp-prophetic-end-time-implications?blog=9</p>\n<p>Chofetz Chaim Laws of Speech Naaleh Torah Video Online: http://www.naaleh.com/search/class/148</p>\n<p>Contact Us: http://www.chabadcountyline.com/index.php?option=com_facileforms&act=quickmode&task=edit&formName=ContactUs&Itemid=152</p>\n<p>Count Today's Omer - Sefirat HaOmer: http://www.chabad.org/holidays/sefirah/omer-count.asp?date=4/19/2011</p>\n<p>Credit Bureau Reporting: http://www.thelpa.com/lpa/credit-reporting.html</p>\n<p>Deuteronomy 1622 Bible Lexicon: http://biblelexicon.org/deuteronomy/16-22.htm</p>\n<p>Didache: http://www.earlychristianwritings.com/didache.html</p>\n<p>directoryBedini SG - PESWiki Tesla Generator: http://peswiki.com/energy/Directory:Bedini_SG</p>\n<p>Dr. Janice Hughes, Clinical Social Work-Therapist, Cape Coral, FL 33914 Psychology Today's Therapy Directory: http://therapists.psychologytoday.com/rms/prof_detail.php?profid=73368&sid=1281461131.0405_1063&city=Fort+Myers&county=Lee&state=Florida&spec=166</p>\n<p>D'var Torah on the Parashah - Torah Portion of the Week: http://www.artscroll.com/parashah/current.html</p>\n<p>Early Christian Writings New Testament, Apocrypha, Gnostics, Church Fathers: http://www.earlychristianwritings.com/index.html</p>\n<p>Early Jewish Writings: http://www.earlyjewishwritings.com/</p>\n<p>Easiest Online Backup Service - Backblaze: http://www.backblaze.com/</p>\n<p>ebay Seller Tools File Exchange: http://pages.ebay.com/file_exchange/</p>\n<p>Essays and Articles: http://therefinersfire.org/ea.htm</p>\n<p>Essenes, Essene Teachings and Essene Theology: http://www.thenazareneway.com/</p>\n<p>Filipino Audio Lessons: http://mylanguages.org/filipino_audio.php</p>\n<p>Firm Foundation Guitar Tabs, Chords & Lyrics by Jamie Harvill & Nancy Gordon: http://www.worshiparchive.com/song/firm-foundation</p>\n<p>First Fruits Core [Beta]: https://ffoz.org/members/member_cp/index.php</p>\n<p>Florida Lotto: http://www.flalottery.com/inet/lottoMain.do</p>\n<p>FoundationStone - a free and easy way to learn Hebrew: http://foundationstone.com.au/FoundationStone.html?./OnlineHebrewTutorial.html</p>\n<p>Free Lessons Learn Biblical Hebrew with eTeacher: http://eteacherbiblical.com/free-lessons</p>\n<p>Free Online Hebrew Dictionary. Type in Hebrew-English. Translate Hebrew or Phonetic Hebrew.: http://www.doitinhebrew.com/Translate/default.aspx?kb=IL%20Hebrew%20Phonetic</p>\n<p>Front Page www.jewishpresstampa.com Jewish Press of Tampa: http://www.jewishpresstampa.com/</p>\n<p>G-DCAST: http://www.g-dcast.com/</p>\n<p>Google Translate: http://translate.google.com/#</p>\n<p>Hanukkah - Chanukah 2010 - Menorah, Dreidels, Latkes, Recipes, Games and more: http://www.chabad.org/holidays/chanukah/default_cdo/jewish/Hanukkah.htm</p>\n<p>Hebrew Lessons — Ten Commandments: http://www.levitt.com/hebrew/commandments.html</p>\n<p>Home Yarnell: http://www.yarnellschool.com/online/yarnell/index.html</p>\n<p>Information Technology Students and Faculty: http://msdn01.e-academy.com/elms/Storefront/Home.aspx?campus=sfc_it</p>\n<p>Introduction to Islam BiblicalTraining.org: http://www.biblicaltraining.org/introduction-islam/timothy-tennent</p>\n<p>iTanakh: http://www.itanakh.org/</p>\n<p>Jewish Community, Jewish Synagogue, Kosher Food, Makati, Philippines: http://jewishphilippines.net/newsletter.php#</p>\n<p>Jewish Genealogy By Genetics - JewishGen: http://www.jewishgen.org/dna/genbygen.html</p>\n<p>Jewish Roots Learning Center Haydid: http://www.haydid.org/</p>\n<p>Jewish Studies Org: http://www.jewishstudies.org/</p>\n<p>Journey To Jerusalem Home Page: http://www.journeytojerusalem.com/</p>\n<p>Judaism and Jewish Resources - Andrew Tannenbaum: http://shamash.org/trb/judaism.html#learning</p>\n<p>JVIMS Courses: http://www.jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64&vmcchk=1&Itemid=64</p>\n<p>JVIMS Rabbi Courses: http://jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64</p>\n<p>KidsAstronomy.com Online Astronomy Academy: http://www.kidsastronomy.com/academy/index.htm</p>\n<p>Learn Hebrew and Jewish prayer with our user friendly Software, Book and Audio tutorials and products: http://www.hebrewworld.com/specialOffer.html</p>\n<p>Lighting Up Chanukah: http://www.betemunah.org/chanukah.html</p>\n<p>Listen to Prayers Bet Am Shalom - A Reconstructionist Synagogue, White Plains, NY: http://betamshalom.org/prayers</p>\n<p>Liturgy: http://www.cyber-synagogue.com/Liturgy.htm</p>\n<p>Mahal - Volunteer for the IDF - Join the Israeli Army: http://www.mahal-idf-volunteers.org/support/linkus.htm</p>\n<p>Messianic Jewish Rabbinical Council: http://ourrabbis.org/main/</p>\n<p>Messianic Jewish Radio - San Francisco, CA - Listen Online: http://radiotime.com/station/s_111593/Messianic_Jewish_Radio.aspx</p>\n<p>Messianic Symbol Hebraic Roots Banner-Red: https://www.storesonline.com/site/742858/product/SFA-32133</p>\n<p>Money Transfer - Send Money Online: https://www.xoom.com/sendmoneynow/home2/security</p>\n<p>Mount Olive Press: http://mountolivepress.com/Mount_Olive_Press/Home.html</p>\n<p>Movie Showtimes and Tickets for 33982 - Yahoo! Movies: http://movies.yahoo.com/showtimes-tickets/?location=33982&r=sim</p>\n<p>My Account Login: https://idp.securepaynet.net/login.aspx?isc=wwbb53&prog%5Fid=register%5Fcheap%5Fdomain%5Fnames%5Fcheap%5Fweb%5Fhosting&spkey=SPSWB166&login=9492585&target=myportal%2Fdomainren%2Easp</p>\n<p>My order history BiblicalTraining.org: http://www.biblicaltraining.org/user/39652/orders</p>\n<p>My Wish List Decorative Painting Bookstore: http://www.decorativepaintingbookstore.com/mm5/merchant.mvc?</p>\n<p>Nazarene Media - Home: http://www.nazarenemedia.net/</p>\n<p>Nazerine books and school: http://stores.lulu.com/nazarene</p>\n<p>Not Subject to the Law of God: http://www.yashanet.com/library/underlaw.htm</p>\n<p>Of His Kingdom There Shall Be No End Mashiyach Mashiyach Mashiyach: http://www.mashiyach.com/index.htm</p>\n<p>ohr chadash: http://www.ohrchadash.org/contact.htm</p>\n<p>Old Testament Studies: http://www.torahclass.com/old-testament-studies</p>\n<p>Operation Wake Up Call - Nazarene Space: http://nazarenespace.com/group/operationwakeupcall</p>\n<p>orderConfirmation: https://www.rewardsuite.com/rewardsuite/Catalog/View/orderConfirmation.aspx?i=dzOj5blTL2o%3d</p>\n<p>page 3: http://www.globalprebuilder.com/page3-v2.php</p>\n<p>Pasco County Library System Program Home Page: http://evanced.info/pasco/sr/homepage.asp?ProgramID=27</p>\n<p>Peshitta.org • View forum - Nazarene-Messianic Judaism Forum: http://www.peshitta.org/forum/viewforum.php?f=6</p>\n<p>Principles of Christian Dream Interpretation: http://www.cwgministries.org/Principles-of-Christian-Dream-Interpretation</p>\n<p>prophecyrevealed - I Have A Friend Who's Jewish Book: http://prophecyrevealed.com/</p>\n<p>Psalms 23 - Hebrew - English Bible - Mechon-Mamre: http://www.mechon-mamre.org/p/pt/pt2623.htm</p>\n<p>Rabbi Eric Levy and Family: http://www.ericlevy.com/</p>\n<p>Revelation 11-6: http://www.yashanet.com/studies/revstudy/text/r1_1-6.htm</p>\n<p>Revelation Study Text Index: http://www.yashanet.com/studies/revstudy/text/index.htm</p>\n<p>Rick Recht Online Store -: http://store.jewishrockrecords.com/store/listings.cfm?cat=12</p>\n<p>SAP Support Portal - Homepage: https://websmp202.sap-ag.de/support</p>\n<p>School of Biblical Evangelism: http://www.biblicalevangelism.com/register</p>\n<p>Shaarei halachah a summary of laws for Jewish living Online Free Books: http://online-free-books.com/title/Txt4EidAVFEC.html</p>\n<p>Shoresh David Messianic Jewish Synagogues (Tampa, Lakeland, & Wesley Chapel, FL): http://www.shoreshdavid.org/</p>\n<p>Silver Jerusalem design Hand Embroidered Hat: http://www.ajudaica.com/item/1494_324/Silver+Jerusalem+design+Hand+Embroidered+Hat</p>\n<p>Smile of a Child TV -- Television Program Schedule: http://www.smileofachildtv.org/watch/schedule_weekview.php</p>\n<p>Sounds of Shalom: http://www.soundsofshalom.com/</p>\n<p>Subscription Donations; Messianic Christianity: http://www.messianicchristianity.org/subscription_donations.html</p>\n<p>Table of Contents - Torah 101 - Mechon Mamre: http://www.mechon-mamre.org/jewfaq/index.htm</p>\n<p>Tanach Index Hebrew with Vowels: http://www.sacred-texts.com/bib/tan/index.htm</p>\n<p>Temple Aron Hakodesh: http://www.tak.org/services.htm</p>\n<p>Temple Israel NH » Prayer Recordings: http://www.templeisraelnh.org/education/prayer-recordings/</p>\n<p>TES - Largest Distributor of Jewish Software, Hebrew Language and Bible Software (0): http://www.jewishsoftware.com/default.asp?page=category&id=4</p>\n<p>Texts of Judaism: http://www.sacred-texts.com/jud/</p>\n<p>The Aramaic origin of the Fourth gospel - Google Books: http://books.google.com/ebooks/reader?id=2WhAAAAAIAAJ&printsec=frontcover&output=reader</p>\n<p>The Book of Zohar Books Michael Laitman Kabbalah Library - Bnei Baruch Kabbalah Education & Research Institute: http://www.kabbalah.info/eng/content/view/frame/2842?/eng/content/view/full_list/2842&main</p>\n<p>The Evolution of a Creationist Jobe Martin Creation vs. Evolution: http://evolutionofacreationist.com/</p>\n<p>The Foreign Service of the Philippines - Manila Forwarders: http://www.manilaforwarder.com/immigrantvisa_pg2.html</p>\n<p>The Lotto Black Book! Secrets Exposed...: http://lottoblackbooks.co.cc/aff.php</p>\n<p>The Nazarite Site: http://www.nazarite.net/home.html</p>\n<p>The Poetry of Genesis Chapter One: http://www.ancient-hebrew.org/23_genesis_1.html</p>\n<p>The Revelation of Y'shua HaMoshiach Thus Say The Prophets!: http://jewtoo.org/prophecy/</p>\n<p>The Shul of the Web: http://www.heartofisrael.net/</p>\n<p>The Worldwide Nazarene Assembly of Elohim - Directory of WNAE Assemblies: http://www.wnae.org/assemblies.htm</p>\n<p>THE WORSHIP OF IMAGES catholism: http://www.justforcatholics.org/images.htm</p>\n<p>Tim Hegg Shabbat Shalom Page: http://www.torahresource.com/</p>\n<p>Torah (Teachings) of Yahshua Messiah (Jesus, Yeshua) - rebuking the Trinity and Lawlessness: http://www.torahofmessiah.com/trinity_art.html</p>\n<p>Torah Institute FREE PDF DOWNLOADS: http://www.torahzone.net/FREE-PDF-DOWNLOADS/?page=3</p>\n<p>torah institute: http://www.fossilizedcustoms.com/</p>\n<p>Torah on the Web - Virtual Beit Midrash - Torah: http://www.vbm-torah.org/moadim.html</p>\n<p>Torah on the Web - Virtual Beit Midrash: http://vbm-torah.org/</p>\n<p>TotShabbat.com: http://www.totshabbat.com/</p>\n<p>Twebrew School: http://www.twebrewschool.org/</p>\n<p>Twinkle, twinkle little star: http://www.storytimesongs.com/guitarchords/Twinkle.html</p>\n<p>Victorious Homeschoolers: https://www.homeschool-life.com/sysfiles/member/index_public.cfm?memberid=693</p>\n<p>Virtual Jerusalem-The Place Where Jews Click: http://www.virtualjerusalem.com/</p>\n<p>virtualyeshiva.com Torah Learning on the Net: http://www.virtualyeshiva.com/</p>\n<p>Walking in Torah Teachings Hebrew Nation: http://walkingintorah.com/Teachings.html</p>\n<p>way of the master radio: http://www.oneplace.com/ministries/way-of-the-master/</p>\n<p>Wearing the Tallit Katan: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Tallit_Katan/tallit_katan.html</p>\n<p>Welcome kabbalah course: http://edu.kabbalah.info/group/lc/home</p>\n<p>Welcome to biblediagrams.com: http://biblediagrams.com/diagrams/index.htm</p>\n<p>Welcome to MessianicJewish.net: http://www.messianicjewish.net/</p>\n<p>Welcome to the JewToo.org Blog!: http://jewtoo.multiply.com/</p>\n<p>Welcome to your Tampa Jewish Community Online!: http://www.jewishtampa.com/calendar/index.aspx?startdate=3/9/2011%206:30:00%20PM&enddate=3/9/2011%207:45:00%20PM&id=20&eventid=510947&recurrenceid=11922</p>\n<p>WINK News: http://www.winknews.com/weather/doppler</p>\n<p>Witnessing Resources -: http://www.nazarenemedia.net/witnessing-resources.html</p>\n<p>worship song guitar chords: http://www.worshiparchive.com/</p>\n<p>www.wstw.fm listen: http://www.wstw.fm/listen%20live.html</p>\n<p>YashaNet HomePage: http://www.yashanet.com/index.htm</p>\n<p>Yeshiva Beth HaShem Bible 101: http://yeshivabethhashem.org/yeshiva/1lesson4.html</p>\n<p>Yotzer Hame'orot - the Morning Blessing: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Yotzer_Hame_orot/yotzer_hame_orot.html</p>\n<p>The Straight Way Ministry Home Page: http://thestraightway.org/</p>\n<p>ifcj Journey Through the Scriptures: http://www.ifcj.org/site/PageNavigator/eng/jtts</p>\n<p>Tanya - The Book of Tanya Translated into English - Text of the Tanya: http://www.chabad.org/library/tanya/tanya_cdo/aid/1028862/jewish/Tanya.htm</p>\n<p>Jewish Holidays, Torah, Jewish Jobs, Tuition Affordability, Kosher Certification, Jewish Teens: http://www.ou.org/</p>\n<p>Esynagogue.org - Home: http://www.esynagogue.org/</p>\n<p>Keep It!: javascript:(a%20=%20(b%20=%20document).createElement('script')).src%20=%20'http://keepvid.com/js/bm.js',%20b.body.appendChild(a);void(0;</p>\n<p>www.VirtualCantor.com: http://virtualcantor.com/CDOrder.htm</p>\n<p>TRI Classes: http://www.triclasses.com/</p>\n<p>Aggressive Christianity Library: http://www.aggressivechristianity.net/library.htm</p>\n<p>Dictionary of the Targumim, the Talmud Babli and Yerushalmi, and the Midrashic Literature by Marcus Jastrow: [suspicious link removed]</p>\n<p>Halakhah.com Babylonian Talmud Online in English: http://halakhah.com/</p>\n<p>Khabouris Codex Aramaic Peshitta New Testament Manuscript: http://www.aramaicpeshitta.com/AramaicNTtools/khabouris.htm</p>\n<p>Jacob Richman's Hot Sites - Jewish - Learn Hebrew Sites: http://www.jr.co.il/hotsites/j-hebrew.htm</p>\n<p>Index for Part 1 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index1.html</p>\n<p>Index for Part 2 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index2.html</p>\n<p>Biblia Hebraica (Biblical Hebrew - Home): http://www.bible101.org/hebrew/home.html</p>","file_name":"COMPREHENSIVE_DOCUMENTATION.md","stored_name":"20260417-160828-6006d4f1.md","mime_type":"application/octet-stream","file_size_bytes":"22313","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-160828-6006d4f1.md","is_deleted":"0","created_at":"2026-04-17 12:08:29","updated_at":"2026-04-17 14:50:34","editor_content":"Here are the names and URLs extracted from the provided bookmarks list:\r\n\r\nGTE Mobile Banking: https://m.gtefcu.org/mobile/Default.aspx?ReturnUrl=%2fmobile%2fMember%2fAccounts%2fTransactionHistory.aspx%3fR6ldQ2Rt%252bjUGc0%252f3%252fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%253d-YuyNrzQIfzg%253d&R6ldQ2Rt%2bjUGc0%2f3%2fhP9UxU2hEVocAsOAVJ4EPdcd9Hh0BsxuR0ilPHrPQzwSXJWzbVCFr3txWVrhtMn9YIzTE6DpzA4jgy3BwTNCXbCmuc%3d-YuyNrzQIfzg%3d\r\n\r\nView All Photos | 100 Easy Chicken Recipes | Cooking Light: http://www.cookinglight.com/food/quick-healthy/easy-chicken-recipes/view-all\r\n\r\nYahoo! Mail: https://m.mg.mail.yahoo.com/hg/?.intl=us\r\n\r\nWeather Station History | Weather Underground: http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=ITARLACC2\r\n\r\nAbout Shalom Sesame – Shalom Sesame: http://www.shalomsesame.org/about2.html\r\n\r\nAMAZING FACTS: http://www.amazingfacts.org/store/entity/tabid/267/entityname/category/categoryid/126/sename/character-studies/default.aspx\r\n\r\nAncient Hebrew Research Center - Home Page: http://www.ancient-hebrew.org/\r\n\r\nAnthem BlueCross BlueShield - Verizon Home: http://www.anthem.com/verizon/\r\n\r\nAtheist Central -- Ray Comfort's Blog: http://raycomfortfood.blogspot.com/\r\n\r\nAudio Lessons « Think Hebrew: http://thinkhebrew.wordpress.com/audio-lessons/\r\n\r\nBarnes & Noble.com - eBooks Search Orthodox Jewish bible: http://productsearch.barnesandnoble.com/search/results.aspx?store=EBOOK&WRD=Orthodox+Jewish+bible\r\n\r\nBeginning Hebrew Class Tim Hegg: http://www.torahresource.com/Hebrew.html\r\n\r\nBehind The Scenes- 5-9-11: http://www.livingwaters.com/index.php?option=com_k2&view=item&id=2753&Itemid=314&lang=en\r\n\r\nBeit Tehila Congregation - Shalom: http://www.topraise.net/pages/about-us/history.htm\r\n\r\nBeth Messiah Messianic Synagogue Sarasota, Florida USA Rabbi Judah Hungerman: http://www.bethmessiahfla.com/\r\n\r\nBible and Mishneh Torah for All - Jews and Gentiles - Mechon Mamre: http://www.mechon-mamre.org/\r\n\r\nbible passages: http://www.openbible.info/topics/eating_fish\r\n\r\nBible University: http://www.bibleuniversity.com/University/Welcome.asp\r\n\r\nBiblical Hebrew Blog - By eTeacherBiblical.com Learn Biblical Hebrew online: http://blog.eteacherbiblical.com/\r\n\r\nBiblical Hermeneutics BiblicalTraining.org: http://www.biblicaltraining.org/biblical-hermeneutics/robert-stein\r\n\r\nBiblicalDiscipleship.org: http://biblicaldiscipleship.org/\r\n\r\nBirth Issues Yeshua in Context: http://yeshuaincontext.com/2011/03/birth-issues/\r\n\r\nBlogEngine.NET: http://blogengine.codeplex.com/discussions?prog_id=register_cheap_domain_names_cheap_web_hosting&ProjectName=blogengine\r\n\r\nBukharin Yarmulke Pattern k2p2.net Blog: http://k2p2.net/blog2/2006/08/31/bukharin-yarmulke-pattern/\r\n\r\nBuy Jewish Gifts Online - Jewelry - Kippah - Mezuzah - Star of David - Kabbalah Red String Bracelets - Evil Eye Charm: http://www.yourholylandstore.com/\r\n\r\nChabad Jewish Holidays: http://www.chabad.org/library/article_cdo/aid/102698/jewish/Tevet-10.htm\r\n\r\nCHANUKAH ...History & Prophetic End Time Implications: http://www.graftedforlife.org/blogs/index.php/2010/12/03/chanukah-history-aamp-prophetic-end-time-implications?blog=9\r\n\r\nChofetz Chaim Laws of Speech Naaleh Torah Video Online: http://www.naaleh.com/search/class/148\r\n\r\nContact Us: http://www.chabadcountyline.com/index.php?option=com_facileforms&act=quickmode&task=edit&formName=ContactUs&Itemid=152\r\n\r\nCount Today's Omer - Sefirat HaOmer: http://www.chabad.org/holidays/sefirah/omer-count.asp?date=4/19/2011\r\n\r\nCredit Bureau Reporting: http://www.thelpa.com/lpa/credit-reporting.html\r\n\r\nDeuteronomy 1622 Bible Lexicon: http://biblelexicon.org/deuteronomy/16-22.htm\r\n\r\nDidache: http://www.earlychristianwritings.com/didache.html\r\n\r\ndirectoryBedini SG - PESWiki Tesla Generator: http://peswiki.com/energy/Directory:Bedini_SG\r\n\r\nDr. Janice Hughes, Clinical Social Work-Therapist, Cape Coral, FL 33914 Psychology Today's Therapy Directory: http://therapists.psychologytoday.com/rms/prof_detail.php?profid=73368&sid=1281461131.0405_1063&city=Fort+Myers&county=Lee&state=Florida&spec=166\r\n\r\nD'var Torah on the Parashah - Torah Portion of the Week: http://www.artscroll.com/parashah/current.html\r\n\r\nEarly Christian Writings New Testament, Apocrypha, Gnostics, Church Fathers: http://www.earlychristianwritings.com/index.html\r\n\r\nEarly Jewish Writings: http://www.earlyjewishwritings.com/\r\n\r\nEasiest Online Backup Service - Backblaze: http://www.backblaze.com/\r\n\r\nebay Seller Tools File Exchange: http://pages.ebay.com/file_exchange/\r\n\r\nEssays and Articles: http://therefinersfire.org/ea.htm\r\n\r\nEssenes, Essene Teachings and Essene Theology: http://www.thenazareneway.com/\r\n\r\nFilipino Audio Lessons: http://mylanguages.org/filipino_audio.php\r\n\r\nFirm Foundation Guitar Tabs, Chords & Lyrics by Jamie Harvill & Nancy Gordon: http://www.worshiparchive.com/song/firm-foundation\r\n\r\nFirst Fruits Core [Beta]: https://ffoz.org/members/member_cp/index.php\r\n\r\nFlorida Lotto: http://www.flalottery.com/inet/lottoMain.do\r\n\r\nFoundationStone - a free and easy way to learn Hebrew: http://foundationstone.com.au/FoundationStone.html?./OnlineHebrewTutorial.html\r\n\r\nFree Lessons Learn Biblical Hebrew with eTeacher: http://eteacherbiblical.com/free-lessons\r\n\r\nFree Online Hebrew Dictionary. Type in Hebrew-English. Translate Hebrew or Phonetic Hebrew.: http://www.doitinhebrew.com/Translate/default.aspx?kb=IL%20Hebrew%20Phonetic\r\n\r\nFront Page www.jewishpresstampa.com Jewish Press of Tampa: http://www.jewishpresstampa.com/\r\n\r\nG-DCAST: http://www.g-dcast.com/\r\n\r\nGoogle Translate: http://translate.google.com/#\r\n\r\nHanukkah - Chanukah 2010 - Menorah, Dreidels, Latkes, Recipes, Games and more: http://www.chabad.org/holidays/chanukah/default_cdo/jewish/Hanukkah.htm\r\n\r\nHebrew Lessons — Ten Commandments: http://www.levitt.com/hebrew/commandments.html\r\n\r\nHome Yarnell: http://www.yarnellschool.com/online/yarnell/index.html\r\n\r\nInformation Technology Students and Faculty: http://msdn01.e-academy.com/elms/Storefront/Home.aspx?campus=sfc_it\r\n\r\nIntroduction to Islam BiblicalTraining.org: http://www.biblicaltraining.org/introduction-islam/timothy-tennent\r\n\r\niTanakh: http://www.itanakh.org/\r\n\r\nJewish Community, Jewish Synagogue, Kosher Food, Makati, Philippines: http://jewishphilippines.net/newsletter.php#\r\n\r\nJewish Genealogy By Genetics - JewishGen: http://www.jewishgen.org/dna/genbygen.html\r\n\r\nJewish Roots Learning Center Haydid: http://www.haydid.org/\r\n\r\nJewish Studies Org: http://www.jewishstudies.org/\r\n\r\nJourney To Jerusalem Home Page: http://www.journeytojerusalem.com/\r\n\r\nJudaism and Jewish Resources - Andrew Tannenbaum: http://shamash.org/trb/judaism.html#learning\r\n\r\nJVIMS Courses: http://www.jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64&vmcchk=1&Itemid=64\r\n\r\nJVIMS Rabbi Courses: http://jvims.com/index.php?page=shop.browse&category_id=6&option=com_virtuemart&Itemid=64\r\n\r\nKidsAstronomy.com Online Astronomy Academy: http://www.kidsastronomy.com/academy/index.htm\r\n\r\nLearn Hebrew and Jewish prayer with our user friendly Software, Book and Audio tutorials and products: http://www.hebrewworld.com/specialOffer.html\r\n\r\nLighting Up Chanukah: http://www.betemunah.org/chanukah.html\r\n\r\nListen to Prayers Bet Am Shalom - A Reconstructionist Synagogue, White Plains, NY: http://betamshalom.org/prayers\r\n\r\nLiturgy: http://www.cyber-synagogue.com/Liturgy.htm\r\n\r\nMahal - Volunteer for the IDF - Join the Israeli Army: http://www.mahal-idf-volunteers.org/support/linkus.htm\r\n\r\nMessianic Jewish Rabbinical Council: http://ourrabbis.org/main/\r\n\r\nMessianic Jewish Radio - San Francisco, CA - Listen Online: http://radiotime.com/station/s_111593/Messianic_Jewish_Radio.aspx\r\n\r\nMessianic Symbol Hebraic Roots Banner-Red: https://www.storesonline.com/site/742858/product/SFA-32133\r\n\r\nMoney Transfer - Send Money Online: https://www.xoom.com/sendmoneynow/home2/security\r\n\r\nMount Olive Press: http://mountolivepress.com/Mount_Olive_Press/Home.html\r\n\r\nMovie Showtimes and Tickets for 33982 - Yahoo! Movies: http://movies.yahoo.com/showtimes-tickets/?location=33982&r=sim\r\n\r\nMy Account Login: https://idp.securepaynet.net/login.aspx?isc=wwbb53&prog%5Fid=register%5Fcheap%5Fdomain%5Fnames%5Fcheap%5Fweb%5Fhosting&spkey=SPSWB166&login=9492585&target=myportal%2Fdomainren%2Easp\r\n\r\nMy order history BiblicalTraining.org: http://www.biblicaltraining.org/user/39652/orders\r\n\r\nMy Wish List Decorative Painting Bookstore: http://www.decorativepaintingbookstore.com/mm5/merchant.mvc?\r\n\r\nNazarene Media - Home: http://www.nazarenemedia.net/\r\n\r\nNazerine books and school: http://stores.lulu.com/nazarene\r\n\r\nNot Subject to the Law of God: http://www.yashanet.com/library/underlaw.htm\r\n\r\nOf His Kingdom There Shall Be No End Mashiyach Mashiyach Mashiyach: http://www.mashiyach.com/index.htm\r\n\r\nohr chadash: http://www.ohrchadash.org/contact.htm\r\n\r\nOld Testament Studies: http://www.torahclass.com/old-testament-studies\r\n\r\nOperation Wake Up Call - Nazarene Space: http://nazarenespace.com/group/operationwakeupcall\r\n\r\norderConfirmation: https://www.rewardsuite.com/rewardsuite/Catalog/View/orderConfirmation.aspx?i=dzOj5blTL2o%3d\r\n\r\npage 3: http://www.globalprebuilder.com/page3-v2.php\r\n\r\nPasco County Library System Program Home Page: http://evanced.info/pasco/sr/homepage.asp?ProgramID=27\r\n\r\nPeshitta.org • View forum - Nazarene-Messianic Judaism Forum: http://www.peshitta.org/forum/viewforum.php?f=6\r\n\r\nPrinciples of Christian Dream Interpretation: http://www.cwgministries.org/Principles-of-Christian-Dream-Interpretation\r\n\r\nprophecyrevealed - I Have A Friend Who's Jewish Book: http://prophecyrevealed.com/\r\n\r\nPsalms 23 - Hebrew - English Bible - Mechon-Mamre: http://www.mechon-mamre.org/p/pt/pt2623.htm\r\n\r\nRabbi Eric Levy and Family: http://www.ericlevy.com/\r\n\r\nRevelation 11-6: http://www.yashanet.com/studies/revstudy/text/r1_1-6.htm\r\n\r\nRevelation Study Text Index: http://www.yashanet.com/studies/revstudy/text/index.htm\r\n\r\nRick Recht Online Store -: http://store.jewishrockrecords.com/store/listings.cfm?cat=12\r\n\r\nSAP Support Portal - Homepage: https://websmp202.sap-ag.de/support\r\n\r\nSchool of Biblical Evangelism: http://www.biblicalevangelism.com/register\r\n\r\nShaarei halachah a summary of laws for Jewish living Online Free Books: http://online-free-books.com/title/Txt4EidAVFEC.html\r\n\r\nShoresh David Messianic Jewish Synagogues (Tampa, Lakeland, & Wesley Chapel, FL): http://www.shoreshdavid.org/\r\n\r\nSilver Jerusalem design Hand Embroidered Hat: http://www.ajudaica.com/item/1494_324/Silver+Jerusalem+design+Hand+Embroidered+Hat\r\n\r\nSmile of a Child TV -- Television Program Schedule: http://www.smileofachildtv.org/watch/schedule_weekview.php\r\n\r\nSounds of Shalom: http://www.soundsofshalom.com/\r\n\r\nSubscription Donations; Messianic Christianity: http://www.messianicchristianity.org/subscription_donations.html\r\n\r\nTable of Contents - Torah 101 - Mechon Mamre: http://www.mechon-mamre.org/jewfaq/index.htm\r\n\r\nTanach Index Hebrew with Vowels: http://www.sacred-texts.com/bib/tan/index.htm\r\n\r\nTemple Aron Hakodesh: http://www.tak.org/services.htm\r\n\r\nTemple Israel NH » Prayer Recordings: http://www.templeisraelnh.org/education/prayer-recordings/\r\n\r\nTES - Largest Distributor of Jewish Software, Hebrew Language and Bible Software (0): http://www.jewishsoftware.com/default.asp?page=category&id=4\r\n\r\nTexts of Judaism: http://www.sacred-texts.com/jud/\r\n\r\nThe Aramaic origin of the Fourth gospel - Google Books: http://books.google.com/ebooks/reader?id=2WhAAAAAIAAJ&printsec=frontcover&output=reader\r\n\r\nThe Book of Zohar Books Michael Laitman Kabbalah Library - Bnei Baruch Kabbalah Education & Research Institute: http://www.kabbalah.info/eng/content/view/frame/2842?/eng/content/view/full_list/2842&main\r\n\r\nThe Evolution of a Creationist Jobe Martin Creation vs. Evolution: http://evolutionofacreationist.com/\r\n\r\nThe Foreign Service of the Philippines - Manila Forwarders: http://www.manilaforwarder.com/immigrantvisa_pg2.html\r\n\r\nThe Lotto Black Book! Secrets Exposed...: http://lottoblackbooks.co.cc/aff.php\r\n\r\nThe Nazarite Site: http://www.nazarite.net/home.html\r\n\r\nThe Poetry of Genesis Chapter One: http://www.ancient-hebrew.org/23_genesis_1.html\r\n\r\nThe Revelation of Y'shua HaMoshiach Thus Say The Prophets!: http://jewtoo.org/prophecy/\r\n\r\nThe Shul of the Web: http://www.heartofisrael.net/\r\n\r\nThe Worldwide Nazarene Assembly of Elohim - Directory of WNAE Assemblies: http://www.wnae.org/assemblies.htm\r\n\r\nTHE WORSHIP OF IMAGES catholism: http://www.justforcatholics.org/images.htm\r\n\r\nTim Hegg Shabbat Shalom Page: http://www.torahresource.com/\r\n\r\nTorah (Teachings) of Yahshua Messiah (Jesus, Yeshua) - rebuking the Trinity and Lawlessness: http://www.torahofmessiah.com/trinity_art.html\r\n\r\nTorah Institute FREE PDF DOWNLOADS: http://www.torahzone.net/FREE-PDF-DOWNLOADS/?page=3\r\n\r\ntorah institute: http://www.fossilizedcustoms.com/\r\n\r\nTorah on the Web - Virtual Beit Midrash - Torah: http://www.vbm-torah.org/moadim.html\r\n\r\nTorah on the Web - Virtual Beit Midrash: http://vbm-torah.org/\r\n\r\nTotShabbat.com: http://www.totshabbat.com/\r\n\r\nTwebrew School: http://www.twebrewschool.org/\r\n\r\nTwinkle, twinkle little star: http://www.storytimesongs.com/guitarchords/Twinkle.html\r\n\r\nVictorious Homeschoolers: https://www.homeschool-life.com/sysfiles/member/index_public.cfm?memberid=693\r\n\r\nVirtual Jerusalem-The Place Where Jews Click: http://www.virtualjerusalem.com/\r\n\r\nvirtualyeshiva.com Torah Learning on the Net: http://www.virtualyeshiva.com/\r\n\r\nWalking in Torah Teachings Hebrew Nation: http://walkingintorah.com/Teachings.html\r\n\r\nway of the master radio: http://www.oneplace.com/ministries/way-of-the-master/\r\n\r\nWearing the Tallit Katan: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Tallit_Katan/tallit_katan.html\r\n\r\nWelcome kabbalah course: http://edu.kabbalah.info/group/lc/home\r\n\r\nWelcome to biblediagrams.com: http://biblediagrams.com/diagrams/index.htm\r\n\r\nWelcome to MessianicJewish.net: http://www.messianicjewish.net/\r\n\r\nWelcome to the JewToo.org Blog!: http://jewtoo.multiply.com/\r\n\r\nWelcome to your Tampa Jewish Community Online!: http://www.jewishtampa.com/calendar/index.aspx?startdate=3/9/2011%206:30:00%20PM&enddate=3/9/2011%207:45:00%20PM&id=20&eventid=510947&recurrenceid=11922\r\n\r\nWINK News: http://www.winknews.com/weather/doppler\r\n\r\nWitnessing Resources -: http://www.nazarenemedia.net/witnessing-resources.html\r\n\r\nworship song guitar chords: http://www.worshiparchive.com/\r\n\r\nwww.wstw.fm listen: http://www.wstw.fm/listen%20live.html\r\n\r\nYashaNet HomePage: http://www.yashanet.com/index.htm\r\n\r\nYeshiva Beth HaShem Bible 101: http://yeshivabethhashem.org/yeshiva/1lesson4.html\r\n\r\nYotzer Hame'orot - the Morning Blessing: http://www.hebrew4christians.com/Blessings/Daily_Blessings/Yotzer_Hame_orot/yotzer_hame_orot.html\r\n\r\nThe Straight Way Ministry Home Page: http://thestraightway.org/\r\n\r\nifcj Journey Through the Scriptures: http://www.ifcj.org/site/PageNavigator/eng/jtts\r\n\r\nTanya - The Book of Tanya Translated into English - Text of the Tanya: http://www.chabad.org/library/tanya/tanya_cdo/aid/1028862/jewish/Tanya.htm\r\n\r\nJewish Holidays, Torah, Jewish Jobs, Tuition Affordability, Kosher Certification, Jewish Teens: http://www.ou.org/\r\n\r\nEsynagogue.org - Home: http://www.esynagogue.org/\r\n\r\nKeep It!: javascript:(a%20=%20(b%20=%20document).createElement('script')).src%20=%20'http://keepvid.com/js/bm.js',%20b.body.appendChild(a);void(0;\r\n\r\nwww.VirtualCantor.com: http://virtualcantor.com/CDOrder.htm\r\n\r\nTRI Classes: http://www.triclasses.com/\r\n\r\nAggressive Christianity Library: http://www.aggressivechristianity.net/library.htm\r\n\r\nDictionary of the Targumim, the Talmud Babli and Yerushalmi, and the Midrashic Literature by Marcus Jastrow: [suspicious link removed]\r\n\r\nHalakhah.com Babylonian Talmud Online in English: http://halakhah.com/\r\n\r\nKhabouris Codex Aramaic Peshitta New Testament Manuscript: http://www.aramaicpeshitta.com/AramaicNTtools/khabouris.htm\r\n\r\nJacob Richman's Hot Sites - Jewish - Learn Hebrew Sites: http://www.jr.co.il/hotsites/j-hebrew.htm\r\n\r\nIndex for Part 1 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index1.html\r\n\r\nIndex for Part 2 Kitzur Shulchan Aruch Linear Translation by Yona Newman© 1999-2010: http://www.yonanewman.org/kizzur/kiz-index2.html\r\n\r\nBiblia Hebraica (Biblical Hebrew - Home): http://www.bible101.org/hebrew/home.html","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"20","document_type":"upload","title":"Complete Documentation up to date","slug":"complete-documentation-up-to-date","summary_text":null,"content_markdown":null,"content_html":null,"file_name":"COMPREHENSIVE_DOCUMENTATION.md","stored_name":"20260417-160828-6006d4f1.md","mime_type":"application/octet-stream","file_size_bytes":"22313","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-160828-6006d4f1.md","is_deleted":"0","created_at":"2026-04-17 12:08:29","updated_at":"2026-04-17 12:08:29","editor_content":"# Ella's Alterations - Comprehensive System Documentation\n\n**Generated:** April 17, 2026\n**Location:** C:\\code\\customerdb\n\n---\n\n## Table of Contents\n\n1. [System Overview](#1-system-overview)\n2. [Directory Structure](#2-directory-structure)\n3. [Database Schema](#3-database-schema)\n4. [API Endpoints](#4-api-endpoints)\n5. [External Integrations](#5-external-integrations)\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\n7. [Web Applications](#7-web-applications)\n8. [Settings & Configuration](#8-settings--configuration)\n9. [Migration Notes](#9-migration-notes)\n10. [Scripts & Tools](#10-scripts--tools)\n\n---\n\n## 1. System Overview\n\n### Purpose\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\n\n### Server Information\n- **Primary Server:** kefa@192.168.7.202\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\n- **Web Server:** Apache 2.4 with mod-php\n- **PHP Version:** 8.3\n\n### Three Web Applications\n\n| App | Local URL | Public URL | Purpose |\n|-----|-----------|------------|---------|\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\n\n---\n\n## 2. Directory Structure\n\n```\ncustomerdb/\n├── backend/ # Main PHP backend API and services\n│ ├── api/ # REST API endpoints (~57 files)\n│ ├── bin/ # Binary/executable scripts\n│ ├── backup/ # Backup versions of services\n│ └── jobs/ # Background job scripts\n├── frontend/ # Front Desk app (port 8895)\n│ ├── *.html # HTML pages\n│ ├── api/ # PHP API endpoints\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\n├── webui/ # Management app (port 8894)\n│ ├── *.html # HTML pages\n│ ├── api/ # PHP API endpoints\n│ ├── backend/ # Backend API copy\n│ └── email/ # Email templates\n├── schedule/ # Easy!Appointments installation\n├── schema/ # Database schema files\n├── migrations/ # SQL migrations\n├── scripts/ # PowerShell/Python import scripts\n├── docs/ # Documentation\n├── blog/ # Blog system and reengineering\n├── csv/ # CSV data files\n├── backups/ # Backup archives\n├── Topaz/ # Topaz signature pad configs\n├── webui/ # Second web interface\n│ ├── api/\n│ ├── backend/\n│ └── frontend/\n├── notused/ # Deprecated/unused files\n└── wordpress-plugin/ # WordPress integration\n```\n\n---\n\n## 3. Database Schema\n\n### Core Tables (from mariadb_core_schema.sql)\n\n#### customers\nMain customer registry.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| customer_id | INT UNSIGNED PK | Auto-increment |\n| legacy_customer_id | INT NULL | Original Access DB ID |\n| created_at | DATETIME | Record creation time |\n| full_name | VARCHAR(200) | Customer full name |\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\n| email | VARCHAR(255) | Email address |\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\n| description | TEXT | Description |\n| notes | TEXT | Internal notes |\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\n\n#### visits\nOne row per alteration job/order.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| visit_id | INT UNSIGNED PK | |\n| legacy_visit_id | INT NULL | Original Access DB ID |\n| customer_id | INT UNSIGNED FK | → customers |\n| visit_date | DATETIME | Drop-off date |\n| pickup_date | DATETIME | Expected pickup |\n| notes | TEXT | General visit notes |\n| customer_name_snapshot | VARCHAR(200) | |\n| phone_snapshot | VARCHAR(30) | |\n| email_snapshot | VARCHAR(255) | |\n| color | VARCHAR(100) | Garment color |\n| alterations_needed | TEXT | |\n| alterations_notes | TEXT | Detailed alteration instructions |\n| alterations_price | DECIMAL(10,2) | Manual price field |\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\n| expedited_fee | DECIMAL(10,2) | |\n| paid_date | DATETIME | When paid |\n| signature_text | TEXT | Text signature (legacy) |\n| signature_date | DATETIME | |\n| paid_flag | TINYINT(1) | 1 = paid |\n| source_key_value | VARCHAR(100) | |\n\n#### visit_signatures\nFull resolution signature images per visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| signature_id | BIGINT UNSIGNED PK | |\n| visit_id | INT UNIQUE | One signature per visit |\n| signature_date | DATETIME | |\n| image_format | VARCHAR(50) | Default 'image/png' |\n| original_filename | VARCHAR(255) | |\n| original_extension | VARCHAR(20) | |\n| file_size_bytes | BIGINT | |\n| sha256_hash | CHAR(64) | Hash for verification |\n| signature_image | LONGBLOB | Raw PNG binary |\n| created_at | DATETIME | |\n\n#### item_types\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\n\n| Column | Type | Notes |\n|--------|------|-------|\n| item_type_id | INT UNSIGNED PK | |\n| type_name | VARCHAR(100) UNIQUE | Display name |\n\n#### orders\nOrders per visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_id | BIGINT UNSIGNED PK | |\n| visit_id | INT UNSIGNED FK | → visits |\n| customer_id | INT UNSIGNED FK | → customers |\n| created_at | DATETIME | |\n| no_tax | TINYINT(1) | Tax exempt |\n| picked_up | TINYINT(1) | |\n| source_table | VARCHAR(50) | |\n\n#### order_items\nLine items for each alteration within a visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_item_id | BIGINT UNSIGNED PK | |\n| order_id | BIGINT UNSIGNED FK | → orders |\n| legacy_alteration_id | INT NULL | |\n| item_type_id | INT FK | → item_types |\n| item_type_name | VARCHAR(100) | |\n| description | TEXT | |\n| quantity | INT | Default 1 |\n| unit_price | DECIMAL(10,2) | |\n| total_price | DECIMAL(10,2) | |\n| notes | TEXT | Per-item notes |\n| color | VARCHAR(100) | Garment color |\n| no_tax | TINYINT(1) | Tax exempt |\n| is_range_price | TINYINT(1) | |\n| range_price_label | VARCHAR(100) | |\n| paid_flag | TINYINT(1) | |\n| paid_type | VARCHAR(100) | |\n| picked_up | TINYINT(1) | |\n| created_at | DATETIME | |\n\n#### order_item_type_links\nMany-to-many links between order items and item types.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\n| item_type_id | INT UNSIGNED FK | → item_types |\n| slot_number | TINYINT UNSIGNED | |\n\n#### payments\nPayment records per order.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| payment_id | BIGINT UNSIGNED PK | |\n| order_id | BIGINT UNSIGNED FK | → orders |\n| payment_date | DATETIME | |\n| payment_type | VARCHAR(100) | |\n| payment_method | VARCHAR(100) | |\n| amount | DECIMAL(10,2) | |\n| notes | TEXT | |\n\n#### tax_payments\nTax payment records.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| tax_payment_id | INT UNSIGNED PK | |\n| legacy_tax_payment_id | INT NULL | |\n| payment_date | DATETIME | |\n| payment_type | VARCHAR(100) | |\n| payment_method | VARCHAR(100) | |\n| amount | DECIMAL(10,2) | |\n| notes | TEXT | |\n| receipt_blob | LONGBLOB | |\n\n#### change_log\nAudit log for changes.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| log_id | BIGINT UNSIGNED PK | |\n| legacy_log_id | INT NULL | |\n| table_name | VARCHAR(100) | |\n| record_id | BIGINT | |\n| visit_id | INT | |\n| field_name | VARCHAR(100) | |\n| old_value | TEXT | |\n| new_value | TEXT | |\n| changed_at | DATETIME | |\n| changed_by | VARCHAR(100) | |\n\n#### ads\nAdvertising/articles.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| ad_id | INT UNSIGNED PK | |\n| legacy_ad_id | INT NULL | |\n| article_text | TEXT | |\n| article_picture | LONGBLOB | |\n| site_submitted | VARCHAR(255) | |\n| views | INT | Default 0 |\n| date_posted | DATETIME | |\n\n#### staging_* tables\nStaging tables for data migration.\n\n| Table Name | Purpose |\n|------------|---------|\n| staging_customer_table_raw | Staging for legacy customer data |\n| staging_customer_visits_raw | Staging for legacy visits |\n| staging_alteration_items_raw | Staging for items |\n\n### Additional Backend Tables\n\n| Table Name | Purpose |\n|------------|---------|\n| setmore_appointments | Synced from Setmore |\n| backend_conversion_job | Document conversion jobs |\n| backend_job_runs | Background job execution log |\n| customer_totals | Daily customer totals |\n| daily_customer_totals | Daily summary totals |\n| customer_payment_transactions | Payment ledger |\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\n| backend_appointment_workflow | Appointment state machine |\n| backend_no_show_log | No-show tracking |\n| backend_error_log | Error logging |\n| backend_change_log | Change audit log |\n| backend_document | Document storage |\n| backend_document_version | Document version history |\n| backend_speed_dial | Quick links |\n| backend_square_terminal_code | Square terminal pairing codes |\n| backend_square_terminal_device | Square device registry |\n| cron_job | Scheduled job definitions |\n| cron_job_run | Cron execution history |\n| app_settings | Global configuration |\n| app_users | Login accounts |\n| remember_tokens | \"Remember Me\" tokens |\n| customer_reports | PDF report index |\n\n---\n\n## 4. API Endpoints\n\n### 4.1 Frontend API (port 8895 /api/)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **customer_get.php** | Get customer by ID |\n| **customer_save.php** | Create or update customer |\n| **customer_detail.php** | Customer + visits summary |\n| **customer_delete.php** | Delete customer |\n| **customer_list_hide.php** | Hide customer from list |\n| **search_customers.php** | Search by name/phone |\n| **visit_get.php** | Get visit + items |\n| **visit_save.php** | Create/update visit + items |\n| **visit_delete.php** | Delete visit |\n| **visit_report_generate.php** | Generate visit report |\n| **item_types.php** | List all alteration types |\n| **item_type_add.php** | Add new item type |\n| **item_type_update.php** | Update item type |\n| **item_type_delete.php** | Delete item type |\n| **pickups_today.php** | Orders due today |\n| **ready.php** | Orders ready for pickup |\n| **overdue.php** | Overdue orders |\n| **upcoming.php** | Orders due in next N days |\n| **workload_7days.php** | 7-day workload |\n| **save_signature.php** | Save canvas signature |\n| **reports_list.php** | Reports for customer |\n| **attach_reports.php** | Index all PDFs to DB |\n| **save_signature.php** | Save signature image |\n| **today_appointments_list.php** | Today's appointments |\n| **settings_get.php** | Get app settings |\n| **settings_save.php** | Save app settings |\n| **settings_css.php** | Live theme CSS |\n| **send_email.php** | Send email via SMTP |\n| **send_sms.php** | Send SMS via Twilio |\n| **send_reminders.php** | Send appointment reminders |\n| **ea_appointments.php** | EasyAppointments REST API |\n| **ea_webhook.php** | Receives EA webhook events |\n| **setmore.php** | Setmore API proxy |\n| **setmore_to_ea_import.php** | Import customers to EA |\n| **square_config.php** | Square public config |\n| **square_process.php** | Process Square payment |\n| **square_customer_log.php** | Customer Square log |\n| **square_terminal.php** | Square terminal |\n| **login.php** | Authenticate |\n| **logout.php** | Clear session |\n| **session_check.php** | Check if logged in |\n| **auth.php** | Session authentication |\n| **config.php** | App config constants |\n| **db.php** | Database connection |\n| **log_error.php** | Log client-side error |\n| **todo_list.php** | Todo list management |\n| **send_telegram.php** | Send Telegram messages |\n\n### 4.2 Management API (port 8894 /api/)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **login.php** | Authenticate |\n| **logout.php** | Clear session |\n| **session_check.php** | Check if logged in |\n| **customer_detail.php** | Customer + visits |\n| **search_customers.php** | Search customers |\n| **update_customer.php** | Update customer |\n| **users_list.php** | List all users |\n| **user_save.php** | Create/update user |\n| **list_report_dirs.php** | Date folders |\n| **list_report_files.php** | PDFs in folder |\n| **report_file.php** | Access report file |\n| **pickups_today.php** | Today's pickups |\n| **ready.php** | Ready for pickup |\n| **overdue.php** | Overdue |\n| **settings_get.php** | Get settings |\n| **settings_save.php** | Save settings |\n| **settings_css.php** | Live CSS theme |\n| **imap_list.php** | List IMAP folders |\n| **imap_fetch.php** | Fetch emails |\n| **imap_send.php** | Send email via IMAP |\n| **imap_action.php** | IMAP actions |\n| **sms_send_helper.php** | SMS sending helper |\n| **sms_webhook_receive.php** | Receive SMS webhooks |\n| **sms_inbox_api.php** | SMS inbox API |\n| **slideshow_list.php** | Slideshow images |\n| **slideshow_upload.php** | Upload slideshow |\n| **slideshow_delete.php** | Delete slideshow |\n| **users_list.php** | User management |\n| **report_list.php** | Report list |\n| **debug_filters.php** | Debug filters |\n| **sysadmin.php** | System admin |\n| **test.php** | Testing endpoint |\n\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **customer.php** | Get customer details |\n| **customers.php** | List/search customers |\n| **customer_save.php** | Save/update customer |\n| **visit.php** | Get visit details |\n| **visit_delete.php** | Delete visit |\n| **appointments.php** | Unified appointments |\n| **appointments_summary.php** | Appointment statistics |\n| **setmore_appointments.php** | Setmore appointments |\n| **setmore_import.php** | Import from Setmore |\n| **module_*.php** | Module loaders |\n| **alteration_items.php** | Alteration management |\n| **alteration_items_summary.php** | Alteration statistics |\n| **gold_silver_*.php** | Gold/silver prices |\n| **bank_*.php** | Bank transactions |\n| **stocks_summary.php** | Stock data |\n| **medication_*.php** | Medication tracking |\n| **payroll_import.php** | Payroll import |\n| **car_maintenance_*.php** | Vehicle maintenance |\n| **duke_*.php** | Duke integration |\n| **reports_*.php** | Report generation |\n| **totals_import.php** | Customer totals import |\n| **documents.php** | Document management |\n| **document_upload.php** | File uploads |\n| **cust_email_*.php** | Email data |\n| **mail_*.php** | Email sending |\n| **reminders_create.php** | Create reminders |\n| **nightly_reports.php** | Nightly reports |\n| **morning_jobs.php** | Morning jobs |\n| **appointment_workflow.php** | Workflow management |\n| **sysadmin.php** | System admin |\n| **reporting_module.php** | Reporting |\n| **job_runs.php** | Job tracking |\n| **health.php** | Health check |\n| **overview.php** | Dashboard data |\n\n---\n\n## 5. External Integrations\n\n### 5.1 Square (Payment Processing)\n- **Purpose:** Point-of-sale payments, receipts, terminal management\n- **Features:**\n - Square Terminal device pairing/management\n - Payment processing with tips\n - Customer payment logging\n - Sandbox and live environments\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\n\n### 5.2 Twilio (SMS)\n- **Purpose:** SMS notifications and reminders\n- **Features:**\n - Send SMS to customers\n - Appointment reminders\n - SMS consent tracking\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\n\n### 5.3 Setmore (Appointment Booking)\n- **Purpose:** Appointment synchronization\n- **Features:**\n - Pull appointments from Setmore API\n - Link to customers/visits\n- **Table:** `setmore_appointments`\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\n\n### 5.4 EasyAppointments (Alternative Booking)\n- **Purpose:** Full appointment booking platform\n- **Location:** C:\\code\\customerdb\\schedule\\\n- **Features:**\n - Customer/provider management\n - Web-based booking widget\n - REST API\n\n### 5.5 Telegram (Notifications)\n- **Purpose:** Bot-based notifications\n- **Features:**\n - Send notifications to channels\n - Visit alerts\n - Cron job status\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\n\n### 5.6 Ollama (AI - Optional)\n- **Purpose:** Local AI for email generation\n- **Settings:** `ollama_url`, `ollama_default_model`\n\n---\n\n## 6. Scheduled Jobs / Cron\n\n### Configured Cron Jobs\n\n| Job Label | Schedule | Purpose |\n|----------|----------|---------|\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\n\n### Background Job Scripts (backend/jobs/)\n\n| Script | Purpose |\n|--------|---------|\n| **run_morning_jobs.php** | Main morning job runner |\n| **smoke_morning_jobs.php** | Smoke test |\n| **send_nightly_reports.php** | Nightly report emailer |\n| **send_nextday_customer_emails.php** | Next-day reminders |\n| **delete_add_name_records.php** | Cleanup job |\n| **rebuild_customer_totals.php** | Rebuild totals |\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\n| **import_customer_totals_csv.php** | CSV import |\n| **backfill_square_customer_log.php** | Square log backfill |\n| **run_cron_job.php** | Generic cron runner |\n\n---\n\n## 7. Web Applications\n\n### 7.1 Front Desk App (port 8895)\n\n| Page | Purpose |\n|------|---------|\n| **index.html** | Main dashboard, search, stats |\n| **customer.html** | Customer detail + visit history |\n| **visit.html** | Create/edit alteration order |\n| **customer_display.html** | Second monitor display |\n| **receipt.html** | Printable receipt |\n| **reports.html** | Order list by status |\n| **upcoming.html** | Next 7 days pickups |\n| **schedule.html** | Appointments calendar |\n| **login.html** | Login page |\n| **item_types.html** | Item type management |\n\n### 7.2 Management App (port 8894)\n\n| Page | Purpose |\n|------|---------|\n| **index.html** | Login page |\n| **webui.html** | Dashboard |\n| **customer.html** | Customer detail view |\n| **admin.html** | Reports browser |\n| **settings.html** | App settings editor |\n| **users.html** | User management |\n| **reports.html** | Order reports |\n\n---\n\n## 8. Settings & Configuration\n\n### app_settings Keys\n\n| Key | Purpose |\n|-----|---------|\n| theme_bg | Page background color |\n| theme_panel | Card/panel color |\n| theme_text | Text color |\n| theme_accent | Accent/button color |\n| theme_accent_deep | Darker accent |\n| theme_teal | Teal color |\n| theme_font | Font family |\n| smtp_host | Email server hostname |\n| smtp_port | Email server port |\n| smtp_secure | tls or ssl |\n| smtp_user | Email login |\n| smtp_pass | Email password |\n| smtp_from_email | From address |\n| smtp_from_name | From display name |\n| twilio_sid | Twilio Account SID |\n| twilio_token | Twilio Auth Token |\n| twilio_from | Twilio phone number |\n| imap_host | Incoming email server |\n| imap_port | IMAP port |\n| imap_ssl | SSL enable |\n| imap_user | IMAP login |\n| imap_pass | IMAP password |\n| square_token | Square API token |\n| square_location | Square location ID |\n| setmore_staff_key | Setmore API key |\n| setmore_refresh | Setmore refresh token |\n| require_report | PDF required |\n| require_signature | Signature required |\n| reports_base_path | Reports disk path |\n| reports_base_url | Reports URL |\n| ollama_url | Ollama AI URL |\n| ollama_default_model | Ollama model |\n| telegram_bot_token | Telegram bot token |\n| telegram_chat_id | Telegram chat ID |\n\n---\n\n## 9. Migration Notes\n\n### Access to MariaDB Workflow\n\n1. **Export source data from Access**\n - Export ordinary tables to CSV\n - Export attachment fields with VBA or DAO Recordset2 logic\n - Keep original IDs during migration\n\n2. **Load into MariaDB staging**\n - Use `LOAD DATA LOCAL INFILE` for CSV files\n - Load signatures separately into `visit_signatures`\n\n3. **Normalize**\n - `CustomerTable` → `customers`\n - `CustomerVisits` → `visits`\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\n - `2024TaxPayments` → `tax_payments`\n - `AlterationItemsChangeLog` → `change_log`\n\n4. **Signature BLOB path**\n - Export Access attachment to temp file\n - Convert BMP to PNG\n - Insert PNG bytes into `visit_signatures.signature_image`\n\n5. **Validation**\n - Compare source row counts vs target\n - Compare distinct legacy IDs\n - Compare sample signatures by SHA256\n\n---\n\n## 10. Scripts & Tools\n\n### PowerShell Scripts (scripts/)\n\n| Script | Purpose |\n|--------|---------|\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\n| **sync_backend_mirror.ps1** | Sync backend mirror |\n| **check_backend_mirror.ps1** | Check mirror status |\n\n### Python Scripts (scripts/)\n\n| Script | Purpose |\n|--------|---------|\n| **signature_blob_loader.py** | Load signature blobs |\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\n| **import_access_v2.py** | Import Access v2 |\n| **import_csv_v2.py** | Import CSV v2 |\n\n### SQL Migrations (migrations/)\n\n| File | Purpose |\n|------|---------|\n| **20260407_add_conversion_job.sql** | Add conversion job table |\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\n\n---\n\n## File Statistics\n\n- **Total PHP files:** 200+\n- **Total SQL files:** 100+\n- **Total HTML pages:** 20+\n- **Database tables:** 30+\n\n---\n\n*End of Documentation*","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"20","document_type":"upload","title":"Complete Documentation up to date 04-17-2026","slug":"complete-documentation-up-to-date","summary_text":"Complete Documentation up to date 04-17-2026","content_markdown":"# Ella's Alterations - Comprehensive System Documentation\r\n\r\n**Generated:** April 17, 2026\r\n**Location:** C:\\code\\customerdb\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n1. [System Overview](#1-system-overview)\r\n2. [Directory Structure](#2-directory-structure)\r\n3. [Database Schema](#3-database-schema)\r\n4. [API Endpoints](#4-api-endpoints)\r\n5. [External Integrations](#5-external-integrations)\r\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\r\n7. [Web Applications](#7-web-applications)\r\n8. [Settings & Configuration](#8-settings--configuration)\r\n9. [Migration Notes](#9-migration-notes)\r\n10. [Scripts & Tools](#10-scripts--tools)\r\n\r\n---\r\n\r\n## 1. System Overview\r\n\r\n### Purpose\r\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\r\n\r\n### Server Information\r\n- **Primary Server:** kefa@192.168.7.202\r\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\r\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\r\n- **Web Server:** Apache 2.4 with mod-php\r\n- **PHP Version:** 8.3\r\n\r\n### Three Web Applications\r\n\r\n| App | Local URL | Public URL | Purpose |\r\n|-----|-----------|------------|---------|\r\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\r\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\r\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\r\n\r\n---\r\n\r\n## 2. Directory Structure\r\n\r\n```\r\ncustomerdb/\r\n├── backend/ # Main PHP backend API and services\r\n│ ├── api/ # REST API endpoints (~57 files)\r\n│ ├── bin/ # Binary/executable scripts\r\n│ ├── backup/ # Backup versions of services\r\n│ └── jobs/ # Background job scripts\r\n├── frontend/ # Front Desk app (port 8895)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\r\n├── webui/ # Management app (port 8894)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ ├── backend/ # Backend API copy\r\n│ └── email/ # Email templates\r\n├── schedule/ # Easy!Appointments installation\r\n├── schema/ # Database schema files\r\n├── migrations/ # SQL migrations\r\n├── scripts/ # PowerShell/Python import scripts\r\n├── docs/ # Documentation\r\n├── blog/ # Blog system and reengineering\r\n├── csv/ # CSV data files\r\n├── backups/ # Backup archives\r\n├── Topaz/ # Topaz signature pad configs\r\n├── webui/ # Second web interface\r\n│ ├── api/\r\n│ ├── backend/\r\n│ └── frontend/\r\n├── notused/ # Deprecated/unused files\r\n└── wordpress-plugin/ # WordPress integration\r\n```\r\n\r\n---\r\n\r\n## 3. Database Schema\r\n\r\n### Core Tables (from mariadb_core_schema.sql)\r\n\r\n#### customers\r\nMain customer registry.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| customer_id | INT UNSIGNED PK | Auto-increment |\r\n| legacy_customer_id | INT NULL | Original Access DB ID |\r\n| created_at | DATETIME | Record creation time |\r\n| full_name | VARCHAR(200) | Customer full name |\r\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\r\n| email | VARCHAR(255) | Email address |\r\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\r\n| description | TEXT | Description |\r\n| notes | TEXT | Internal notes |\r\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\r\n\r\n#### visits\r\nOne row per alteration job/order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| visit_id | INT UNSIGNED PK | |\r\n| legacy_visit_id | INT NULL | Original Access DB ID |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| visit_date | DATETIME | Drop-off date |\r\n| pickup_date | DATETIME | Expected pickup |\r\n| notes | TEXT | General visit notes |\r\n| customer_name_snapshot | VARCHAR(200) | |\r\n| phone_snapshot | VARCHAR(30) | |\r\n| email_snapshot | VARCHAR(255) | |\r\n| color | VARCHAR(100) | Garment color |\r\n| alterations_needed | TEXT | |\r\n| alterations_notes | TEXT | Detailed alteration instructions |\r\n| alterations_price | DECIMAL(10,2) | Manual price field |\r\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\r\n| expedited_fee | DECIMAL(10,2) | |\r\n| paid_date | DATETIME | When paid |\r\n| signature_text | TEXT | Text signature (legacy) |\r\n| signature_date | DATETIME | |\r\n| paid_flag | TINYINT(1) | 1 = paid |\r\n| source_key_value | VARCHAR(100) | |\r\n\r\n#### visit_signatures\r\nFull resolution signature images per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| signature_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNIQUE | One signature per visit |\r\n| signature_date | DATETIME | |\r\n| image_format | VARCHAR(50) | Default 'image/png' |\r\n| original_filename | VARCHAR(255) | |\r\n| original_extension | VARCHAR(20) | |\r\n| file_size_bytes | BIGINT | |\r\n| sha256_hash | CHAR(64) | Hash for verification |\r\n| signature_image | LONGBLOB | Raw PNG binary |\r\n| created_at | DATETIME | |\r\n\r\n#### item_types\r\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| item_type_id | INT UNSIGNED PK | |\r\n| type_name | VARCHAR(100) UNIQUE | Display name |\r\n\r\n#### orders\r\nOrders per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNSIGNED FK | → visits |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| created_at | DATETIME | |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| picked_up | TINYINT(1) | |\r\n| source_table | VARCHAR(50) | |\r\n\r\n#### order_items\r\nLine items for each alteration within a visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| legacy_alteration_id | INT NULL | |\r\n| item_type_id | INT FK | → item_types |\r\n| item_type_name | VARCHAR(100) | |\r\n| description | TEXT | |\r\n| quantity | INT | Default 1 |\r\n| unit_price | DECIMAL(10,2) | |\r\n| total_price | DECIMAL(10,2) | |\r\n| notes | TEXT | Per-item notes |\r\n| color | VARCHAR(100) | Garment color |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| is_range_price | TINYINT(1) | |\r\n| range_price_label | VARCHAR(100) | |\r\n| paid_flag | TINYINT(1) | |\r\n| paid_type | VARCHAR(100) | |\r\n| picked_up | TINYINT(1) | |\r\n| created_at | DATETIME | |\r\n\r\n#### order_item_type_links\r\nMany-to-many links between order items and item types.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\r\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\r\n| item_type_id | INT UNSIGNED FK | → item_types |\r\n| slot_number | TINYINT UNSIGNED | |\r\n\r\n#### payments\r\nPayment records per order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| payment_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n\r\n#### tax_payments\r\nTax payment records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| tax_payment_id | INT UNSIGNED PK | |\r\n| legacy_tax_payment_id | INT NULL | |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n| receipt_blob | LONGBLOB | |\r\n\r\n#### change_log\r\nAudit log for changes.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| log_id | BIGINT UNSIGNED PK | |\r\n| legacy_log_id | INT NULL | |\r\n| table_name | VARCHAR(100) | |\r\n| record_id | BIGINT | |\r\n| visit_id | INT | |\r\n| field_name | VARCHAR(100) | |\r\n| old_value | TEXT | |\r\n| new_value | TEXT | |\r\n| changed_at | DATETIME | |\r\n| changed_by | VARCHAR(100) | |\r\n\r\n#### ads\r\nAdvertising/articles.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| ad_id | INT UNSIGNED PK | |\r\n| legacy_ad_id | INT NULL | |\r\n| article_text | TEXT | |\r\n| article_picture | LONGBLOB | |\r\n| site_submitted | VARCHAR(255) | |\r\n| views | INT | Default 0 |\r\n| date_posted | DATETIME | |\r\n\r\n#### staging_* tables\r\nStaging tables for data migration.\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| staging_customer_table_raw | Staging for legacy customer data |\r\n| staging_customer_visits_raw | Staging for legacy visits |\r\n| staging_alteration_items_raw | Staging for items |\r\n\r\n### Additional Backend Tables\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| setmore_appointments | Synced from Setmore |\r\n| backend_conversion_job | Document conversion jobs |\r\n| backend_job_runs | Background job execution log |\r\n| customer_totals | Daily customer totals |\r\n| daily_customer_totals | Daily summary totals |\r\n| customer_payment_transactions | Payment ledger |\r\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\r\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\r\n| backend_appointment_workflow | Appointment state machine |\r\n| backend_no_show_log | No-show tracking |\r\n| backend_error_log | Error logging |\r\n| backend_change_log | Change audit log |\r\n| backend_document | Document storage |\r\n| backend_document_version | Document version history |\r\n| backend_speed_dial | Quick links |\r\n| backend_square_terminal_code | Square terminal pairing codes |\r\n| backend_square_terminal_device | Square device registry |\r\n| cron_job | Scheduled job definitions |\r\n| cron_job_run | Cron execution history |\r\n| app_settings | Global configuration |\r\n| app_users | Login accounts |\r\n| remember_tokens | \"Remember Me\" tokens |\r\n| customer_reports | PDF report index |\r\n\r\n---\r\n\r\n## 4. API Endpoints\r\n\r\n### 4.1 Frontend API (port 8895 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer_get.php** | Get customer by ID |\r\n| **customer_save.php** | Create or update customer |\r\n| **customer_detail.php** | Customer + visits summary |\r\n| **customer_delete.php** | Delete customer |\r\n| **customer_list_hide.php** | Hide customer from list |\r\n| **search_customers.php** | Search by name/phone |\r\n| **visit_get.php** | Get visit + items |\r\n| **visit_save.php** | Create/update visit + items |\r\n| **visit_delete.php** | Delete visit |\r\n| **visit_report_generate.php** | Generate visit report |\r\n| **item_types.php** | List all alteration types |\r\n| **item_type_add.php** | Add new item type |\r\n| **item_type_update.php** | Update item type |\r\n| **item_type_delete.php** | Delete item type |\r\n| **pickups_today.php** | Orders due today |\r\n| **ready.php** | Orders ready for pickup |\r\n| **overdue.php** | Overdue orders |\r\n| **upcoming.php** | Orders due in next N days |\r\n| **workload_7days.php** | 7-day workload |\r\n| **save_signature.php** | Save canvas signature |\r\n| **reports_list.php** | Reports for customer |\r\n| **attach_reports.php** | Index all PDFs to DB |\r\n| **save_signature.php** | Save signature image |\r\n| **today_appointments_list.php** | Today's appointments |\r\n| **settings_get.php** | Get app settings |\r\n| **settings_save.php** | Save app settings |\r\n| **settings_css.php** | Live theme CSS |\r\n| **send_email.php** | Send email via SMTP |\r\n| **send_sms.php** | Send SMS via Twilio |\r\n| **send_reminders.php** | Send appointment reminders |\r\n| **ea_appointments.php** | EasyAppointments REST API |\r\n| **ea_webhook.php** | Receives EA webhook events |\r\n| **setmore.php** | Setmore API proxy |\r\n| **setmore_to_ea_import.php** | Import customers to EA |\r\n| **square_config.php** | Square public config |\r\n| **square_process.php** | Process Square payment |\r\n| **square_customer_log.php** | Customer Square log |\r\n| **square_terminal.php** | Square terminal |\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **auth.php** | Session authentication |\r\n| **config.php** | App config constants |\r\n| **db.php** | Database connection |\r\n| **log_error.php** | Log client-side error |\r\n| **todo_list.php** | Todo list management |\r\n| **send_telegram.php** | Send Telegram messages |\r\n\r\n### 4.2 Management API (port 8894 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **customer_detail.php** | Customer + visits |\r\n| **search_customers.php** | Search customers |\r\n| **update_customer.php** | Update customer |\r\n| **users_list.php** | List all users |\r\n| **user_save.php** | Create/update user |\r\n| **list_report_dirs.php** | Date folders |\r\n| **list_report_files.php** | PDFs in folder |\r\n| **report_file.php** | Access report file |\r\n| **pickups_today.php** | Today's pickups |\r\n| **ready.php** | Ready for pickup |\r\n| **overdue.php** | Overdue |\r\n| **settings_get.php** | Get settings |\r\n| **settings_save.php** | Save settings |\r\n| **settings_css.php** | Live CSS theme |\r\n| **imap_list.php** | List IMAP folders |\r\n| **imap_fetch.php** | Fetch emails |\r\n| **imap_send.php** | Send email via IMAP |\r\n| **imap_action.php** | IMAP actions |\r\n| **sms_send_helper.php** | SMS sending helper |\r\n| **sms_webhook_receive.php** | Receive SMS webhooks |\r\n| **sms_inbox_api.php** | SMS inbox API |\r\n| **slideshow_list.php** | Slideshow images |\r\n| **slideshow_upload.php** | Upload slideshow |\r\n| **slideshow_delete.php** | Delete slideshow |\r\n| **users_list.php** | User management |\r\n| **report_list.php** | Report list |\r\n| **debug_filters.php** | Debug filters |\r\n| **sysadmin.php** | System admin |\r\n| **test.php** | Testing endpoint |\r\n\r\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer.php** | Get customer details |\r\n| **customers.php** | List/search customers |\r\n| **customer_save.php** | Save/update customer |\r\n| **visit.php** | Get visit details |\r\n| **visit_delete.php** | Delete visit |\r\n| **appointments.php** | Unified appointments |\r\n| **appointments_summary.php** | Appointment statistics |\r\n| **setmore_appointments.php** | Setmore appointments |\r\n| **setmore_import.php** | Import from Setmore |\r\n| **module_*.php** | Module loaders |\r\n| **alteration_items.php** | Alteration management |\r\n| **alteration_items_summary.php** | Alteration statistics |\r\n| **gold_silver_*.php** | Gold/silver prices |\r\n| **bank_*.php** | Bank transactions |\r\n| **stocks_summary.php** | Stock data |\r\n| **medication_*.php** | Medication tracking |\r\n| **payroll_import.php** | Payroll import |\r\n| **car_maintenance_*.php** | Vehicle maintenance |\r\n| **duke_*.php** | Duke integration |\r\n| **reports_*.php** | Report generation |\r\n| **totals_import.php** | Customer totals import |\r\n| **documents.php** | Document management |\r\n| **document_upload.php** | File uploads |\r\n| **cust_email_*.php** | Email data |\r\n| **mail_*.php** | Email sending |\r\n| **reminders_create.php** | Create reminders |\r\n| **nightly_reports.php** | Nightly reports |\r\n| **morning_jobs.php** | Morning jobs |\r\n| **appointment_workflow.php** | Workflow management |\r\n| **sysadmin.php** | System admin |\r\n| **reporting_module.php** | Reporting |\r\n| **job_runs.php** | Job tracking |\r\n| **health.php** | Health check |\r\n| **overview.php** | Dashboard data |\r\n\r\n---\r\n\r\n## 5. External Integrations\r\n\r\n### 5.1 Square (Payment Processing)\r\n- **Purpose:** Point-of-sale payments, receipts, terminal management\r\n- **Features:**\r\n - Square Terminal device pairing/management\r\n - Payment processing with tips\r\n - Customer payment logging\r\n - Sandbox and live environments\r\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\r\n\r\n### 5.2 Twilio (SMS)\r\n- **Purpose:** SMS notifications and reminders\r\n- **Features:**\r\n - Send SMS to customers\r\n - Appointment reminders\r\n - SMS consent tracking\r\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\r\n\r\n### 5.3 Setmore (Appointment Booking)\r\n- **Purpose:** Appointment synchronization\r\n- **Features:**\r\n - Pull appointments from Setmore API\r\n - Link to customers/visits\r\n- **Table:** `setmore_appointments`\r\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\r\n\r\n### 5.4 EasyAppointments (Alternative Booking)\r\n- **Purpose:** Full appointment booking platform\r\n- **Location:** C:\\code\\customerdb\\schedule\\\r\n- **Features:**\r\n - Customer/provider management\r\n - Web-based booking widget\r\n - REST API\r\n\r\n### 5.5 Telegram (Notifications)\r\n- **Purpose:** Bot-based notifications\r\n- **Features:**\r\n - Send notifications to channels\r\n - Visit alerts\r\n - Cron job status\r\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\r\n\r\n### 5.6 Ollama (AI - Optional)\r\n- **Purpose:** Local AI for email generation\r\n- **Settings:** `ollama_url`, `ollama_default_model`\r\n\r\n---\r\n\r\n## 6. Scheduled Jobs / Cron\r\n\r\n### Configured Cron Jobs\r\n\r\n| Job Label | Schedule | Purpose |\r\n|----------|----------|---------|\r\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\r\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\r\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\r\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\r\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\r\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\r\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\r\n\r\n### Background Job Scripts (backend/jobs/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **run_morning_jobs.php** | Main morning job runner |\r\n| **smoke_morning_jobs.php** | Smoke test |\r\n| **send_nightly_reports.php** | Nightly report emailer |\r\n| **send_nextday_customer_emails.php** | Next-day reminders |\r\n| **delete_add_name_records.php** | Cleanup job |\r\n| **rebuild_customer_totals.php** | Rebuild totals |\r\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\r\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\r\n| **import_customer_totals_csv.php** | CSV import |\r\n| **backfill_square_customer_log.php** | Square log backfill |\r\n| **run_cron_job.php** | Generic cron runner |\r\n\r\n---\r\n\r\n## 7. Web Applications\r\n\r\n### 7.1 Front Desk App (port 8895)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Main dashboard, search, stats |\r\n| **customer.html** | Customer detail + visit history |\r\n| **visit.html** | Create/edit alteration order |\r\n| **customer_display.html** | Second monitor display |\r\n| **receipt.html** | Printable receipt |\r\n| **reports.html** | Order list by status |\r\n| **upcoming.html** | Next 7 days pickups |\r\n| **schedule.html** | Appointments calendar |\r\n| **login.html** | Login page |\r\n| **item_types.html** | Item type management |\r\n\r\n### 7.2 Management App (port 8894)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Login page |\r\n| **webui.html** | Dashboard |\r\n| **customer.html** | Customer detail view |\r\n| **admin.html** | Reports browser |\r\n| **settings.html** | App settings editor |\r\n| **users.html** | User management |\r\n| **reports.html** | Order reports |\r\n\r\n---\r\n\r\n## 8. Settings & Configuration\r\n\r\n### app_settings Keys\r\n\r\n| Key | Purpose |\r\n|-----|---------|\r\n| theme_bg | Page background color |\r\n| theme_panel | Card/panel color |\r\n| theme_text | Text color |\r\n| theme_accent | Accent/button color |\r\n| theme_accent_deep | Darker accent |\r\n| theme_teal | Teal color |\r\n| theme_font | Font family |\r\n| smtp_host | Email server hostname |\r\n| smtp_port | Email server port |\r\n| smtp_secure | tls or ssl |\r\n| smtp_user | Email login |\r\n| smtp_pass | Email password |\r\n| smtp_from_email | From address |\r\n| smtp_from_name | From display name |\r\n| twilio_sid | Twilio Account SID |\r\n| twilio_token | Twilio Auth Token |\r\n| twilio_from | Twilio phone number |\r\n| imap_host | Incoming email server |\r\n| imap_port | IMAP port |\r\n| imap_ssl | SSL enable |\r\n| imap_user | IMAP login |\r\n| imap_pass | IMAP password |\r\n| square_token | Square API token |\r\n| square_location | Square location ID |\r\n| setmore_staff_key | Setmore API key |\r\n| setmore_refresh | Setmore refresh token |\r\n| require_report | PDF required |\r\n| require_signature | Signature required |\r\n| reports_base_path | Reports disk path |\r\n| reports_base_url | Reports URL |\r\n| ollama_url | Ollama AI URL |\r\n| ollama_default_model | Ollama model |\r\n| telegram_bot_token | Telegram bot token |\r\n| telegram_chat_id | Telegram chat ID |\r\n\r\n---\r\n\r\n## 9. Migration Notes\r\n\r\n### Access to MariaDB Workflow\r\n\r\n1. **Export source data from Access**\r\n - Export ordinary tables to CSV\r\n - Export attachment fields with VBA or DAO Recordset2 logic\r\n - Keep original IDs during migration\r\n\r\n2. **Load into MariaDB staging**\r\n - Use `LOAD DATA LOCAL INFILE` for CSV files\r\n - Load signatures separately into `visit_signatures`\r\n\r\n3. **Normalize**\r\n - `CustomerTable` → `customers`\r\n - `CustomerVisits` → `visits`\r\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\r\n - `2024TaxPayments` → `tax_payments`\r\n - `AlterationItemsChangeLog` → `change_log`\r\n\r\n4. **Signature BLOB path**\r\n - Export Access attachment to temp file\r\n - Convert BMP to PNG\r\n - Insert PNG bytes into `visit_signatures.signature_image`\r\n\r\n5. **Validation**\r\n - Compare source row counts vs target\r\n - Compare distinct legacy IDs\r\n - Compare sample signatures by SHA256\r\n\r\n---\r\n\r\n## 10. Scripts & Tools\r\n\r\n### PowerShell Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\r\n| **sync_backend_mirror.ps1** | Sync backend mirror |\r\n| **check_backend_mirror.ps1** | Check mirror status |\r\n\r\n### Python Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **signature_blob_loader.py** | Load signature blobs |\r\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\r\n| **import_access_v2.py** | Import Access v2 |\r\n| **import_csv_v2.py** | Import CSV v2 |\r\n\r\n### SQL Migrations (migrations/)\r\n\r\n| File | Purpose |\r\n|------|---------|\r\n| **20260407_add_conversion_job.sql** | Add conversion job table |\r\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\r\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\r\n\r\n---\r\n\r\n## File Statistics\r\n\r\n- **Total PHP files:** 200+\r\n- **Total SQL files:** 100+\r\n- **Total HTML pages:** 20+\r\n- **Database tables:** 30+\r\n\r\n---\r\n\r\n*End of Documentation*","content_html":"<h1>Ella's Alterations - Comprehensive System Documentation</h1>\n<p><strong>Generated:</strong> April 17, 2026</p>\n<p><strong>Location:</strong> C:\\code\\customerdb</p>\n<p>---</p>\n<h2>Table of Contents</h2>\n<p>1. [System Overview](#1-system-overview)</p>\n<p>2. [Directory Structure](#2-directory-structure)</p>\n<p>3. [Database Schema](#3-database-schema)</p>\n<p>4. [API Endpoints](#4-api-endpoints)</p>\n<p>5. [External Integrations](#5-external-integrations)</p>\n<p>6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)</p>\n<p>7. [Web Applications](#7-web-applications)</p>\n<p>8. [Settings & Configuration](#8-settings--configuration)</p>\n<p>9. [Migration Notes](#9-migration-notes)</p>\n<p>10. [Scripts & Tools](#10-scripts--tools)</p>\n<p>---</p>\n<h2>1. System Overview</h2>\n<h3>Purpose</h3>\n<p>Ella's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.</p>\n<h3>Server Information</h3>\n<ul>\n<li><strong>Primary Server:</strong> kefa@192.168.7.202</li>\n<li><strong>Mirror Drive:</strong> /mnt/drive2 (mirrors /mnt/drive1)</li>\n<li><strong>Database:</strong> MariaDB - database `ellas_alterations`, user `kefa_admin`</li>\n<li><strong>Web Server:</strong> Apache 2.4 with mod-php</li>\n<li><strong>PHP Version:</strong> 8.3</li>\n</ul>\n<h3>Three Web Applications</h3>\n<p>| App | Local URL | Public URL | Purpose |</p>\n<p>|-----|-----------|------------|---------|</p>\n<p>| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |</p>\n<p>| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |</p>\n<p>| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |</p>\n<p>---</p>\n<h2>2. Directory Structure</h2>\n<p>```</p>\n<p>customerdb/</p>\n<p>├── backend/ # Main PHP backend API and services</p>\n<p>│ ├── api/ # REST API endpoints (~57 files)</p>\n<p>│ ├── bin/ # Binary/executable scripts</p>\n<p>│ ├── backup/ # Backup versions of services</p>\n<p>│ └── jobs/ # Background job scripts</p>\n<p>├── frontend/ # Front Desk app (port 8895)</p>\n<p>│ ├── *.html # HTML pages</p>\n<p>│ ├── api/ # PHP API endpoints</p>\n<p>│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)</p>\n<p>├── webui/ # Management app (port 8894)</p>\n<p>│ ├── *.html # HTML pages</p>\n<p>│ ├── api/ # PHP API endpoints</p>\n<p>│ ├── backend/ # Backend API copy</p>\n<p>│ └── email/ # Email templates</p>\n<p>├── schedule/ # Easy!Appointments installation</p>\n<p>├── schema/ # Database schema files</p>\n<p>├── migrations/ # SQL migrations</p>\n<p>├── scripts/ # PowerShell/Python import scripts</p>\n<p>├── docs/ # Documentation</p>\n<p>├── blog/ # Blog system and reengineering</p>\n<p>├── csv/ # CSV data files</p>\n<p>├── backups/ # Backup archives</p>\n<p>├── Topaz/ # Topaz signature pad configs</p>\n<p>├── webui/ # Second web interface</p>\n<p>│ ├── api/</p>\n<p>│ ├── backend/</p>\n<p>│ └── frontend/</p>\n<p>├── notused/ # Deprecated/unused files</p>\n<p>└── wordpress-plugin/ # WordPress integration</p>\n<p>```</p>\n<p>---</p>\n<h2>3. Database Schema</h2>\n<h3>Core Tables (from mariadb_core_schema.sql)</h3>\n<p>#### customers</p>\n<p>Main customer registry.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| customer_id | INT UNSIGNED PK | Auto-increment |</p>\n<p>| legacy_customer_id | INT NULL | Original Access DB ID |</p>\n<p>| created_at | DATETIME | Record creation time |</p>\n<p>| full_name | VARCHAR(200) | Customer full name |</p>\n<p>| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |</p>\n<p>| email | VARCHAR(255) | Email address |</p>\n<p>| pickup_due_at | DATETIME | Next pickup date (synced from EA) |</p>\n<p>| description | TEXT | Description |</p>\n<p>| notes | TEXT | Internal notes |</p>\n<p>| is_new_customer | TINYINT(1) | 1 if flagged as new |</p>\n<p>#### visits</p>\n<p>One row per alteration job/order.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| visit_id | INT UNSIGNED PK | |</p>\n<p>| legacy_visit_id | INT NULL | Original Access DB ID |</p>\n<p>| customer_id | INT UNSIGNED FK | → customers |</p>\n<p>| visit_date | DATETIME | Drop-off date |</p>\n<p>| pickup_date | DATETIME | Expected pickup |</p>\n<p>| notes | TEXT | General visit notes |</p>\n<p>| customer_name_snapshot | VARCHAR(200) | |</p>\n<p>| phone_snapshot | VARCHAR(30) | |</p>\n<p>| email_snapshot | VARCHAR(255) | |</p>\n<p>| color | VARCHAR(100) | Garment color |</p>\n<p>| alterations_needed | TEXT | |</p>\n<p>| alterations_notes | TEXT | Detailed alteration instructions |</p>\n<p>| alterations_price | DECIMAL(10,2) | Manual price field |</p>\n<p>| total_alteration_price | DECIMAL(10,2) | Calculated from items |</p>\n<p>| expedited_fee | DECIMAL(10,2) | |</p>\n<p>| paid_date | DATETIME | When paid |</p>\n<p>| signature_text | TEXT | Text signature (legacy) |</p>\n<p>| signature_date | DATETIME | |</p>\n<p>| paid_flag | TINYINT(1) | 1 = paid |</p>\n<p>| source_key_value | VARCHAR(100) | |</p>\n<p>#### visit_signatures</p>\n<p>Full resolution signature images per visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| signature_id | BIGINT UNSIGNED PK | |</p>\n<p>| visit_id | INT UNIQUE | One signature per visit |</p>\n<p>| signature_date | DATETIME | |</p>\n<p>| image_format | VARCHAR(50) | Default 'image/png' |</p>\n<p>| original_filename | VARCHAR(255) | |</p>\n<p>| original_extension | VARCHAR(20) | |</p>\n<p>| file_size_bytes | BIGINT | |</p>\n<p>| sha256_hash | CHAR(64) | Hash for verification |</p>\n<p>| signature_image | LONGBLOB | Raw PNG binary |</p>\n<p>| created_at | DATETIME | |</p>\n<p>#### item_types</p>\n<p>Master list of alteration types (e.g., Hem, Zipper, Taper).</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| item_type_id | INT UNSIGNED PK | |</p>\n<p>| type_name | VARCHAR(100) UNIQUE | Display name |</p>\n<p>#### orders</p>\n<p>Orders per visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_id | BIGINT UNSIGNED PK | |</p>\n<p>| visit_id | INT UNSIGNED FK | → visits |</p>\n<p>| customer_id | INT UNSIGNED FK | → customers |</p>\n<p>| created_at | DATETIME | |</p>\n<p>| no_tax | TINYINT(1) | Tax exempt |</p>\n<p>| picked_up | TINYINT(1) | |</p>\n<p>| source_table | VARCHAR(50) | |</p>\n<p>#### order_items</p>\n<p>Line items for each alteration within a visit.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_item_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_id | BIGINT UNSIGNED FK | → orders |</p>\n<p>| legacy_alteration_id | INT NULL | |</p>\n<p>| item_type_id | INT FK | → item_types |</p>\n<p>| item_type_name | VARCHAR(100) | |</p>\n<p>| description | TEXT | |</p>\n<p>| quantity | INT | Default 1 |</p>\n<p>| unit_price | DECIMAL(10,2) | |</p>\n<p>| total_price | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | Per-item notes |</p>\n<p>| color | VARCHAR(100) | Garment color |</p>\n<p>| no_tax | TINYINT(1) | Tax exempt |</p>\n<p>| is_range_price | TINYINT(1) | |</p>\n<p>| range_price_label | VARCHAR(100) | |</p>\n<p>| paid_flag | TINYINT(1) | |</p>\n<p>| paid_type | VARCHAR(100) | |</p>\n<p>| picked_up | TINYINT(1) | |</p>\n<p>| created_at | DATETIME | |</p>\n<p>#### order_item_type_links</p>\n<p>Many-to-many links between order items and item types.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| order_item_type_link_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_item_id | BIGINT UNSIGNED FK | → order_items |</p>\n<p>| item_type_id | INT UNSIGNED FK | → item_types |</p>\n<p>| slot_number | TINYINT UNSIGNED | |</p>\n<p>#### payments</p>\n<p>Payment records per order.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| payment_id | BIGINT UNSIGNED PK | |</p>\n<p>| order_id | BIGINT UNSIGNED FK | → orders |</p>\n<p>| payment_date | DATETIME | |</p>\n<p>| payment_type | VARCHAR(100) | |</p>\n<p>| payment_method | VARCHAR(100) | |</p>\n<p>| amount | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | |</p>\n<p>#### tax_payments</p>\n<p>Tax payment records.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| tax_payment_id | INT UNSIGNED PK | |</p>\n<p>| legacy_tax_payment_id | INT NULL | |</p>\n<p>| payment_date | DATETIME | |</p>\n<p>| payment_type | VARCHAR(100) | |</p>\n<p>| payment_method | VARCHAR(100) | |</p>\n<p>| amount | DECIMAL(10,2) | |</p>\n<p>| notes | TEXT | |</p>\n<p>| receipt_blob | LONGBLOB | |</p>\n<p>#### change_log</p>\n<p>Audit log for changes.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| log_id | BIGINT UNSIGNED PK | |</p>\n<p>| legacy_log_id | INT NULL | |</p>\n<p>| table_name | VARCHAR(100) | |</p>\n<p>| record_id | BIGINT | |</p>\n<p>| visit_id | INT | |</p>\n<p>| field_name | VARCHAR(100) | |</p>\n<p>| old_value | TEXT | |</p>\n<p>| new_value | TEXT | |</p>\n<p>| changed_at | DATETIME | |</p>\n<p>| changed_by | VARCHAR(100) | |</p>\n<p>#### ads</p>\n<p>Advertising/articles.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| ad_id | INT UNSIGNED PK | |</p>\n<p>| legacy_ad_id | INT NULL | |</p>\n<p>| article_text | TEXT | |</p>\n<p>| article_picture | LONGBLOB | |</p>\n<p>| site_submitted | VARCHAR(255) | |</p>\n<p>| views | INT | Default 0 |</p>\n<p>| date_posted | DATETIME | |</p>\n<p>#### staging_* tables</p>\n<p>Staging tables for data migration.</p>\n<p>| Table Name | Purpose |</p>\n<p>|------------|---------|</p>\n<p>| staging_customer_table_raw | Staging for legacy customer data |</p>\n<p>| staging_customer_visits_raw | Staging for legacy visits |</p>\n<p>| staging_alteration_items_raw | Staging for items |</p>\n<h3>Additional Backend Tables</h3>\n<p>| Table Name | Purpose |</p>\n<p>|------------|---------|</p>\n<p>| setmore_appointments | Synced from Setmore |</p>\n<p>| backend_conversion_job | Document conversion jobs |</p>\n<p>| backend_job_runs | Background job execution log |</p>\n<p>| customer_totals | Daily customer totals |</p>\n<p>| daily_customer_totals | Daily summary totals |</p>\n<p>| customer_payment_transactions | Payment ledger |</p>\n<p>| legacy_customer_totals_monthly | Monthly totals (legacy) |</p>\n<p>| legacy_customer_totals_irs_monthly | IRS monthly reports |</p>\n<p>| backend_appointment_workflow | Appointment state machine |</p>\n<p>| backend_no_show_log | No-show tracking |</p>\n<p>| backend_error_log | Error logging |</p>\n<p>| backend_change_log | Change audit log |</p>\n<p>| backend_document | Document storage |</p>\n<p>| backend_document_version | Document version history |</p>\n<p>| backend_speed_dial | Quick links |</p>\n<p>| backend_square_terminal_code | Square terminal pairing codes |</p>\n<p>| backend_square_terminal_device | Square device registry |</p>\n<p>| cron_job | Scheduled job definitions |</p>\n<p>| cron_job_run | Cron execution history |</p>\n<p>| app_settings | Global configuration |</p>\n<p>| app_users | Login accounts |</p>\n<p>| remember_tokens | "Remember Me" tokens |</p>\n<p>| customer_reports | PDF report index |</p>\n<p>---</p>\n<h2>4. API Endpoints</h2>\n<h3>4.1 Frontend API (port 8895 /api/)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>customer_get.php</strong> | Get customer by ID |</p>\n<p>| <strong>customer_save.php</strong> | Create or update customer |</p>\n<p>| <strong>customer_detail.php</strong> | Customer + visits summary |</p>\n<p>| <strong>customer_delete.php</strong> | Delete customer |</p>\n<p>| <strong>customer_list_hide.php</strong> | Hide customer from list |</p>\n<p>| <strong>search_customers.php</strong> | Search by name/phone |</p>\n<p>| <strong>visit_get.php</strong> | Get visit + items |</p>\n<p>| <strong>visit_save.php</strong> | Create/update visit + items |</p>\n<p>| <strong>visit_delete.php</strong> | Delete visit |</p>\n<p>| <strong>visit_report_generate.php</strong> | Generate visit report |</p>\n<p>| <strong>item_types.php</strong> | List all alteration types |</p>\n<p>| <strong>item_type_add.php</strong> | Add new item type |</p>\n<p>| <strong>item_type_update.php</strong> | Update item type |</p>\n<p>| <strong>item_type_delete.php</strong> | Delete item type |</p>\n<p>| <strong>pickups_today.php</strong> | Orders due today |</p>\n<p>| <strong>ready.php</strong> | Orders ready for pickup |</p>\n<p>| <strong>overdue.php</strong> | Overdue orders |</p>\n<p>| <strong>upcoming.php</strong> | Orders due in next N days |</p>\n<p>| <strong>workload_7days.php</strong> | 7-day workload |</p>\n<p>| <strong>save_signature.php</strong> | Save canvas signature |</p>\n<p>| <strong>reports_list.php</strong> | Reports for customer |</p>\n<p>| <strong>attach_reports.php</strong> | Index all PDFs to DB |</p>\n<p>| <strong>save_signature.php</strong> | Save signature image |</p>\n<p>| <strong>today_appointments_list.php</strong> | Today's appointments |</p>\n<p>| <strong>settings_get.php</strong> | Get app settings |</p>\n<p>| <strong>settings_save.php</strong> | Save app settings |</p>\n<p>| <strong>settings_css.php</strong> | Live theme CSS |</p>\n<p>| <strong>send_email.php</strong> | Send email via SMTP |</p>\n<p>| <strong>send_sms.php</strong> | Send SMS via Twilio |</p>\n<p>| <strong>send_reminders.php</strong> | Send appointment reminders |</p>\n<p>| <strong>ea_appointments.php</strong> | EasyAppointments REST API |</p>\n<p>| <strong>ea_webhook.php</strong> | Receives EA webhook events |</p>\n<p>| <strong>setmore.php</strong> | Setmore API proxy |</p>\n<p>| <strong>setmore_to_ea_import.php</strong> | Import customers to EA |</p>\n<p>| <strong>square_config.php</strong> | Square public config |</p>\n<p>| <strong>square_process.php</strong> | Process Square payment |</p>\n<p>| <strong>square_customer_log.php</strong> | Customer Square log |</p>\n<p>| <strong>square_terminal.php</strong> | Square terminal |</p>\n<p>| <strong>login.php</strong> | Authenticate |</p>\n<p>| <strong>logout.php</strong> | Clear session |</p>\n<p>| <strong>session_check.php</strong> | Check if logged in |</p>\n<p>| <strong>auth.php</strong> | Session authentication |</p>\n<p>| <strong>config.php</strong> | App config constants |</p>\n<p>| <strong>db.php</strong> | Database connection |</p>\n<p>| <strong>log_error.php</strong> | Log client-side error |</p>\n<p>| <strong>todo_list.php</strong> | Todo list management |</p>\n<p>| <strong>send_telegram.php</strong> | Send Telegram messages |</p>\n<h3>4.2 Management API (port 8894 /api/)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>login.php</strong> | Authenticate |</p>\n<p>| <strong>logout.php</strong> | Clear session |</p>\n<p>| <strong>session_check.php</strong> | Check if logged in |</p>\n<p>| <strong>customer_detail.php</strong> | Customer + visits |</p>\n<p>| <strong>search_customers.php</strong> | Search customers |</p>\n<p>| <strong>update_customer.php</strong> | Update customer |</p>\n<p>| <strong>users_list.php</strong> | List all users |</p>\n<p>| <strong>user_save.php</strong> | Create/update user |</p>\n<p>| <strong>list_report_dirs.php</strong> | Date folders |</p>\n<p>| <strong>list_report_files.php</strong> | PDFs in folder |</p>\n<p>| <strong>report_file.php</strong> | Access report file |</p>\n<p>| <strong>pickups_today.php</strong> | Today's pickups |</p>\n<p>| <strong>ready.php</strong> | Ready for pickup |</p>\n<p>| <strong>overdue.php</strong> | Overdue |</p>\n<p>| <strong>settings_get.php</strong> | Get settings |</p>\n<p>| <strong>settings_save.php</strong> | Save settings |</p>\n<p>| <strong>settings_css.php</strong> | Live CSS theme |</p>\n<p>| <strong>imap_list.php</strong> | List IMAP folders |</p>\n<p>| <strong>imap_fetch.php</strong> | Fetch emails |</p>\n<p>| <strong>imap_send.php</strong> | Send email via IMAP |</p>\n<p>| <strong>imap_action.php</strong> | IMAP actions |</p>\n<p>| <strong>sms_send_helper.php</strong> | SMS sending helper |</p>\n<p>| <strong>sms_webhook_receive.php</strong> | Receive SMS webhooks |</p>\n<p>| <strong>sms_inbox_api.php</strong> | SMS inbox API |</p>\n<p>| <strong>slideshow_list.php</strong> | Slideshow images |</p>\n<p>| <strong>slideshow_upload.php</strong> | Upload slideshow |</p>\n<p>| <strong>slideshow_delete.php</strong> | Delete slideshow |</p>\n<p>| <strong>users_list.php</strong> | User management |</p>\n<p>| <strong>report_list.php</strong> | Report list |</p>\n<p>| <strong>debug_filters.php</strong> | Debug filters |</p>\n<p>| <strong>sysadmin.php</strong> | System admin |</p>\n<p>| <strong>test.php</strong> | Testing endpoint |</p>\n<h3>4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)</h3>\n<p>| Endpoint File | Purpose |</p>\n<p>|---------------|---------|</p>\n<p>| <strong>customer.php</strong> | Get customer details |</p>\n<p>| <strong>customers.php</strong> | List/search customers |</p>\n<p>| <strong>customer_save.php</strong> | Save/update customer |</p>\n<p>| <strong>visit.php</strong> | Get visit details |</p>\n<p>| <strong>visit_delete.php</strong> | Delete visit |</p>\n<p>| <strong>appointments.php</strong> | Unified appointments |</p>\n<p>| <strong>appointments_summary.php</strong> | Appointment statistics |</p>\n<p>| <strong>setmore_appointments.php</strong> | Setmore appointments |</p>\n<p>| <strong>setmore_import.php</strong> | Import from Setmore |</p>\n<p>| <strong>module_*.php</strong> | Module loaders |</p>\n<p>| <strong>alteration_items.php</strong> | Alteration management |</p>\n<p>| <strong>alteration_items_summary.php</strong> | Alteration statistics |</p>\n<p>| <strong>gold_silver_*.php</strong> | Gold/silver prices |</p>\n<p>| <strong>bank_*.php</strong> | Bank transactions |</p>\n<p>| <strong>stocks_summary.php</strong> | Stock data |</p>\n<p>| <strong>medication_*.php</strong> | Medication tracking |</p>\n<p>| <strong>payroll_import.php</strong> | Payroll import |</p>\n<p>| <strong>car_maintenance_*.php</strong> | Vehicle maintenance |</p>\n<p>| <strong>duke_*.php</strong> | Duke integration |</p>\n<p>| <strong>reports_*.php</strong> | Report generation |</p>\n<p>| <strong>totals_import.php</strong> | Customer totals import |</p>\n<p>| <strong>documents.php</strong> | Document management |</p>\n<p>| <strong>document_upload.php</strong> | File uploads |</p>\n<p>| <strong>cust_email_*.php</strong> | Email data |</p>\n<p>| <strong>mail_*.php</strong> | Email sending |</p>\n<p>| <strong>reminders_create.php</strong> | Create reminders |</p>\n<p>| <strong>nightly_reports.php</strong> | Nightly reports |</p>\n<p>| <strong>morning_jobs.php</strong> | Morning jobs |</p>\n<p>| <strong>appointment_workflow.php</strong> | Workflow management |</p>\n<p>| <strong>sysadmin.php</strong> | System admin |</p>\n<p>| <strong>reporting_module.php</strong> | Reporting |</p>\n<p>| <strong>job_runs.php</strong> | Job tracking |</p>\n<p>| <strong>health.php</strong> | Health check |</p>\n<p>| <strong>overview.php</strong> | Dashboard data |</p>\n<p>---</p>\n<h2>5. External Integrations</h2>\n<h3>5.1 Square (Payment Processing)</h3>\n<ul>\n<li><strong>Purpose:</strong> Point-of-sale payments, receipts, terminal management</li>\n<li><strong>Features:</strong></li>\n<li>Square Terminal device pairing/management</li>\n<li>Payment processing with tips</li>\n<li>Customer payment logging</li>\n<li>Sandbox and live environments</li>\n<li><strong>Tables:</strong> `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`</li>\n</ul>\n<h3>5.2 Twilio (SMS)</h3>\n<ul>\n<li><strong>Purpose:</strong> SMS notifications and reminders</li>\n<li><strong>Features:</strong></li>\n<li>Send SMS to customers</li>\n<li>Appointment reminders</li>\n<li>SMS consent tracking</li>\n<li><strong>Settings:</strong> `twilio_sid`, `twilio_token`, `twilio_from`</li>\n</ul>\n<h3>5.3 Setmore (Appointment Booking)</h3>\n<ul>\n<li><strong>Purpose:</strong> Appointment synchronization</li>\n<li><strong>Features:</strong></li>\n<li>Pull appointments from Setmore API</li>\n<li>Link to customers/visits</li>\n<li><strong>Table:</strong> `setmore_appointments`</li>\n<li><strong>Settings:</strong> `setmore_refresh_token`, `setmore_staff_key`</li>\n</ul>\n<h3>5.4 EasyAppointments (Alternative Booking)</h3>\n<ul>\n<li><strong>Purpose:</strong> Full appointment booking platform</li>\n<li><strong>Location:</strong> C:\\code\\customerdb\\schedule\\</li>\n<li><strong>Features:</strong></li>\n<li>Customer/provider management</li>\n<li>Web-based booking widget</li>\n<li>REST API</li>\n</ul>\n<h3>5.5 Telegram (Notifications)</h3>\n<ul>\n<li><strong>Purpose:</strong> Bot-based notifications</li>\n<li><strong>Features:</strong></li>\n<li>Send notifications to channels</li>\n<li>Visit alerts</li>\n<li>Cron job status</li>\n<li><strong>Settings:</strong> `telegram_bot_token`, `telegram_chat_id`</li>\n</ul>\n<h3>5.6 Ollama (AI - Optional)</h3>\n<ul>\n<li><strong>Purpose:</strong> Local AI for email generation</li>\n<li><strong>Settings:</strong> `ollama_url`, `ollama_default_model`</li>\n</ul>\n<p>---</p>\n<h2>6. Scheduled Jobs / Cron</h2>\n<h3>Configured Cron Jobs</h3>\n<p>| Job Label | Schedule | Purpose |</p>\n<p>|----------|----------|---------|</p>\n<p>| <strong>Next-Day Customer Emails</strong> | `0 20 <em> </em> *` (8 PM) | Pickup reminder emails |</p>\n<p>| <strong>Morning Import</strong> | `0 6 <em> </em> *` (6 AM) | Morning data import |</p>\n<p>| <strong>Morning Jobs</strong> | `0 9 <em> </em> *` (9 AM) | Appointment sync, workflow |</p>\n<p>| <strong>Nightly Reports</strong> | `0 20 <em> </em> *` (8 PM) | Generate nightly summaries |</p>\n<p>| <strong>Database Backup</strong> | `0 2 <em> </em> *` (2 AM) | Full MariaDB backup |</p>\n<p>| <strong>Daily Cleanup</strong> | `0 4 <em> </em> *` (4 AM) | Temp files, old logs |</p>\n<p>| <strong>Auto Delete</strong> | `15 4 <em> </em> *` (4:15 AM) | Clean placeholder records |</p>\n<h3>Background Job Scripts (backend/jobs/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>run_morning_jobs.php</strong> | Main morning job runner |</p>\n<p>| <strong>smoke_morning_jobs.php</strong> | Smoke test |</p>\n<p>| <strong>send_nightly_reports.php</strong> | Nightly report emailer |</p>\n<p>| <strong>send_nextday_customer_emails.php</strong> | Next-day reminders |</p>\n<p>| <strong>delete_add_name_records.php</strong> | Cleanup job |</p>\n<p>| <strong>rebuild_customer_totals.php</strong> | Rebuild totals |</p>\n<p>| <strong>rebuild_daily_customer_totals.php</strong> | Rebuild daily totals |</p>\n<p>| <strong>rebuild_payment_ledger.php</strong> | Rebuild payment ledger |</p>\n<p>| <strong>import_customer_totals_csv.php</strong> | CSV import |</p>\n<p>| <strong>backfill_square_customer_log.php</strong> | Square log backfill |</p>\n<p>| <strong>run_cron_job.php</strong> | Generic cron runner |</p>\n<p>---</p>\n<h2>7. Web Applications</h2>\n<h3>7.1 Front Desk App (port 8895)</h3>\n<p>| Page | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>index.html</strong> | Main dashboard, search, stats |</p>\n<p>| <strong>customer.html</strong> | Customer detail + visit history |</p>\n<p>| <strong>visit.html</strong> | Create/edit alteration order |</p>\n<p>| <strong>customer_display.html</strong> | Second monitor display |</p>\n<p>| <strong>receipt.html</strong> | Printable receipt |</p>\n<p>| <strong>reports.html</strong> | Order list by status |</p>\n<p>| <strong>upcoming.html</strong> | Next 7 days pickups |</p>\n<p>| <strong>schedule.html</strong> | Appointments calendar |</p>\n<p>| <strong>login.html</strong> | Login page |</p>\n<p>| <strong>item_types.html</strong> | Item type management |</p>\n<h3>7.2 Management App (port 8894)</h3>\n<p>| Page | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>index.html</strong> | Login page |</p>\n<p>| <strong>webui.html</strong> | Dashboard |</p>\n<p>| <strong>customer.html</strong> | Customer detail view |</p>\n<p>| <strong>admin.html</strong> | Reports browser |</p>\n<p>| <strong>settings.html</strong> | App settings editor |</p>\n<p>| <strong>users.html</strong> | User management |</p>\n<p>| <strong>reports.html</strong> | Order reports |</p>\n<p>---</p>\n<h2>8. Settings & Configuration</h2>\n<h3>app_settings Keys</h3>\n<p>| Key | Purpose |</p>\n<p>|-----|---------|</p>\n<p>| theme_bg | Page background color |</p>\n<p>| theme_panel | Card/panel color |</p>\n<p>| theme_text | Text color |</p>\n<p>| theme_accent | Accent/button color |</p>\n<p>| theme_accent_deep | Darker accent |</p>\n<p>| theme_teal | Teal color |</p>\n<p>| theme_font | Font family |</p>\n<p>| smtp_host | Email server hostname |</p>\n<p>| smtp_port | Email server port |</p>\n<p>| smtp_secure | tls or ssl |</p>\n<p>| smtp_user | Email login |</p>\n<p>| smtp_pass | Email password |</p>\n<p>| smtp_from_email | From address |</p>\n<p>| smtp_from_name | From display name |</p>\n<p>| twilio_sid | Twilio Account SID |</p>\n<p>| twilio_token | Twilio Auth Token |</p>\n<p>| twilio_from | Twilio phone number |</p>\n<p>| imap_host | Incoming email server |</p>\n<p>| imap_port | IMAP port |</p>\n<p>| imap_ssl | SSL enable |</p>\n<p>| imap_user | IMAP login |</p>\n<p>| imap_pass | IMAP password |</p>\n<p>| square_token | Square API token |</p>\n<p>| square_location | Square location ID |</p>\n<p>| setmore_staff_key | Setmore API key |</p>\n<p>| setmore_refresh | Setmore refresh token |</p>\n<p>| require_report | PDF required |</p>\n<p>| require_signature | Signature required |</p>\n<p>| reports_base_path | Reports disk path |</p>\n<p>| reports_base_url | Reports URL |</p>\n<p>| ollama_url | Ollama AI URL |</p>\n<p>| ollama_default_model | Ollama model |</p>\n<p>| telegram_bot_token | Telegram bot token |</p>\n<p>| telegram_chat_id | Telegram chat ID |</p>\n<p>---</p>\n<h2>9. Migration Notes</h2>\n<h3>Access to MariaDB Workflow</h3>\n<p>1. <strong>Export source data from Access</strong></p>\n<ul>\n<li>Export ordinary tables to CSV</li>\n<li>Export attachment fields with VBA or DAO Recordset2 logic</li>\n<li>Keep original IDs during migration</li>\n</ul>\n<p>2. <strong>Load into MariaDB staging</strong></p>\n<ul>\n<li>Use `LOAD DATA LOCAL INFILE` for CSV files</li>\n<li>Load signatures separately into `visit_signatures`</li>\n</ul>\n<p>3. <strong>Normalize</strong></p>\n<ul>\n<li>`CustomerTable` → `customers`</li>\n<li>`CustomerVisits` → `visits`</li>\n<li>`AlterationItems` → `orders`, `order_items`, `order_item_type_links`</li>\n<li>`2024TaxPayments` → `tax_payments`</li>\n<li>`AlterationItemsChangeLog` → `change_log`</li>\n</ul>\n<p>4. <strong>Signature BLOB path</strong></p>\n<ul>\n<li>Export Access attachment to temp file</li>\n<li>Convert BMP to PNG</li>\n<li>Insert PNG bytes into `visit_signatures.signature_image`</li>\n</ul>\n<p>5. <strong>Validation</strong></p>\n<ul>\n<li>Compare source row counts vs target</li>\n<li>Compare distinct legacy IDs</li>\n<li>Compare sample signatures by SHA256</li>\n</ul>\n<p>---</p>\n<h2>10. Scripts & Tools</h2>\n<h3>PowerShell Scripts (scripts/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>import_legacy_totals_from_excel.ps1</strong> | Import totals from Excel |</p>\n<p>| <strong>sync_backend_mirror.ps1</strong> | Sync backend mirror |</p>\n<p>| <strong>check_backend_mirror.ps1</strong> | Check mirror status |</p>\n<h3>Python Scripts (scripts/)</h3>\n<p>| Script | Purpose |</p>\n<p>|--------|---------|</p>\n<p>| <strong>signature_blob_loader.py</strong> | Load signature blobs |</p>\n<p>| <strong>access_to_mariadb_parser.py</strong> | Parse Access to MariaDB |</p>\n<p>| <strong>import_access_v2.py</strong> | Import Access v2 |</p>\n<p>| <strong>import_csv_v2.py</strong> | Import CSV v2 |</p>\n<h3>SQL Migrations (migrations/)</h3>\n<p>| File | Purpose |</p>\n<p>|------|---------|</p>\n<p>| <strong>20260407_add_conversion_job.sql</strong> | Add conversion job table |</p>\n<p>| <strong>20260411_order_items_item_type_name_no_fk.sql</strong> | Order items no FK |</p>\n<p>| <strong>20260416_blog_prompt_templates.sql</strong> | Blog prompt templates |</p>\n<p>---</p>\n<h2>File Statistics</h2>\n<ul>\n<li><strong>Total PHP files:</strong> 200+</li>\n<li><strong>Total SQL files:</strong> 100+</li>\n<li><strong>Total HTML pages:</strong> 20+</li>\n<li><strong>Database tables:</strong> 30+</li>\n</ul>\n<p>---</p>\n<p><em>End of Documentation</em></p>","file_name":"COMPREHENSIVE_DOCUMENTATION.md","stored_name":"20260417-160828-6006d4f1.md","mime_type":"application/octet-stream","file_size_bytes":"22313","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-160828-6006d4f1.md","is_deleted":"0","created_at":"2026-04-17 12:08:29","updated_at":"2026-04-17 12:08:52","editor_content":"# Ella's Alterations - Comprehensive System Documentation\r\n\r\n**Generated:** April 17, 2026\r\n**Location:** C:\\code\\customerdb\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n1. [System Overview](#1-system-overview)\r\n2. [Directory Structure](#2-directory-structure)\r\n3. [Database Schema](#3-database-schema)\r\n4. [API Endpoints](#4-api-endpoints)\r\n5. [External Integrations](#5-external-integrations)\r\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\r\n7. [Web Applications](#7-web-applications)\r\n8. [Settings & Configuration](#8-settings--configuration)\r\n9. [Migration Notes](#9-migration-notes)\r\n10. [Scripts & Tools](#10-scripts--tools)\r\n\r\n---\r\n\r\n## 1. System Overview\r\n\r\n### Purpose\r\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\r\n\r\n### Server Information\r\n- **Primary Server:** kefa@192.168.7.202\r\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\r\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\r\n- **Web Server:** Apache 2.4 with mod-php\r\n- **PHP Version:** 8.3\r\n\r\n### Three Web Applications\r\n\r\n| App | Local URL | Public URL | Purpose |\r\n|-----|-----------|------------|---------|\r\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\r\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\r\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\r\n\r\n---\r\n\r\n## 2. Directory Structure\r\n\r\n```\r\ncustomerdb/\r\n├── backend/ # Main PHP backend API and services\r\n│ ├── api/ # REST API endpoints (~57 files)\r\n│ ├── bin/ # Binary/executable scripts\r\n│ ├── backup/ # Backup versions of services\r\n│ └── jobs/ # Background job scripts\r\n├── frontend/ # Front Desk app (port 8895)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\r\n├── webui/ # Management app (port 8894)\r\n│ ├── *.html # HTML pages\r\n│ ├── api/ # PHP API endpoints\r\n│ ├── backend/ # Backend API copy\r\n│ └── email/ # Email templates\r\n├── schedule/ # Easy!Appointments installation\r\n├── schema/ # Database schema files\r\n├── migrations/ # SQL migrations\r\n├── scripts/ # PowerShell/Python import scripts\r\n├── docs/ # Documentation\r\n├── blog/ # Blog system and reengineering\r\n├── csv/ # CSV data files\r\n├── backups/ # Backup archives\r\n├── Topaz/ # Topaz signature pad configs\r\n├── webui/ # Second web interface\r\n│ ├── api/\r\n│ ├── backend/\r\n│ └── frontend/\r\n├── notused/ # Deprecated/unused files\r\n└── wordpress-plugin/ # WordPress integration\r\n```\r\n\r\n---\r\n\r\n## 3. Database Schema\r\n\r\n### Core Tables (from mariadb_core_schema.sql)\r\n\r\n#### customers\r\nMain customer registry.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| customer_id | INT UNSIGNED PK | Auto-increment |\r\n| legacy_customer_id | INT NULL | Original Access DB ID |\r\n| created_at | DATETIME | Record creation time |\r\n| full_name | VARCHAR(200) | Customer full name |\r\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\r\n| email | VARCHAR(255) | Email address |\r\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\r\n| description | TEXT | Description |\r\n| notes | TEXT | Internal notes |\r\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\r\n\r\n#### visits\r\nOne row per alteration job/order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| visit_id | INT UNSIGNED PK | |\r\n| legacy_visit_id | INT NULL | Original Access DB ID |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| visit_date | DATETIME | Drop-off date |\r\n| pickup_date | DATETIME | Expected pickup |\r\n| notes | TEXT | General visit notes |\r\n| customer_name_snapshot | VARCHAR(200) | |\r\n| phone_snapshot | VARCHAR(30) | |\r\n| email_snapshot | VARCHAR(255) | |\r\n| color | VARCHAR(100) | Garment color |\r\n| alterations_needed | TEXT | |\r\n| alterations_notes | TEXT | Detailed alteration instructions |\r\n| alterations_price | DECIMAL(10,2) | Manual price field |\r\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\r\n| expedited_fee | DECIMAL(10,2) | |\r\n| paid_date | DATETIME | When paid |\r\n| signature_text | TEXT | Text signature (legacy) |\r\n| signature_date | DATETIME | |\r\n| paid_flag | TINYINT(1) | 1 = paid |\r\n| source_key_value | VARCHAR(100) | |\r\n\r\n#### visit_signatures\r\nFull resolution signature images per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| signature_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNIQUE | One signature per visit |\r\n| signature_date | DATETIME | |\r\n| image_format | VARCHAR(50) | Default 'image/png' |\r\n| original_filename | VARCHAR(255) | |\r\n| original_extension | VARCHAR(20) | |\r\n| file_size_bytes | BIGINT | |\r\n| sha256_hash | CHAR(64) | Hash for verification |\r\n| signature_image | LONGBLOB | Raw PNG binary |\r\n| created_at | DATETIME | |\r\n\r\n#### item_types\r\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| item_type_id | INT UNSIGNED PK | |\r\n| type_name | VARCHAR(100) UNIQUE | Display name |\r\n\r\n#### orders\r\nOrders per visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_id | BIGINT UNSIGNED PK | |\r\n| visit_id | INT UNSIGNED FK | → visits |\r\n| customer_id | INT UNSIGNED FK | → customers |\r\n| created_at | DATETIME | |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| picked_up | TINYINT(1) | |\r\n| source_table | VARCHAR(50) | |\r\n\r\n#### order_items\r\nLine items for each alteration within a visit.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| legacy_alteration_id | INT NULL | |\r\n| item_type_id | INT FK | → item_types |\r\n| item_type_name | VARCHAR(100) | |\r\n| description | TEXT | |\r\n| quantity | INT | Default 1 |\r\n| unit_price | DECIMAL(10,2) | |\r\n| total_price | DECIMAL(10,2) | |\r\n| notes | TEXT | Per-item notes |\r\n| color | VARCHAR(100) | Garment color |\r\n| no_tax | TINYINT(1) | Tax exempt |\r\n| is_range_price | TINYINT(1) | |\r\n| range_price_label | VARCHAR(100) | |\r\n| paid_flag | TINYINT(1) | |\r\n| paid_type | VARCHAR(100) | |\r\n| picked_up | TINYINT(1) | |\r\n| created_at | DATETIME | |\r\n\r\n#### order_item_type_links\r\nMany-to-many links between order items and item types.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\r\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\r\n| item_type_id | INT UNSIGNED FK | → item_types |\r\n| slot_number | TINYINT UNSIGNED | |\r\n\r\n#### payments\r\nPayment records per order.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| payment_id | BIGINT UNSIGNED PK | |\r\n| order_id | BIGINT UNSIGNED FK | → orders |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n\r\n#### tax_payments\r\nTax payment records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| tax_payment_id | INT UNSIGNED PK | |\r\n| legacy_tax_payment_id | INT NULL | |\r\n| payment_date | DATETIME | |\r\n| payment_type | VARCHAR(100) | |\r\n| payment_method | VARCHAR(100) | |\r\n| amount | DECIMAL(10,2) | |\r\n| notes | TEXT | |\r\n| receipt_blob | LONGBLOB | |\r\n\r\n#### change_log\r\nAudit log for changes.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| log_id | BIGINT UNSIGNED PK | |\r\n| legacy_log_id | INT NULL | |\r\n| table_name | VARCHAR(100) | |\r\n| record_id | BIGINT | |\r\n| visit_id | INT | |\r\n| field_name | VARCHAR(100) | |\r\n| old_value | TEXT | |\r\n| new_value | TEXT | |\r\n| changed_at | DATETIME | |\r\n| changed_by | VARCHAR(100) | |\r\n\r\n#### ads\r\nAdvertising/articles.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| ad_id | INT UNSIGNED PK | |\r\n| legacy_ad_id | INT NULL | |\r\n| article_text | TEXT | |\r\n| article_picture | LONGBLOB | |\r\n| site_submitted | VARCHAR(255) | |\r\n| views | INT | Default 0 |\r\n| date_posted | DATETIME | |\r\n\r\n#### staging_* tables\r\nStaging tables for data migration.\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| staging_customer_table_raw | Staging for legacy customer data |\r\n| staging_customer_visits_raw | Staging for legacy visits |\r\n| staging_alteration_items_raw | Staging for items |\r\n\r\n### Additional Backend Tables\r\n\r\n| Table Name | Purpose |\r\n|------------|---------|\r\n| setmore_appointments | Synced from Setmore |\r\n| backend_conversion_job | Document conversion jobs |\r\n| backend_job_runs | Background job execution log |\r\n| customer_totals | Daily customer totals |\r\n| daily_customer_totals | Daily summary totals |\r\n| customer_payment_transactions | Payment ledger |\r\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\r\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\r\n| backend_appointment_workflow | Appointment state machine |\r\n| backend_no_show_log | No-show tracking |\r\n| backend_error_log | Error logging |\r\n| backend_change_log | Change audit log |\r\n| backend_document | Document storage |\r\n| backend_document_version | Document version history |\r\n| backend_speed_dial | Quick links |\r\n| backend_square_terminal_code | Square terminal pairing codes |\r\n| backend_square_terminal_device | Square device registry |\r\n| cron_job | Scheduled job definitions |\r\n| cron_job_run | Cron execution history |\r\n| app_settings | Global configuration |\r\n| app_users | Login accounts |\r\n| remember_tokens | \"Remember Me\" tokens |\r\n| customer_reports | PDF report index |\r\n\r\n---\r\n\r\n## 4. API Endpoints\r\n\r\n### 4.1 Frontend API (port 8895 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer_get.php** | Get customer by ID |\r\n| **customer_save.php** | Create or update customer |\r\n| **customer_detail.php** | Customer + visits summary |\r\n| **customer_delete.php** | Delete customer |\r\n| **customer_list_hide.php** | Hide customer from list |\r\n| **search_customers.php** | Search by name/phone |\r\n| **visit_get.php** | Get visit + items |\r\n| **visit_save.php** | Create/update visit + items |\r\n| **visit_delete.php** | Delete visit |\r\n| **visit_report_generate.php** | Generate visit report |\r\n| **item_types.php** | List all alteration types |\r\n| **item_type_add.php** | Add new item type |\r\n| **item_type_update.php** | Update item type |\r\n| **item_type_delete.php** | Delete item type |\r\n| **pickups_today.php** | Orders due today |\r\n| **ready.php** | Orders ready for pickup |\r\n| **overdue.php** | Overdue orders |\r\n| **upcoming.php** | Orders due in next N days |\r\n| **workload_7days.php** | 7-day workload |\r\n| **save_signature.php** | Save canvas signature |\r\n| **reports_list.php** | Reports for customer |\r\n| **attach_reports.php** | Index all PDFs to DB |\r\n| **save_signature.php** | Save signature image |\r\n| **today_appointments_list.php** | Today's appointments |\r\n| **settings_get.php** | Get app settings |\r\n| **settings_save.php** | Save app settings |\r\n| **settings_css.php** | Live theme CSS |\r\n| **send_email.php** | Send email via SMTP |\r\n| **send_sms.php** | Send SMS via Twilio |\r\n| **send_reminders.php** | Send appointment reminders |\r\n| **ea_appointments.php** | EasyAppointments REST API |\r\n| **ea_webhook.php** | Receives EA webhook events |\r\n| **setmore.php** | Setmore API proxy |\r\n| **setmore_to_ea_import.php** | Import customers to EA |\r\n| **square_config.php** | Square public config |\r\n| **square_process.php** | Process Square payment |\r\n| **square_customer_log.php** | Customer Square log |\r\n| **square_terminal.php** | Square terminal |\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **auth.php** | Session authentication |\r\n| **config.php** | App config constants |\r\n| **db.php** | Database connection |\r\n| **log_error.php** | Log client-side error |\r\n| **todo_list.php** | Todo list management |\r\n| **send_telegram.php** | Send Telegram messages |\r\n\r\n### 4.2 Management API (port 8894 /api/)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **login.php** | Authenticate |\r\n| **logout.php** | Clear session |\r\n| **session_check.php** | Check if logged in |\r\n| **customer_detail.php** | Customer + visits |\r\n| **search_customers.php** | Search customers |\r\n| **update_customer.php** | Update customer |\r\n| **users_list.php** | List all users |\r\n| **user_save.php** | Create/update user |\r\n| **list_report_dirs.php** | Date folders |\r\n| **list_report_files.php** | PDFs in folder |\r\n| **report_file.php** | Access report file |\r\n| **pickups_today.php** | Today's pickups |\r\n| **ready.php** | Ready for pickup |\r\n| **overdue.php** | Overdue |\r\n| **settings_get.php** | Get settings |\r\n| **settings_save.php** | Save settings |\r\n| **settings_css.php** | Live CSS theme |\r\n| **imap_list.php** | List IMAP folders |\r\n| **imap_fetch.php** | Fetch emails |\r\n| **imap_send.php** | Send email via IMAP |\r\n| **imap_action.php** | IMAP actions |\r\n| **sms_send_helper.php** | SMS sending helper |\r\n| **sms_webhook_receive.php** | Receive SMS webhooks |\r\n| **sms_inbox_api.php** | SMS inbox API |\r\n| **slideshow_list.php** | Slideshow images |\r\n| **slideshow_upload.php** | Upload slideshow |\r\n| **slideshow_delete.php** | Delete slideshow |\r\n| **users_list.php** | User management |\r\n| **report_list.php** | Report list |\r\n| **debug_filters.php** | Debug filters |\r\n| **sysadmin.php** | System admin |\r\n| **test.php** | Testing endpoint |\r\n\r\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\r\n\r\n| Endpoint File | Purpose |\r\n|---------------|---------|\r\n| **customer.php** | Get customer details |\r\n| **customers.php** | List/search customers |\r\n| **customer_save.php** | Save/update customer |\r\n| **visit.php** | Get visit details |\r\n| **visit_delete.php** | Delete visit |\r\n| **appointments.php** | Unified appointments |\r\n| **appointments_summary.php** | Appointment statistics |\r\n| **setmore_appointments.php** | Setmore appointments |\r\n| **setmore_import.php** | Import from Setmore |\r\n| **module_*.php** | Module loaders |\r\n| **alteration_items.php** | Alteration management |\r\n| **alteration_items_summary.php** | Alteration statistics |\r\n| **gold_silver_*.php** | Gold/silver prices |\r\n| **bank_*.php** | Bank transactions |\r\n| **stocks_summary.php** | Stock data |\r\n| **medication_*.php** | Medication tracking |\r\n| **payroll_import.php** | Payroll import |\r\n| **car_maintenance_*.php** | Vehicle maintenance |\r\n| **duke_*.php** | Duke integration |\r\n| **reports_*.php** | Report generation |\r\n| **totals_import.php** | Customer totals import |\r\n| **documents.php** | Document management |\r\n| **document_upload.php** | File uploads |\r\n| **cust_email_*.php** | Email data |\r\n| **mail_*.php** | Email sending |\r\n| **reminders_create.php** | Create reminders |\r\n| **nightly_reports.php** | Nightly reports |\r\n| **morning_jobs.php** | Morning jobs |\r\n| **appointment_workflow.php** | Workflow management |\r\n| **sysadmin.php** | System admin |\r\n| **reporting_module.php** | Reporting |\r\n| **job_runs.php** | Job tracking |\r\n| **health.php** | Health check |\r\n| **overview.php** | Dashboard data |\r\n\r\n---\r\n\r\n## 5. External Integrations\r\n\r\n### 5.1 Square (Payment Processing)\r\n- **Purpose:** Point-of-sale payments, receipts, terminal management\r\n- **Features:**\r\n - Square Terminal device pairing/management\r\n - Payment processing with tips\r\n - Customer payment logging\r\n - Sandbox and live environments\r\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\r\n\r\n### 5.2 Twilio (SMS)\r\n- **Purpose:** SMS notifications and reminders\r\n- **Features:**\r\n - Send SMS to customers\r\n - Appointment reminders\r\n - SMS consent tracking\r\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\r\n\r\n### 5.3 Setmore (Appointment Booking)\r\n- **Purpose:** Appointment synchronization\r\n- **Features:**\r\n - Pull appointments from Setmore API\r\n - Link to customers/visits\r\n- **Table:** `setmore_appointments`\r\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\r\n\r\n### 5.4 EasyAppointments (Alternative Booking)\r\n- **Purpose:** Full appointment booking platform\r\n- **Location:** C:\\code\\customerdb\\schedule\\\r\n- **Features:**\r\n - Customer/provider management\r\n - Web-based booking widget\r\n - REST API\r\n\r\n### 5.5 Telegram (Notifications)\r\n- **Purpose:** Bot-based notifications\r\n- **Features:**\r\n - Send notifications to channels\r\n - Visit alerts\r\n - Cron job status\r\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\r\n\r\n### 5.6 Ollama (AI - Optional)\r\n- **Purpose:** Local AI for email generation\r\n- **Settings:** `ollama_url`, `ollama_default_model`\r\n\r\n---\r\n\r\n## 6. Scheduled Jobs / Cron\r\n\r\n### Configured Cron Jobs\r\n\r\n| Job Label | Schedule | Purpose |\r\n|----------|----------|---------|\r\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\r\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\r\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\r\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\r\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\r\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\r\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\r\n\r\n### Background Job Scripts (backend/jobs/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **run_morning_jobs.php** | Main morning job runner |\r\n| **smoke_morning_jobs.php** | Smoke test |\r\n| **send_nightly_reports.php** | Nightly report emailer |\r\n| **send_nextday_customer_emails.php** | Next-day reminders |\r\n| **delete_add_name_records.php** | Cleanup job |\r\n| **rebuild_customer_totals.php** | Rebuild totals |\r\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\r\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\r\n| **import_customer_totals_csv.php** | CSV import |\r\n| **backfill_square_customer_log.php** | Square log backfill |\r\n| **run_cron_job.php** | Generic cron runner |\r\n\r\n---\r\n\r\n## 7. Web Applications\r\n\r\n### 7.1 Front Desk App (port 8895)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Main dashboard, search, stats |\r\n| **customer.html** | Customer detail + visit history |\r\n| **visit.html** | Create/edit alteration order |\r\n| **customer_display.html** | Second monitor display |\r\n| **receipt.html** | Printable receipt |\r\n| **reports.html** | Order list by status |\r\n| **upcoming.html** | Next 7 days pickups |\r\n| **schedule.html** | Appointments calendar |\r\n| **login.html** | Login page |\r\n| **item_types.html** | Item type management |\r\n\r\n### 7.2 Management App (port 8894)\r\n\r\n| Page | Purpose |\r\n|------|---------|\r\n| **index.html** | Login page |\r\n| **webui.html** | Dashboard |\r\n| **customer.html** | Customer detail view |\r\n| **admin.html** | Reports browser |\r\n| **settings.html** | App settings editor |\r\n| **users.html** | User management |\r\n| **reports.html** | Order reports |\r\n\r\n---\r\n\r\n## 8. Settings & Configuration\r\n\r\n### app_settings Keys\r\n\r\n| Key | Purpose |\r\n|-----|---------|\r\n| theme_bg | Page background color |\r\n| theme_panel | Card/panel color |\r\n| theme_text | Text color |\r\n| theme_accent | Accent/button color |\r\n| theme_accent_deep | Darker accent |\r\n| theme_teal | Teal color |\r\n| theme_font | Font family |\r\n| smtp_host | Email server hostname |\r\n| smtp_port | Email server port |\r\n| smtp_secure | tls or ssl |\r\n| smtp_user | Email login |\r\n| smtp_pass | Email password |\r\n| smtp_from_email | From address |\r\n| smtp_from_name | From display name |\r\n| twilio_sid | Twilio Account SID |\r\n| twilio_token | Twilio Auth Token |\r\n| twilio_from | Twilio phone number |\r\n| imap_host | Incoming email server |\r\n| imap_port | IMAP port |\r\n| imap_ssl | SSL enable |\r\n| imap_user | IMAP login |\r\n| imap_pass | IMAP password |\r\n| square_token | Square API token |\r\n| square_location | Square location ID |\r\n| setmore_staff_key | Setmore API key |\r\n| setmore_refresh | Setmore refresh token |\r\n| require_report | PDF required |\r\n| require_signature | Signature required |\r\n| reports_base_path | Reports disk path |\r\n| reports_base_url | Reports URL |\r\n| ollama_url | Ollama AI URL |\r\n| ollama_default_model | Ollama model |\r\n| telegram_bot_token | Telegram bot token |\r\n| telegram_chat_id | Telegram chat ID |\r\n\r\n---\r\n\r\n## 9. Migration Notes\r\n\r\n### Access to MariaDB Workflow\r\n\r\n1. **Export source data from Access**\r\n - Export ordinary tables to CSV\r\n - Export attachment fields with VBA or DAO Recordset2 logic\r\n - Keep original IDs during migration\r\n\r\n2. **Load into MariaDB staging**\r\n - Use `LOAD DATA LOCAL INFILE` for CSV files\r\n - Load signatures separately into `visit_signatures`\r\n\r\n3. **Normalize**\r\n - `CustomerTable` → `customers`\r\n - `CustomerVisits` → `visits`\r\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\r\n - `2024TaxPayments` → `tax_payments`\r\n - `AlterationItemsChangeLog` → `change_log`\r\n\r\n4. **Signature BLOB path**\r\n - Export Access attachment to temp file\r\n - Convert BMP to PNG\r\n - Insert PNG bytes into `visit_signatures.signature_image`\r\n\r\n5. **Validation**\r\n - Compare source row counts vs target\r\n - Compare distinct legacy IDs\r\n - Compare sample signatures by SHA256\r\n\r\n---\r\n\r\n## 10. Scripts & Tools\r\n\r\n### PowerShell Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\r\n| **sync_backend_mirror.ps1** | Sync backend mirror |\r\n| **check_backend_mirror.ps1** | Check mirror status |\r\n\r\n### Python Scripts (scripts/)\r\n\r\n| Script | Purpose |\r\n|--------|---------|\r\n| **signature_blob_loader.py** | Load signature blobs |\r\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\r\n| **import_access_v2.py** | Import Access v2 |\r\n| **import_csv_v2.py** | Import CSV v2 |\r\n\r\n### SQL Migrations (migrations/)\r\n\r\n| File | Purpose |\r\n|------|---------|\r\n| **20260407_add_conversion_job.sql** | Add conversion job table |\r\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\r\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\r\n\r\n---\r\n\r\n## File Statistics\r\n\r\n- **Total PHP files:** 200+\r\n- **Total SQL files:** 100+\r\n- **Total HTML pages:** 20+\r\n- **Database tables:** 30+\r\n\r\n---\r\n\r\n*End of Documentation*","is_text_editable":1,"can_edit_inline":1}
{"file_name":"COMPREHENSIVE_DOCUMENTATION.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"20","document_type":"upload","title":"Complete Documentation up to date","slug":"complete-documentation-up-to-date","summary_text":null,"content_markdown":null,"content_html":null,"file_name":"COMPREHENSIVE_DOCUMENTATION.md","stored_name":"20260417-160828-6006d4f1.md","mime_type":"application/octet-stream","file_size_bytes":"22313","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260417-160828-6006d4f1.md","is_deleted":"0","created_at":"2026-04-17 12:08:29","updated_at":"2026-04-17 12:08:29","editor_content":"# Ella's Alterations - Comprehensive System Documentation\n\n**Generated:** April 17, 2026\n**Location:** C:\\code\\customerdb\n\n---\n\n## Table of Contents\n\n1. [System Overview](#1-system-overview)\n2. [Directory Structure](#2-directory-structure)\n3. [Database Schema](#3-database-schema)\n4. [API Endpoints](#4-api-endpoints)\n5. [External Integrations](#5-external-integrations)\n6. [Scheduled Jobs / Cron](#6-scheduled-jobs--cron)\n7. [Web Applications](#7-web-applications)\n8. [Settings & Configuration](#8-settings--configuration)\n9. [Migration Notes](#9-migration-notes)\n10. [Scripts & Tools](#10-scripts--tools)\n\n---\n\n## 1. System Overview\n\n### Purpose\nElla's Alterations is a comprehensive customer relationship management (CRM) andAlteration tracking system for a tailor shop. It manages customers, visits/alteration orders, appointments, payments, and provides integration with external services.\n\n### Server Information\n- **Primary Server:** kefa@192.168.7.202\n- **Mirror Drive:** /mnt/drive2 (mirrors /mnt/drive1)\n- **Database:** MariaDB - database `ellas_alterations`, user `kefa_admin`\n- **Web Server:** Apache 2.4 with mod-php\n- **PHP Version:** 8.3\n\n### Three Web Applications\n\n| App | Local URL | Public URL | Purpose |\n|-----|-----------|------------|---------|\n| Front Desk | http://192.168.7.202:8895 | https://ella.floridaalterations.com | Customer-facing operations |\n| Management | http://192.168.7.202:8894 | (LAN only) | Reports, admin, settings |\n| Scheduler | http://192.168.7.202:8897 | https://schedule.floridaalterations.com | Easy!Appointments booking |\n\n---\n\n## 2. Directory Structure\n\n```\ncustomerdb/\n├── backend/ # Main PHP backend API and services\n│ ├── api/ # REST API endpoints (~57 files)\n│ ├── bin/ # Binary/executable scripts\n│ ├── backup/ # Backup versions of services\n│ └── jobs/ # Background job scripts\n├── frontend/ # Front Desk app (port 8895)\n│ ├── *.html # HTML pages\n│ ├── api/ # PHP API endpoints\n│ └── reports/ # PDF reports (MMDDYYYY/filename.pdf)\n├── webui/ # Management app (port 8894)\n│ ├── *.html # HTML pages\n│ ├── api/ # PHP API endpoints\n│ ├── backend/ # Backend API copy\n│ └── email/ # Email templates\n├── schedule/ # Easy!Appointments installation\n├── schema/ # Database schema files\n├── migrations/ # SQL migrations\n├── scripts/ # PowerShell/Python import scripts\n├── docs/ # Documentation\n├── blog/ # Blog system and reengineering\n├── csv/ # CSV data files\n├── backups/ # Backup archives\n├── Topaz/ # Topaz signature pad configs\n├── webui/ # Second web interface\n│ ├── api/\n│ ├── backend/\n│ └── frontend/\n├── notused/ # Deprecated/unused files\n└── wordpress-plugin/ # WordPress integration\n```\n\n---\n\n## 3. Database Schema\n\n### Core Tables (from mariadb_core_schema.sql)\n\n#### customers\nMain customer registry.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| customer_id | INT UNSIGNED PK | Auto-increment |\n| legacy_customer_id | INT NULL | Original Access DB ID |\n| created_at | DATETIME | Record creation time |\n| full_name | VARCHAR(200) | Customer full name |\n| phone | VARCHAR(30) | Formatted (XXX) XXX-XXXX |\n| email | VARCHAR(255) | Email address |\n| pickup_due_at | DATETIME | Next pickup date (synced from EA) |\n| description | TEXT | Description |\n| notes | TEXT | Internal notes |\n| is_new_customer | TINYINT(1) | 1 if flagged as new |\n\n#### visits\nOne row per alteration job/order.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| visit_id | INT UNSIGNED PK | |\n| legacy_visit_id | INT NULL | Original Access DB ID |\n| customer_id | INT UNSIGNED FK | → customers |\n| visit_date | DATETIME | Drop-off date |\n| pickup_date | DATETIME | Expected pickup |\n| notes | TEXT | General visit notes |\n| customer_name_snapshot | VARCHAR(200) | |\n| phone_snapshot | VARCHAR(30) | |\n| email_snapshot | VARCHAR(255) | |\n| color | VARCHAR(100) | Garment color |\n| alterations_needed | TEXT | |\n| alterations_notes | TEXT | Detailed alteration instructions |\n| alterations_price | DECIMAL(10,2) | Manual price field |\n| total_alteration_price | DECIMAL(10,2) | Calculated from items |\n| expedited_fee | DECIMAL(10,2) | |\n| paid_date | DATETIME | When paid |\n| signature_text | TEXT | Text signature (legacy) |\n| signature_date | DATETIME | |\n| paid_flag | TINYINT(1) | 1 = paid |\n| source_key_value | VARCHAR(100) | |\n\n#### visit_signatures\nFull resolution signature images per visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| signature_id | BIGINT UNSIGNED PK | |\n| visit_id | INT UNIQUE | One signature per visit |\n| signature_date | DATETIME | |\n| image_format | VARCHAR(50) | Default 'image/png' |\n| original_filename | VARCHAR(255) | |\n| original_extension | VARCHAR(20) | |\n| file_size_bytes | BIGINT | |\n| sha256_hash | CHAR(64) | Hash for verification |\n| signature_image | LONGBLOB | Raw PNG binary |\n| created_at | DATETIME | |\n\n#### item_types\nMaster list of alteration types (e.g., Hem, Zipper, Taper).\n\n| Column | Type | Notes |\n|--------|------|-------|\n| item_type_id | INT UNSIGNED PK | |\n| type_name | VARCHAR(100) UNIQUE | Display name |\n\n#### orders\nOrders per visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_id | BIGINT UNSIGNED PK | |\n| visit_id | INT UNSIGNED FK | → visits |\n| customer_id | INT UNSIGNED FK | → customers |\n| created_at | DATETIME | |\n| no_tax | TINYINT(1) | Tax exempt |\n| picked_up | TINYINT(1) | |\n| source_table | VARCHAR(50) | |\n\n#### order_items\nLine items for each alteration within a visit.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_item_id | BIGINT UNSIGNED PK | |\n| order_id | BIGINT UNSIGNED FK | → orders |\n| legacy_alteration_id | INT NULL | |\n| item_type_id | INT FK | → item_types |\n| item_type_name | VARCHAR(100) | |\n| description | TEXT | |\n| quantity | INT | Default 1 |\n| unit_price | DECIMAL(10,2) | |\n| total_price | DECIMAL(10,2) | |\n| notes | TEXT | Per-item notes |\n| color | VARCHAR(100) | Garment color |\n| no_tax | TINYINT(1) | Tax exempt |\n| is_range_price | TINYINT(1) | |\n| range_price_label | VARCHAR(100) | |\n| paid_flag | TINYINT(1) | |\n| paid_type | VARCHAR(100) | |\n| picked_up | TINYINT(1) | |\n| created_at | DATETIME | |\n\n#### order_item_type_links\nMany-to-many links between order items and item types.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| order_item_type_link_id | BIGINT UNSIGNED PK | |\n| order_item_id | BIGINT UNSIGNED FK | → order_items |\n| item_type_id | INT UNSIGNED FK | → item_types |\n| slot_number | TINYINT UNSIGNED | |\n\n#### payments\nPayment records per order.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| payment_id | BIGINT UNSIGNED PK | |\n| order_id | BIGINT UNSIGNED FK | → orders |\n| payment_date | DATETIME | |\n| payment_type | VARCHAR(100) | |\n| payment_method | VARCHAR(100) | |\n| amount | DECIMAL(10,2) | |\n| notes | TEXT | |\n\n#### tax_payments\nTax payment records.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| tax_payment_id | INT UNSIGNED PK | |\n| legacy_tax_payment_id | INT NULL | |\n| payment_date | DATETIME | |\n| payment_type | VARCHAR(100) | |\n| payment_method | VARCHAR(100) | |\n| amount | DECIMAL(10,2) | |\n| notes | TEXT | |\n| receipt_blob | LONGBLOB | |\n\n#### change_log\nAudit log for changes.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| log_id | BIGINT UNSIGNED PK | |\n| legacy_log_id | INT NULL | |\n| table_name | VARCHAR(100) | |\n| record_id | BIGINT | |\n| visit_id | INT | |\n| field_name | VARCHAR(100) | |\n| old_value | TEXT | |\n| new_value | TEXT | |\n| changed_at | DATETIME | |\n| changed_by | VARCHAR(100) | |\n\n#### ads\nAdvertising/articles.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| ad_id | INT UNSIGNED PK | |\n| legacy_ad_id | INT NULL | |\n| article_text | TEXT | |\n| article_picture | LONGBLOB | |\n| site_submitted | VARCHAR(255) | |\n| views | INT | Default 0 |\n| date_posted | DATETIME | |\n\n#### staging_* tables\nStaging tables for data migration.\n\n| Table Name | Purpose |\n|------------|---------|\n| staging_customer_table_raw | Staging for legacy customer data |\n| staging_customer_visits_raw | Staging for legacy visits |\n| staging_alteration_items_raw | Staging for items |\n\n### Additional Backend Tables\n\n| Table Name | Purpose |\n|------------|---------|\n| setmore_appointments | Synced from Setmore |\n| backend_conversion_job | Document conversion jobs |\n| backend_job_runs | Background job execution log |\n| customer_totals | Daily customer totals |\n| daily_customer_totals | Daily summary totals |\n| customer_payment_transactions | Payment ledger |\n| legacy_customer_totals_monthly | Monthly totals (legacy) |\n| legacy_customer_totals_irs_monthly | IRS monthly reports |\n| backend_appointment_workflow | Appointment state machine |\n| backend_no_show_log | No-show tracking |\n| backend_error_log | Error logging |\n| backend_change_log | Change audit log |\n| backend_document | Document storage |\n| backend_document_version | Document version history |\n| backend_speed_dial | Quick links |\n| backend_square_terminal_code | Square terminal pairing codes |\n| backend_square_terminal_device | Square device registry |\n| cron_job | Scheduled job definitions |\n| cron_job_run | Cron execution history |\n| app_settings | Global configuration |\n| app_users | Login accounts |\n| remember_tokens | \"Remember Me\" tokens |\n| customer_reports | PDF report index |\n\n---\n\n## 4. API Endpoints\n\n### 4.1 Frontend API (port 8895 /api/)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **customer_get.php** | Get customer by ID |\n| **customer_save.php** | Create or update customer |\n| **customer_detail.php** | Customer + visits summary |\n| **customer_delete.php** | Delete customer |\n| **customer_list_hide.php** | Hide customer from list |\n| **search_customers.php** | Search by name/phone |\n| **visit_get.php** | Get visit + items |\n| **visit_save.php** | Create/update visit + items |\n| **visit_delete.php** | Delete visit |\n| **visit_report_generate.php** | Generate visit report |\n| **item_types.php** | List all alteration types |\n| **item_type_add.php** | Add new item type |\n| **item_type_update.php** | Update item type |\n| **item_type_delete.php** | Delete item type |\n| **pickups_today.php** | Orders due today |\n| **ready.php** | Orders ready for pickup |\n| **overdue.php** | Overdue orders |\n| **upcoming.php** | Orders due in next N days |\n| **workload_7days.php** | 7-day workload |\n| **save_signature.php** | Save canvas signature |\n| **reports_list.php** | Reports for customer |\n| **attach_reports.php** | Index all PDFs to DB |\n| **save_signature.php** | Save signature image |\n| **today_appointments_list.php** | Today's appointments |\n| **settings_get.php** | Get app settings |\n| **settings_save.php** | Save app settings |\n| **settings_css.php** | Live theme CSS |\n| **send_email.php** | Send email via SMTP |\n| **send_sms.php** | Send SMS via Twilio |\n| **send_reminders.php** | Send appointment reminders |\n| **ea_appointments.php** | EasyAppointments REST API |\n| **ea_webhook.php** | Receives EA webhook events |\n| **setmore.php** | Setmore API proxy |\n| **setmore_to_ea_import.php** | Import customers to EA |\n| **square_config.php** | Square public config |\n| **square_process.php** | Process Square payment |\n| **square_customer_log.php** | Customer Square log |\n| **square_terminal.php** | Square terminal |\n| **login.php** | Authenticate |\n| **logout.php** | Clear session |\n| **session_check.php** | Check if logged in |\n| **auth.php** | Session authentication |\n| **config.php** | App config constants |\n| **db.php** | Database connection |\n| **log_error.php** | Log client-side error |\n| **todo_list.php** | Todo list management |\n| **send_telegram.php** | Send Telegram messages |\n\n### 4.2 Management API (port 8894 /api/)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **login.php** | Authenticate |\n| **logout.php** | Clear session |\n| **session_check.php** | Check if logged in |\n| **customer_detail.php** | Customer + visits |\n| **search_customers.php** | Search customers |\n| **update_customer.php** | Update customer |\n| **users_list.php** | List all users |\n| **user_save.php** | Create/update user |\n| **list_report_dirs.php** | Date folders |\n| **list_report_files.php** | PDFs in folder |\n| **report_file.php** | Access report file |\n| **pickups_today.php** | Today's pickups |\n| **ready.php** | Ready for pickup |\n| **overdue.php** | Overdue |\n| **settings_get.php** | Get settings |\n| **settings_save.php** | Save settings |\n| **settings_css.php** | Live CSS theme |\n| **imap_list.php** | List IMAP folders |\n| **imap_fetch.php** | Fetch emails |\n| **imap_send.php** | Send email via IMAP |\n| **imap_action.php** | IMAP actions |\n| **sms_send_helper.php** | SMS sending helper |\n| **sms_webhook_receive.php** | Receive SMS webhooks |\n| **sms_inbox_api.php** | SMS inbox API |\n| **slideshow_list.php** | Slideshow images |\n| **slideshow_upload.php** | Upload slideshow |\n| **slideshow_delete.php** | Delete slideshow |\n| **users_list.php** | User management |\n| **report_list.php** | Report list |\n| **debug_filters.php** | Debug filters |\n| **sysadmin.php** | System admin |\n| **test.php** | Testing endpoint |\n\n### 4.3 Backend API (C:\\code\\customerdb\\backend\\api\\)\n\n| Endpoint File | Purpose |\n|---------------|---------|\n| **customer.php** | Get customer details |\n| **customers.php** | List/search customers |\n| **customer_save.php** | Save/update customer |\n| **visit.php** | Get visit details |\n| **visit_delete.php** | Delete visit |\n| **appointments.php** | Unified appointments |\n| **appointments_summary.php** | Appointment statistics |\n| **setmore_appointments.php** | Setmore appointments |\n| **setmore_import.php** | Import from Setmore |\n| **module_*.php** | Module loaders |\n| **alteration_items.php** | Alteration management |\n| **alteration_items_summary.php** | Alteration statistics |\n| **gold_silver_*.php** | Gold/silver prices |\n| **bank_*.php** | Bank transactions |\n| **stocks_summary.php** | Stock data |\n| **medication_*.php** | Medication tracking |\n| **payroll_import.php** | Payroll import |\n| **car_maintenance_*.php** | Vehicle maintenance |\n| **duke_*.php** | Duke integration |\n| **reports_*.php** | Report generation |\n| **totals_import.php** | Customer totals import |\n| **documents.php** | Document management |\n| **document_upload.php** | File uploads |\n| **cust_email_*.php** | Email data |\n| **mail_*.php** | Email sending |\n| **reminders_create.php** | Create reminders |\n| **nightly_reports.php** | Nightly reports |\n| **morning_jobs.php** | Morning jobs |\n| **appointment_workflow.php** | Workflow management |\n| **sysadmin.php** | System admin |\n| **reporting_module.php** | Reporting |\n| **job_runs.php** | Job tracking |\n| **health.php** | Health check |\n| **overview.php** | Dashboard data |\n\n---\n\n## 5. External Integrations\n\n### 5.1 Square (Payment Processing)\n- **Purpose:** Point-of-sale payments, receipts, terminal management\n- **Features:**\n - Square Terminal device pairing/management\n - Payment processing with tips\n - Customer payment logging\n - Sandbox and live environments\n- **Tables:** `backend_square_terminal_code`, `backend_square_terminal_device`, `square_customer_log`\n\n### 5.2 Twilio (SMS)\n- **Purpose:** SMS notifications and reminders\n- **Features:**\n - Send SMS to customers\n - Appointment reminders\n - SMS consent tracking\n- **Settings:** `twilio_sid`, `twilio_token`, `twilio_from`\n\n### 5.3 Setmore (Appointment Booking)\n- **Purpose:** Appointment synchronization\n- **Features:**\n - Pull appointments from Setmore API\n - Link to customers/visits\n- **Table:** `setmore_appointments`\n- **Settings:** `setmore_refresh_token`, `setmore_staff_key`\n\n### 5.4 EasyAppointments (Alternative Booking)\n- **Purpose:** Full appointment booking platform\n- **Location:** C:\\code\\customerdb\\schedule\\\n- **Features:**\n - Customer/provider management\n - Web-based booking widget\n - REST API\n\n### 5.5 Telegram (Notifications)\n- **Purpose:** Bot-based notifications\n- **Features:**\n - Send notifications to channels\n - Visit alerts\n - Cron job status\n- **Settings:** `telegram_bot_token`, `telegram_chat_id`\n\n### 5.6 Ollama (AI - Optional)\n- **Purpose:** Local AI for email generation\n- **Settings:** `ollama_url`, `ollama_default_model`\n\n---\n\n## 6. Scheduled Jobs / Cron\n\n### Configured Cron Jobs\n\n| Job Label | Schedule | Purpose |\n|----------|----------|---------|\n| **Next-Day Customer Emails** | `0 20 * * *` (8 PM) | Pickup reminder emails |\n| **Morning Import** | `0 6 * * *` (6 AM) | Morning data import |\n| **Morning Jobs** | `0 9 * * *` (9 AM) | Appointment sync, workflow |\n| **Nightly Reports** | `0 20 * * *` (8 PM) | Generate nightly summaries |\n| **Database Backup** | `0 2 * * *` (2 AM) | Full MariaDB backup |\n| **Daily Cleanup** | `0 4 * * *` (4 AM) | Temp files, old logs |\n| **Auto Delete** | `15 4 * * *` (4:15 AM) | Clean placeholder records |\n\n### Background Job Scripts (backend/jobs/)\n\n| Script | Purpose |\n|--------|---------|\n| **run_morning_jobs.php** | Main morning job runner |\n| **smoke_morning_jobs.php** | Smoke test |\n| **send_nightly_reports.php** | Nightly report emailer |\n| **send_nextday_customer_emails.php** | Next-day reminders |\n| **delete_add_name_records.php** | Cleanup job |\n| **rebuild_customer_totals.php** | Rebuild totals |\n| **rebuild_daily_customer_totals.php** | Rebuild daily totals |\n| **rebuild_payment_ledger.php** | Rebuild payment ledger |\n| **import_customer_totals_csv.php** | CSV import |\n| **backfill_square_customer_log.php** | Square log backfill |\n| **run_cron_job.php** | Generic cron runner |\n\n---\n\n## 7. Web Applications\n\n### 7.1 Front Desk App (port 8895)\n\n| Page | Purpose |\n|------|---------|\n| **index.html** | Main dashboard, search, stats |\n| **customer.html** | Customer detail + visit history |\n| **visit.html** | Create/edit alteration order |\n| **customer_display.html** | Second monitor display |\n| **receipt.html** | Printable receipt |\n| **reports.html** | Order list by status |\n| **upcoming.html** | Next 7 days pickups |\n| **schedule.html** | Appointments calendar |\n| **login.html** | Login page |\n| **item_types.html** | Item type management |\n\n### 7.2 Management App (port 8894)\n\n| Page | Purpose |\n|------|---------|\n| **index.html** | Login page |\n| **webui.html** | Dashboard |\n| **customer.html** | Customer detail view |\n| **admin.html** | Reports browser |\n| **settings.html** | App settings editor |\n| **users.html** | User management |\n| **reports.html** | Order reports |\n\n---\n\n## 8. Settings & Configuration\n\n### app_settings Keys\n\n| Key | Purpose |\n|-----|---------|\n| theme_bg | Page background color |\n| theme_panel | Card/panel color |\n| theme_text | Text color |\n| theme_accent | Accent/button color |\n| theme_accent_deep | Darker accent |\n| theme_teal | Teal color |\n| theme_font | Font family |\n| smtp_host | Email server hostname |\n| smtp_port | Email server port |\n| smtp_secure | tls or ssl |\n| smtp_user | Email login |\n| smtp_pass | Email password |\n| smtp_from_email | From address |\n| smtp_from_name | From display name |\n| twilio_sid | Twilio Account SID |\n| twilio_token | Twilio Auth Token |\n| twilio_from | Twilio phone number |\n| imap_host | Incoming email server |\n| imap_port | IMAP port |\n| imap_ssl | SSL enable |\n| imap_user | IMAP login |\n| imap_pass | IMAP password |\n| square_token | Square API token |\n| square_location | Square location ID |\n| setmore_staff_key | Setmore API key |\n| setmore_refresh | Setmore refresh token |\n| require_report | PDF required |\n| require_signature | Signature required |\n| reports_base_path | Reports disk path |\n| reports_base_url | Reports URL |\n| ollama_url | Ollama AI URL |\n| ollama_default_model | Ollama model |\n| telegram_bot_token | Telegram bot token |\n| telegram_chat_id | Telegram chat ID |\n\n---\n\n## 9. Migration Notes\n\n### Access to MariaDB Workflow\n\n1. **Export source data from Access**\n - Export ordinary tables to CSV\n - Export attachment fields with VBA or DAO Recordset2 logic\n - Keep original IDs during migration\n\n2. **Load into MariaDB staging**\n - Use `LOAD DATA LOCAL INFILE` for CSV files\n - Load signatures separately into `visit_signatures`\n\n3. **Normalize**\n - `CustomerTable` → `customers`\n - `CustomerVisits` → `visits`\n - `AlterationItems` → `orders`, `order_items`, `order_item_type_links`\n - `2024TaxPayments` → `tax_payments`\n - `AlterationItemsChangeLog` → `change_log`\n\n4. **Signature BLOB path**\n - Export Access attachment to temp file\n - Convert BMP to PNG\n - Insert PNG bytes into `visit_signatures.signature_image`\n\n5. **Validation**\n - Compare source row counts vs target\n - Compare distinct legacy IDs\n - Compare sample signatures by SHA256\n\n---\n\n## 10. Scripts & Tools\n\n### PowerShell Scripts (scripts/)\n\n| Script | Purpose |\n|--------|---------|\n| **import_legacy_totals_from_excel.ps1** | Import totals from Excel |\n| **sync_backend_mirror.ps1** | Sync backend mirror |\n| **check_backend_mirror.ps1** | Check mirror status |\n\n### Python Scripts (scripts/)\n\n| Script | Purpose |\n|--------|---------|\n| **signature_blob_loader.py** | Load signature blobs |\n| **access_to_mariadb_parser.py** | Parse Access to MariaDB |\n| **import_access_v2.py** | Import Access v2 |\n| **import_csv_v2.py** | Import CSV v2 |\n\n### SQL Migrations (migrations/)\n\n| File | Purpose |\n|------|---------|\n| **20260407_add_conversion_job.sql** | Add conversion job table |\n| **20260411_order_items_item_type_name_no_fk.sql** | Order items no FK |\n| **20260416_blog_prompt_templates.sql** | Blog prompt templates |\n\n---\n\n## File Statistics\n\n- **Total PHP files:** 200+\n- **Total SQL files:** 100+\n- **Total HTML pages:** 20+\n- **Database tables:** 30+\n\n---\n\n*End of Documentation*","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"19","document_type":"upload","title":"Change Hours","slug":"change-hours","summary_text":"Change Business Hours","content_markdown":"# **Places to change hours**\r\n*** https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\n* Website\r\n* facebook\r\n* twitter\r\n* nextdoor\r\n* apple\r\n* yelp**","content_html":"<h1>**Places to change hours**</h1>\n<p><em>*</em> https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info</p>\n<ul>\n<li>Website</li>\n<li>facebook</li>\n<li>twitter</li>\n<li>nextdoor</li>\n<li>apple</li>\n<li>yelp**</li>\n</ul>","file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-17 12:02:14","editor_content":"# **Places to change hours**\r\n*** https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\n* Website\r\n* facebook\r\n* twitter\r\n* nextdoor\r\n* apple\r\n* yelp**","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"19","document_type":"upload","title":"Birthdays","slug":"birthdays","summary_text":"Birthday Dates","content_markdown":"Birthdates:\r\nMe - July 5, 1979\r\nChristy - December 30\r\nKhey - February 22\r\nGian - April 26\r\nJimbo - October 7\r\nLola Julie- October 28\r\nAte pia – September 9\r\nSherill – July 30\r\n\r\nAarons birthday 03/07/\r\ndads birthday 05\r\ndarrens birthday05\r\nmoms birthday 01/21","content_html":"<p>Birthdates:</p>\n<p>Me - July 5, 1979</p>\n<p>Christy - December 30</p>\n<p>Khey - February 22</p>\n<p>Gian - April 26</p>\n<p>Jimbo - October 7</p>\n<p>Lola Julie- October 28</p>\n<p>Ate pia – September 9</p>\n<p>Sherill – July 30</p>\n<p>Aarons birthday 03/07/</p>\n<p>dads birthday 05</p>\n<p>darrens birthday05</p>\n<p>moms birthday 01/21</p>","file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-17 12:02:57","editor_content":"Birthdates:\r\nMe - July 5, 1979\r\nChristy - December 30\r\nKhey - February 22\r\nGian - April 26\r\nJimbo - October 7\r\nLola Julie- October 28\r\nAte pia – September 9\r\nSherill – July 30\r\n\r\nAarons birthday 03/07/\r\ndads birthday 05\r\ndarrens birthday05\r\nmoms birthday 01/21","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"19","document_type":"upload","title":"Change Hours","slug":"change-hours","summary_text":"Change Business Hours","content_markdown":"https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\nWebsite\r\nfacebook\r\ntwitter\r\nnextdoor\r\napple\r\nyelp","content_html":"<p>https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info</p>\n<p>Website</p>\n<p>facebook</p>\n<p>twitter</p>\n<p>nextdoor</p>\n<p>apple</p>\n<p>yelp</p>","file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-17 12:01:43","editor_content":"https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\nWebsite\r\nfacebook\r\ntwitter\r\nnextdoor\r\napple\r\nyelp","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"19","document_type":"upload","title":"Change Hours","slug":"change-hours","summary_text":"Change Business Hours","content_markdown":"# **Places to change hours**\r\n*** https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\n* Website\r\n* facebook\r\n* twitter\r\n* nextdoor\r\n* apple\r\n* yelp**","content_html":"<h1>**Places to change hours**</h1>\n<p><em>*</em> https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info</p>\n<ul>\n<li>Website</li>\n<li>facebook</li>\n<li>twitter</li>\n<li>nextdoor</li>\n<li>apple</li>\n<li>yelp**</li>\n</ul>","file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-17 12:02:14","editor_content":"# **Places to change hours**\r\n*** https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\n* Website\r\n* facebook\r\n* twitter\r\n* nextdoor\r\n* apple\r\n* yelp**","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"19","document_type":"upload","title":"Claude and Curser Reporting Changes 04-16","slug":"claude-and-curser-reporting-changes-04-16","summary_text":"Claude and Curser Reporting Changes 04-16","content_markdown":null,"content_html":null,"file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-16 14:21:20","editor_content":"# Reporting Module Updates Log\n\nDate: 2026-04-15 \nOwner: Codex (implementation) \nScope: Backend reporting module (`backend/reporting_module.php`, `backend/api/reporting_module.php`, `backend/reporting_module_service.php`)\n\n## Purpose\n\nThis document tracks:\n\n- Updates completed\n- Areas implemented vs planned\n- Data sources used by each report\n- Corrections and refactors made\n- API/UI calls and usage patterns\n- Completion timeline\n\n## Completion Timeline\n\n### 2026-04-15 (Initial build)\n\n- Added backend report engine:\n - `backend/reporting_module_service.php`\n- Added API endpoint:\n - `backend/api/reporting_module.php`\n- Added backend themed page:\n - `backend/reporting_module.php`\n- Linked module into existing reporting surfaces:\n - `backend/module_service.php`\n - `backend/reports.php`\n\n### 2026-04-15 (Coverage expansion)\n\n- Expanded report families for:\n - Sales\n - Cost / P&L / Balance Sheet / Cash Flow\n - Customer service and quality\n - Labor/time and trend analysis\n - Compliance and insurance\n - Marketing/sales\n- Added planned placeholders for unsupported schema areas:\n - Inventory\n - Employee performance\n - Goals vs actuals\n - Referral source tracking\n - Missed fitting and seamstress productivity\n\n### 2026-04-15 (Legend + documentation controls)\n\n- Added report metadata registry:\n - status\n - completed date\n - data sources\n - report logic notes\n- Added in-app \"Data Source Legend\" panel in:\n - `backend/reporting_module.php`\n- Added catalog column for completion date in UI.\n\n### 2026-04-15 (Settings integration for backend theming)\n\n- Aligned frontend settings APIs with backend/webui theme controls so backend modules can use centralized style settings:\n - `frontend/api/settings_save.php`\n - `frontend/api/settings_get.php`\n - `frontend/api/settings_css.php`\n- Added missing persistence + exposure for:\n - backend-specific theme keys (`backend_theme_*`)\n - advanced typography and sizing keys (`theme_heading_font`, `theme_border_width`, `theme_radius_*`, `theme_panel_padding`, `theme_button_padding_*`)\n - today-pill color keys and extended button label/color keys\n- Result: settings entered in Settings module now propagate to backend pages that consume `/api/settings_css.php` and CSS variables from `page_helpers.php`.\n\n## Report Areas: Status and Data Uses\n\n| Report Key | Status | Completed On | Data Sources | Notes |\n|---|---|---|---|---|\n| `sales_reports` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Sales trend rollup |\n| `sales_revenue_by_category` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Revenue by service/category |\n| `sales_trends` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Day/month/quarter/year buckets |\n| `top_selling_services` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Quantity and gross sales |\n| `sales_by_customer` | Implemented | 2026-04-15 | `customers`, `orders`, `visits`, `payments` | Customer revenue ranking |\n| `sales_goals_vs_actuals` | Planned | - | Requires goals table/config | Pending schema |\n| `cost_reports` | Implemented | 2026-04-15 | `bills_entry`, `bills_item` | Materials/labor/overhead split |\n| `inventory_reports` | Planned | - | Requires inventory schema | Pending schema |\n| `profit_and_loss_statement` | Implemented | 2026-04-15 | `payments`, `bills_entry`, `bills_item` | Revenue minus expenses |\n| `balance_sheet` | Implemented | 2026-04-15 | `bills_balance`, `orders`, `order_items`, `payments`, `bills_entry` | Assets/liabilities/equity snapshot |\n| `cash_flow_statement` | Implemented | 2026-04-15 | `payments`, `bills_entry`, `tax_payments` | Inflows/outflows/net |\n| `customer_service_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Overdue/aging/volume indicators |\n| `time_and_labor_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Billed labor trend proxy |\n| `trends_and_analysis_reports` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Monthly KPI trend |\n| `compliance_reports` | Implemented | 2026-04-15 | `payments`, `tax_payments` | Sales/tax snapshot |\n| `insurance_reports` | Implemented | 2026-04-15 | `bills_entry`, `bills_item` | Insurance bill tracking |\n| `quality_control_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Open/overdue quality indicators |\n| `marketing_and_sales_reports` | Implemented | 2026-04-15 | `customers`, `orders`, `visits`, `payments` | Top customer activity |\n| `employee_performance_reports` | Planned | - | Requires worker attribution | Pending schema |\n| `workroom_queue` | Implemented | 2026-04-15 | `visits`, `customers`, `orders` | Queue + rush/stage |\n| `missed_fitting_alert` | Planned | - | Requires fitting milestones | Pending schema |\n| `ready_for_pickup_aging` | Implemented | 2026-04-15 | `visits`, `customers`, `orders`, `order_items` | 14+ day pickup aging |\n| `revenue_by_category` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Alias report |\n| `average_order_value` | Implemented | 2026-04-15 | `orders`, `visits`, `payments` | AOV |\n| `outstanding_balances` | Implemented | 2026-04-15 | `orders`, `customers`, `visits`, `order_items`, `payments` | WIP balances |\n| `customer_lifetime_value` | Implemented | 2026-04-15 | `customers`, `orders`, `payments` | Top 20% CLV |\n| `referral_source_tracking` | Planned | - | Requires referral source | Pending schema |\n| `dormant_clients` | Implemented | 2026-04-15 | `customers`, `visits`, `orders`, `payments` | 12+ month inactive clients |\n| `seamstress_productivity` | Planned | - | Requires worker time logs | Pending schema |\n| `garment_type_frequency` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Garment/service frequency |\n| `projected_completion_vs_deadline` | Implemented | 2026-04-15 | `visits`, `customers`, `orders` | Deadline risk status |\n\n## Corrections and Refactors Applied\n\n- Standardized report availability through metadata-based support checks:\n - `app_reporting_module_is_supported()`\n- Introduced single report metadata registry:\n - `app_reporting_module_definitions()`\n- Added metadata accessor:\n - `app_reporting_module_metadata()`\n- Updated UI catalog to include completion dates.\n- Added in-page data-source legend to reduce ambiguity during audits and reconciliation reviews.\n\n## Calls and Usage\n\n### Backend UI Calls\n\n- Open reporting module page:\n - `/backend/reporting_module.php`\n- Run report in browser:\n - `/backend/reporting_module.php?report=sales_trends&start_date=2026-01-01&end_date=2026-04-15&group_by=month&limit=25`\n\n### API Calls\n\n- Catalog:\n - `/backend/api/reporting_module.php?report=catalog`\n- Sales by customer:\n - `/backend/api/reporting_module.php?report=sales_by_customer&start_date=2026-01-01&end_date=2026-04-15&limit=100`\n- P&L:\n - `/backend/api/reporting_module.php?report=profit_and_loss_statement&start_date=2026-01-01&end_date=2026-04-15`\n- Balance sheet:\n - `/backend/api/reporting_module.php?report=balance_sheet&end_date=2026-04-15`\n\n### Internal Function Calls (Core)\n\n- Catalog and metadata:\n - `app_reporting_module_catalog()`\n - `app_reporting_module_metadata()`\n- Dispatcher:\n - `app_reporting_module_run()`\n- Service/report functions:\n - `app_reporting_module_sales_trends()`\n - `app_reporting_module_profit_and_loss()`\n - `app_reporting_module_balance_sheet()`\n - `app_reporting_module_cash_flow()`\n - `app_reporting_module_cost_reports()`\n - plus additional report-specific handlers\n\n## Pending Gaps / Next Corrections\n\n1. Add dedicated inventory schema (stock ledger, reorder points, usage) and implement `inventory_reports`.\n2. Add referral source persistence (`customers.referral_source` or linked table) and implement `referral_source_tracking`.\n3. Add employee/time attribution tables and implement:\n - `employee_performance_reports`\n - `seamstress_productivity`\n4. Add sales goals table to enable `sales_goals_vs_actuals`.\n5. Add fitting milestone fields/events to enable `missed_fitting_alert`.\n\n## Change Control Notes\n\n- All new report logic was added non-destructively.\n- Existing reporting endpoints were not removed.\n- The module keeps planned reports visible for roadmap tracking.\n","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"19","document_type":"upload","title":"Change Hours","slug":"change-hours","summary_text":"Change Business Hours","content_markdown":"https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\nWebsite\r\nfacebook\r\ntwitter\r\nnextdoor\r\napple\r\nyelp","content_html":"<p>https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info</p>\n<p>Website</p>\n<p>facebook</p>\n<p>twitter</p>\n<p>nextdoor</p>\n<p>apple</p>\n<p>yelp</p>","file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-17 12:01:43","editor_content":"https://business.apple.com/companies/1508340657688417792/maps/locations/1554057468158415192/info\r\nWebsite\r\nfacebook\r\ntwitter\r\nnextdoor\r\napple\r\nyelp","is_text_editable":1,"can_edit_inline":1}
{"file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"19","document_type":"upload","title":"Claude and Curser Reporting Changes 04-16","slug":"claude-and-curser-reporting-changes-04-16","summary_text":"Claude and Curser Reporting Changes 04-16","content_markdown":null,"content_html":null,"file_name":"REPORTING_MODULE_UPDATES_2026-04-15.md","stored_name":"20260416-182120-cf1442b8.md","mime_type":"application/octet-stream","file_size_bytes":"8228","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182120-cf1442b8.md","is_deleted":"0","created_at":"2026-04-16 14:21:20","updated_at":"2026-04-16 14:21:20","editor_content":"# Reporting Module Updates Log\n\nDate: 2026-04-15 \nOwner: Codex (implementation) \nScope: Backend reporting module (`backend/reporting_module.php`, `backend/api/reporting_module.php`, `backend/reporting_module_service.php`)\n\n## Purpose\n\nThis document tracks:\n\n- Updates completed\n- Areas implemented vs planned\n- Data sources used by each report\n- Corrections and refactors made\n- API/UI calls and usage patterns\n- Completion timeline\n\n## Completion Timeline\n\n### 2026-04-15 (Initial build)\n\n- Added backend report engine:\n - `backend/reporting_module_service.php`\n- Added API endpoint:\n - `backend/api/reporting_module.php`\n- Added backend themed page:\n - `backend/reporting_module.php`\n- Linked module into existing reporting surfaces:\n - `backend/module_service.php`\n - `backend/reports.php`\n\n### 2026-04-15 (Coverage expansion)\n\n- Expanded report families for:\n - Sales\n - Cost / P&L / Balance Sheet / Cash Flow\n - Customer service and quality\n - Labor/time and trend analysis\n - Compliance and insurance\n - Marketing/sales\n- Added planned placeholders for unsupported schema areas:\n - Inventory\n - Employee performance\n - Goals vs actuals\n - Referral source tracking\n - Missed fitting and seamstress productivity\n\n### 2026-04-15 (Legend + documentation controls)\n\n- Added report metadata registry:\n - status\n - completed date\n - data sources\n - report logic notes\n- Added in-app \"Data Source Legend\" panel in:\n - `backend/reporting_module.php`\n- Added catalog column for completion date in UI.\n\n### 2026-04-15 (Settings integration for backend theming)\n\n- Aligned frontend settings APIs with backend/webui theme controls so backend modules can use centralized style settings:\n - `frontend/api/settings_save.php`\n - `frontend/api/settings_get.php`\n - `frontend/api/settings_css.php`\n- Added missing persistence + exposure for:\n - backend-specific theme keys (`backend_theme_*`)\n - advanced typography and sizing keys (`theme_heading_font`, `theme_border_width`, `theme_radius_*`, `theme_panel_padding`, `theme_button_padding_*`)\n - today-pill color keys and extended button label/color keys\n- Result: settings entered in Settings module now propagate to backend pages that consume `/api/settings_css.php` and CSS variables from `page_helpers.php`.\n\n## Report Areas: Status and Data Uses\n\n| Report Key | Status | Completed On | Data Sources | Notes |\n|---|---|---|---|---|\n| `sales_reports` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Sales trend rollup |\n| `sales_revenue_by_category` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Revenue by service/category |\n| `sales_trends` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Day/month/quarter/year buckets |\n| `top_selling_services` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Quantity and gross sales |\n| `sales_by_customer` | Implemented | 2026-04-15 | `customers`, `orders`, `visits`, `payments` | Customer revenue ranking |\n| `sales_goals_vs_actuals` | Planned | - | Requires goals table/config | Pending schema |\n| `cost_reports` | Implemented | 2026-04-15 | `bills_entry`, `bills_item` | Materials/labor/overhead split |\n| `inventory_reports` | Planned | - | Requires inventory schema | Pending schema |\n| `profit_and_loss_statement` | Implemented | 2026-04-15 | `payments`, `bills_entry`, `bills_item` | Revenue minus expenses |\n| `balance_sheet` | Implemented | 2026-04-15 | `bills_balance`, `orders`, `order_items`, `payments`, `bills_entry` | Assets/liabilities/equity snapshot |\n| `cash_flow_statement` | Implemented | 2026-04-15 | `payments`, `bills_entry`, `tax_payments` | Inflows/outflows/net |\n| `customer_service_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Overdue/aging/volume indicators |\n| `time_and_labor_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Billed labor trend proxy |\n| `trends_and_analysis_reports` | Implemented | 2026-04-15 | `payments`, `orders`, `visits` | Monthly KPI trend |\n| `compliance_reports` | Implemented | 2026-04-15 | `payments`, `tax_payments` | Sales/tax snapshot |\n| `insurance_reports` | Implemented | 2026-04-15 | `bills_entry`, `bills_item` | Insurance bill tracking |\n| `quality_control_reports` | Implemented | 2026-04-15 | `visits`, `orders` | Open/overdue quality indicators |\n| `marketing_and_sales_reports` | Implemented | 2026-04-15 | `customers`, `orders`, `visits`, `payments` | Top customer activity |\n| `employee_performance_reports` | Planned | - | Requires worker attribution | Pending schema |\n| `workroom_queue` | Implemented | 2026-04-15 | `visits`, `customers`, `orders` | Queue + rush/stage |\n| `missed_fitting_alert` | Planned | - | Requires fitting milestones | Pending schema |\n| `ready_for_pickup_aging` | Implemented | 2026-04-15 | `visits`, `customers`, `orders`, `order_items` | 14+ day pickup aging |\n| `revenue_by_category` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Alias report |\n| `average_order_value` | Implemented | 2026-04-15 | `orders`, `visits`, `payments` | AOV |\n| `outstanding_balances` | Implemented | 2026-04-15 | `orders`, `customers`, `visits`, `order_items`, `payments` | WIP balances |\n| `customer_lifetime_value` | Implemented | 2026-04-15 | `customers`, `orders`, `payments` | Top 20% CLV |\n| `referral_source_tracking` | Planned | - | Requires referral source | Pending schema |\n| `dormant_clients` | Implemented | 2026-04-15 | `customers`, `visits`, `orders`, `payments` | 12+ month inactive clients |\n| `seamstress_productivity` | Planned | - | Requires worker time logs | Pending schema |\n| `garment_type_frequency` | Implemented | 2026-04-15 | `order_items`, `item_types`, `orders`, `visits` | Garment/service frequency |\n| `projected_completion_vs_deadline` | Implemented | 2026-04-15 | `visits`, `customers`, `orders` | Deadline risk status |\n\n## Corrections and Refactors Applied\n\n- Standardized report availability through metadata-based support checks:\n - `app_reporting_module_is_supported()`\n- Introduced single report metadata registry:\n - `app_reporting_module_definitions()`\n- Added metadata accessor:\n - `app_reporting_module_metadata()`\n- Updated UI catalog to include completion dates.\n- Added in-page data-source legend to reduce ambiguity during audits and reconciliation reviews.\n\n## Calls and Usage\n\n### Backend UI Calls\n\n- Open reporting module page:\n - `/backend/reporting_module.php`\n- Run report in browser:\n - `/backend/reporting_module.php?report=sales_trends&start_date=2026-01-01&end_date=2026-04-15&group_by=month&limit=25`\n\n### API Calls\n\n- Catalog:\n - `/backend/api/reporting_module.php?report=catalog`\n- Sales by customer:\n - `/backend/api/reporting_module.php?report=sales_by_customer&start_date=2026-01-01&end_date=2026-04-15&limit=100`\n- P&L:\n - `/backend/api/reporting_module.php?report=profit_and_loss_statement&start_date=2026-01-01&end_date=2026-04-15`\n- Balance sheet:\n - `/backend/api/reporting_module.php?report=balance_sheet&end_date=2026-04-15`\n\n### Internal Function Calls (Core)\n\n- Catalog and metadata:\n - `app_reporting_module_catalog()`\n - `app_reporting_module_metadata()`\n- Dispatcher:\n - `app_reporting_module_run()`\n- Service/report functions:\n - `app_reporting_module_sales_trends()`\n - `app_reporting_module_profit_and_loss()`\n - `app_reporting_module_balance_sheet()`\n - `app_reporting_module_cash_flow()`\n - `app_reporting_module_cost_reports()`\n - plus additional report-specific handlers\n\n## Pending Gaps / Next Corrections\n\n1. Add dedicated inventory schema (stock ledger, reorder points, usage) and implement `inventory_reports`.\n2. Add referral source persistence (`customers.referral_source` or linked table) and implement `referral_source_tracking`.\n3. Add employee/time attribution tables and implement:\n - `employee_performance_reports`\n - `seamstress_productivity`\n4. Add sales goals table to enable `sales_goals_vs_actuals`.\n5. Add fitting milestone fields/events to enable `missed_fitting_alert`.\n\n## Change Control Notes\n\n- All new report logic was added non-destructively.\n- Existing reporting endpoints were not removed.\n- The module keeps planned reports visible for roadmap tracking.\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"PAY_BILLS_ENHANCED_FEATURES.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"18","document_type":"upload","title":"Open Code 04-16 Pay Bills changes","slug":"open-code-04-16-pay-bills-changes","summary_text":"Open Code 04-16 Pay Bills changes","content_markdown":null,"content_html":null,"file_name":"PAY_BILLS_ENHANCED_FEATURES.md","stored_name":"20260416-182019-18adea90.md","mime_type":"application/octet-stream","file_size_bytes":"8500","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260416-182019-18adea90.md","is_deleted":"0","created_at":"2026-04-16 14:20:19","updated_at":"2026-04-16 14:20:19","editor_content":"# Pay Bills - Enhanced Features Documentation\n\n**Last Updated:** April 16, 2026 \n**File:** `/backend/bills.php` \n**Server:** `kefa@192.168.7.202` \n**Data Location:** `/mnt/drive1/customerdb/`\n\n---\n\n## Overview\n\nThe Pay Bills page has been enhanced with new features for better bank account management and payment tracking visibility.\n\n---\n\n## New Features\n\n### 1. Bank Account Types\n\nEach bank account now has a **type classification**:\n\n| Type | Icon | Description |\n|------|------|-------------|\n| **Pay Bill** | 💳 | Used for paying bills - appears in bank dropdown |\n| **Show Balance** | 👁 | Track only - excluded from bill payment dropdowns |\n\n**Why this matters:** Accounts marked as \"Show Balance\" won't clutter the bill payment dropdown but still track their running balance.\n\n### 2. Bank URL (Popup Window)\n\nEach account can have a **Bank URL** that opens in a popup window.\n\n- Click the \"🌐 Bank\" button next to any account\n- Opens the URL in a 1200x800 popup window\n- Useful for quick access to bank login without leaving the bills page\n\n### 3. Bal After Pills (Status Badges)\n\nThe \"Bal After\" column now shows **color-coded pills**:\n\n| Status | Appearance | Meaning |\n|--------|------------|---------|\n| **Paid** | Green pill with ✓ | Bill amount has been paid |\n| **Due** | Red pill with ! | Still needs to be paid |\n| **—** | Gray pill | No amount entered |\n\n### 4. Save All Buttons\n\nTwo save buttons added for convenience:\n\n1. **Save All Balances** - In the bank accounts section\n2. **Save All Bills** - At the bottom of the bills table\n\nBoth show \"Saving…\" → \"Saved ✓\" feedback.\n\n### 5. Auto-Update\n\nAll changes auto-save after 600ms of inactivity (debounced).\n\n---\n\n## Database Schema Changes\n\n### New Columns in `bills_accounts` Table\n\n```sql\n-- MySQL / MariaDB\nALTER TABLE bills_accounts\nADD COLUMN account_type ENUM('pay_bill', 'show_balance') DEFAULT 'pay_bill' AFTER name,\nADD COLUMN bank_url TEXT DEFAULT '' AFTER account_type;\n\n-- SQLite\nALTER TABLE bills_accounts ADD COLUMN account_type TEXT DEFAULT 'pay_bill';\nALTER TABLE bills_accounts ADD COLUMN bank_url TEXT DEFAULT '';\n```\n\n### New Tables Created\n\nIf tables don't exist, the following are auto-created:\n\n```sql\nCREATE TABLE bills_accounts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n account_type TEXT DEFAULT 'pay_bill',\n bank_url TEXT DEFAULT '',\n sort_order INTEGER DEFAULT 0,\n created_at TEXT DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE bills_monthly (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n year INTEGER NOT NULL,\n month INTEGER NOT NULL,\n bills_account_id INTEGER,\n opening_balance REAL DEFAULT 0,\n UNIQUE(year, month, bills_account_id)\n);\n\nCREATE TABLE bills_items (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n default_due REAL DEFAULT 0,\n notes TEXT DEFAULT '',\n sort_order INTEGER DEFAULT 0,\n created_at TEXT DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE bills_entries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n year INTEGER NOT NULL,\n month INTEGER NOT NULL,\n bills_item_id INTEGER NOT NULL,\n bills_account_id INTEGER DEFAULT 0,\n payment_due REAL DEFAULT 0,\n amount_paid REAL DEFAULT 0,\n is_paid INTEGER DEFAULT 0,\n paid_date TEXT DEFAULT '',\n UNIQUE(year, month, bills_item_id)\n);\n\nCREATE TABLE bills_copy_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n year INTEGER NOT NULL,\n month INTEGER NOT NULL,\n copied_from_year INTEGER NOT NULL,\n copied_from_month INTEGER NOT NULL,\n copied_at TEXT DEFAULT CURRENT_TIMESTAMP\n);\n```\n\n---\n\n## Deployment Instructions\n\n### Step 1: Upload the File\n\n**Option A: Direct Copy (if file is on local machine)**\n\n```bash\n# From local machine to server\nscp bills.php kefa@192.168.7.202:/var/www/floridaalterations.com/backend/bills.php\n```\n\n**Option B: If editing on server directly**\n\n```bash\nssh kefa@192.168.7.202\n# Then edit the file at:\n# /var/www/floridaalterations.com/backend/bills.php\n```\n\n### Step 2: Set Permissions\n\n```bash\nssh kefa@192.168.202\nchmod 644 /var/www/floridaalterations.com/backend/bills.php\nchown www-data:www-data /var/www/floridaalterations.com/backend/bills.php\n```\n\n### Step 3: Clear Cache (Optional)\n\n```bash\n# If using OPcache\nssh kefa@192.168.7.202\nphp -r \"opcache_get_status();\" 2>/dev/null && echo \"Consider: php-fpm restart\"\n```\n\n### Step 4: Access the Page\n\n```\nhttps://ella.floridaalterations.com/backend/bills.php\n```\n\n---\n\n## How to Use\n\n### Adding a New Account with Bank URL\n\n1. Click **\"+ Add Account\"** button\n2. Enter account name (e.g., \"Chase Checking\")\n3. Select account type:\n - **Pay Bill** - For accounts you'll pay bills from\n - **Show Balance** - For tracking only\n4. Enter Bank URL (e.g., `https://www.chase.com/login`)\n5. Click **\"Add Account\"**\n6. Enter the opening balance and click **\"Save Balances\"**\n\n### Editing an Existing Account\n\n1. Click **\"Edit\"** next to the account\n2. Update name, type, or URL\n3. Click **\"Save Changes\"**\n\n### Switching Account Type\n\n1. Edit the account\n2. Change the radio button selection\n3. Click **\"Save Changes\"**\n\n### Opening Bank in Popup\n\n1. Click the **\"🌐 Bank\"** button next to an account\n2. The bank URL opens in a new popup window\n\n### Marking a Bill as Paid\n\n1. Enter the **Paid ($)** amount (or it auto-calculates)\n2. The **Bal After** pill turns **green** with a ✓ checkmark\n3. Row background turns light blue to indicate paid status\n\n### Viewing Running Balance\n\n- The sidebar shows **Running Totals** for each pay-bill account\n- Updates automatically as you enter payments\n\n---\n\n## File Location Reference\n\n| Item | Server Path |\n|------|-------------|\n| bills.php | `/var/www/floridaalterations.com/backend/bills.php` |\n| Data directory | `/mnt/drive1/customerdb/` |\n| Database | `/mnt/drive1/customerdb/customer.db` |\n| Backup | `/mnt/drive1/customerdb/backup/` |\n| Logs | `/mnt/drive1/customerdb/logs/` |\n\n---\n\n## Troubleshooting\n\n### Database Connection Error\n\n```\nError: Connection failed\n```\n\n**Solution:**\n```bash\n# Check if MySQL is running\nssh kefa@192.168.7.202\nsudo systemctl status mysql\n# or\nsudo systemctl status mariadb\n```\n\n### Table Doesn't Exist\n\nIf you see errors about missing tables:\n\n```bash\n# The tables are auto-created on first page load\n# Just refresh the page once\n```\n\n### Bank URL Not Opening\n\n- Verify the URL includes `https://` or `http://`\n- Check browser popup blocker settings\n- Some banking sites block popup windows\n\n### Bal After Shows \"—\"\n\n**Causes:**\n1. No bank account selected for the bill\n2. Opening balance not saved for the account\n3. Account is marked as \"Show Balance\" type\n\n**Solution:**\n1. Select a bank account from the dropdown\n2. Save the opening balance for that account\n\n### Changes Not Saving\n\n1. Check browser console for JavaScript errors\n2. Verify network tab shows POST requests completing\n3. Clear browser cache and retry\n\n---\n\n## Quick Commands Reference\n\n```bash\n# Connect to server\nssh kefa@192.168.7.202\n\n# View current bills.php\ncat /var/www/floridaalterations.com/backend/bills.php | head -50\n\n# Backup bills.php before updating\ncp /var/www/floridaalterations.com/backend/bills.php /mnt/drive1/customerdb/backup/bills.php.$(date +%Y%m%d)\n\n# Upload new file\nscp bills.php kefa@192.168.7.202:/var/www/floridaalterations.com/backend/bills.php\n\n# Set correct permissions\nchmod 644 /var/www/floridaalterations.com/backend/bills.php\nchown www-data:www-data /var/www/floridaalterations.com/backend/bills.php\n\n# Check PHP error logs\nssh kefa@192.168.7.202 \"tail -50 /var/log/php*-fpm.log\"\n\n# Restart PHP-FPM if needed\nssh kefa@192.168.7.202 \"sudo systemctl restart php*-fpm\"\n```\n\n---\n\n## Security Notes\n\n- Bank URLs are stored as-is - ensure only trusted users can edit accounts\n- No sensitive data encrypted in the database\n- Consider HTTPS-only for all bank URLs\n- Session-based access control should be configured at the application level\n\n---\n\n## Future Enhancements (Suggested)\n\n1. **Auto-fetch balance** - API integration with banks (requires bank credentials)\n2. **Email notifications** - Remind when bills are due\n3. **Export to CSV** - Download bill history\n4. **Recurring bills** - Auto-create monthly bills\n5. **Budget tracking** - Set monthly budget limits per category\n\n---\n\n## Version History\n\n| Version | Date | Changes |\n|---------|------|---------|\n| 1.0 | April 16, 2026 | Initial release with account types, bank URLs, status pills |\n| - | - | Auto-save functionality |\n| - | - | Save All buttons added |\n\n---\n\n*End of Documentation*\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"END_OF_DAY_2026-04-13.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"17","document_type":"upload","title":"Codex - End of day documentation 04-13-26","slug":"codex-end-of-day-documentation-04-13-26","summary_text":"Codex - End of day documentation 04-13-26","content_markdown":null,"content_html":null,"file_name":"END_OF_DAY_2026-04-13.md","stored_name":"20260413-200137-4e26a8e2.md","mime_type":"application/octet-stream","file_size_bytes":"11054","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260413-200137-4e26a8e2.md","is_deleted":"0","created_at":"2026-04-13 16:01:37","updated_at":"2026-04-13 16:01:37","editor_content":"# End Of Day Documentation - April 13, 2026\n\n## Session Scope\nThis document captures the production fixes and feature work completed on **Monday, April 13, 2026** for Florida Alterations CustomerDB, focused on:\n\n- totals correctness (today, month, and year comparison)\n- rebuild operations from backend UI\n- visit save stability and data sync\n- range-to-unit pricing behavior\n- Square payment traceability and reporting\n- next-day email cron stability\n\nThis is the end-of-day technical handoff and restart guide for tomorrow.\n\n---\n\n## Executive Summary\nToday we stabilized and aligned core reporting with business rules:\n\n1. **Year Comparison fixed** to use full prior month totals (not prior-year MTD comparable day).\n2. **Today totals fixed** to use strict `payments.payment_date` logic for today pills.\n3. **Totals rebuild UI added** so customer/daily rebuild can be run from backend (no SSH needed).\n4. **Range pricing behavior fixed** so range text parses last amount into unit price only on blur (no typing interruption).\n5. **Square customer history implemented** (new table + logging + customer history page + visit button integration).\n6. **Next-day emails cron fixed** for schema variance (`status` column missing in `setmore_appointments`).\n\n---\n\n## Business Rules Locked In Today\n\n## 1) Year Comparison\n- Prior implementation used prior-year MTD comparable day and showed values like `3858.83` for April 2025 MTD.\n- New behavior for year comparison uses **full prior month** range (example: `2025-04-01` through `2025-04-30`), showing `10215.20` for April 2025 when daily totals table contains that total.\n- Comparable-day value is still provided in API as `last_year_mtd` for any future UI that still needs it.\n\n## 2) Today totals\n- `today_totals` now use **strict payment-date window**:\n - `payment_date >= today 00:00:00`\n - `payment_date < tomorrow 00:00:00`\n- This avoids inflated totals from `COALESCE(payment_date, NOW())` when historical rows have NULL payment_date.\n\n## 3) Range field behavior\n- Range text accepts values such as `100-250` or `$100 - $250`.\n- Last numeric value (`250`) is applied to `unit_price`.\n- Auto-fill now occurs only **on field blur** (loss of focus), not on every keystroke.\n\n## 4) Square logging (go-forward model)\n- No historical backfill was forced today.\n- Going forward, each Square save writes structured rows to a new log table.\n- Front desk can open customer-specific Square history from visit screen.\n\n---\n\n## Implemented Changes (Detailed)\n\n## A. Totals + Reporting API changes\n\n### Files updated\n- `frontend/api/totals_snapshot.php`\n- `frontend/api/totals_snapshot_live.php`\n- `webui/api/totals_snapshot.php`\n- `webui/api/totals_snapshot_live.php`\n\n### What changed\n1. Year comparison `last_year` switched from prior-year comparable-day MTD to prior-year **full month**.\n2. Added `last_year_mtd` payload key for optional MTD comparisons.\n3. Added strict today payment aggregation function for `today_totals` to avoid NULL-date inflation.\n\n### Outcome\n- Year comparison now aligns with accounting expectation for full month.\n- Today pills align with strict payment rows and expected values (example target discussed: cash `32.06`, credit including tips `424.25`).\n\n---\n\n## B. Backend rebuild tooling\n\n### File added\n- `backend/totals_rebuild.php`\n\n### File updated\n- `backend/index.php` (link added to Totals Rebuild page)\n\n### Mirror route added\n- `webui/backend/totals_rebuild.php` (thin loader to canonical backend page)\n\n### What changed\n- Added backend UI to run:\n - customer totals rebuild for date range\n - daily totals rebuild for date range\n- Added quick compare widget showing today payments vs daily table buckets.\n\n### Outcome\n- Rebuild operations no longer require SSH for normal operations.\n\n---\n\n## C. Customer totals date attribution correction\n\n### Files updated\n- `backend/customer_totals_sync_service.php`\n- `frontend/api/visit_save.php` (local totals sync path)\n\n### What changed\n- Removed logic that shifted `customer_totals.customer_date` to latest payment date.\n- Restored visit-date attribution to prevent old deposits being dragged into today.\n\n### Outcome\n- Eliminated recurring `$30` day-shift drift pattern in daily totals.\n\n---\n\n## D. Visit save and frontend stability improvements\n\n### Files updated\n- `frontend/api/visit_save.php`\n- `frontend/api/visit_delete.php`\n- `frontend/visit.html`\n\n### What changed\n- Added local totals sync helper flow in visit save to avoid include/redeclare fatal paths.\n- Ensured save updates:\n - payment ledger sync\n - `customer_totals` upsert for visit\n - daily totals rebuild for affected date\n- Fixed deposit behavior edge in visit form load/save path.\n\n### Outcome\n- Visit save is stable and returns valid JSON in known fatal edge paths previously reported.\n\n---\n\n## E. Next-day customer emails cron fix\n\n### File updated\n- `backend/jobs/send_nextday_customer_emails.php`\n\n### Problem\n- Cron failed with:\n - `Unknown column 'sa.status' in 'WHERE'`\n\n### Fix\n- Added schema-aware status resolution:\n - prefers `status` if present\n - falls back to `label` or `status_label` when available\n - if none present, job still runs without status predicate\n\n### Outcome\n- Job no longer hard-fails on column mismatch.\n\n---\n\n## F. Range parsing -> unit price behavior\n\n### Files updated\n- `frontend/visit.html`\n- `frontend/api/visit_save.php`\n- `backend/visit.php`\n- `backend/visit_service.php`\n\n### What changed\n- Implemented parser to extract last numeric token from range label.\n- Applies value to `unit_price`.\n- UI only triggers on blur to avoid interrupting typing.\n- Server-side also enforces same rule for consistency.\n\n### Outcome\n- Staff can enter flexible range text while keeping reliable unit pricing for repeat visits.\n\n---\n\n## G. Square reporting and customer history\n\n### Files added\n- `backend/square_payments_report.php`\n- `webui/backend/square_payments_report.php` (mirror route)\n- `frontend/api/square_customer_log.php`\n- `frontend/square_customer.html`\n\n### Files updated\n- `backend/index.php` (Square Details link)\n- `frontend/visit.html` (Square Customer button behavior)\n- `frontend/api/visit_save.php` (log writes + table creation + duplicate guard)\n\n### New table\n- `square_customer_log` (auto-created in visit save path)\n\n### Captured fields\n- customer/visit/order identifiers\n- customer name/phone/email snapshots\n- event_type/message\n- Square reference fields:\n - payment_id\n - receipt_number\n - checkout_id\n - card_brand\n - last_4\n- payment method/type\n- amount/tip/total\n- structured details JSON\n- created_at timestamp\n\n### Behavior\n- On Square save, log entries are written for customer.\n- Duplicate guard prevents repeated identical inserts from repeated save clicks.\n- Visit screen `Square Customer` button now opens local customer history page pre-filtered by current visit customer.\n- Customer page includes quick external Square Directory open button.\n\n### Outcome\n- Front desk has immediate traceability for Square transactions/messages per customer going forward.\n\n---\n\n## Verification Performed\n\n## Syntax checks completed\nUsing `C:\\php\\php.exe -l` on all modified/added PHP files in this session:\n\n- `backend/customer_totals_sync_service.php`\n- `frontend/api/visit_save.php`\n- `backend/index.php`\n- `webui/backend/totals_rebuild.php`\n- `frontend/api/totals_snapshot.php`\n- `frontend/api/totals_snapshot_live.php`\n- `webui/api/totals_snapshot.php`\n- `webui/api/totals_snapshot_live.php`\n- `backend/jobs/send_nextday_customer_emails.php`\n- `backend/visit_service.php`\n- `backend/square_payments_report.php`\n- `webui/backend/square_payments_report.php`\n- `backend/visit.php`\n- `frontend/api/square_customer_log.php`\n\nAll lint checks passed with no syntax errors.\n\n## Data validation evidence reviewed\n- Workbook validation confirmed:\n - strict today rows matched expected payment set\n - prior-year MTD (`3858.83`) vs full-month (`10215.20`) mismatch source identified\n- Query results validated that full-month April 2025 totals are correct in both:\n - `daily_customer_totals`\n - `customer_totals`\n\n---\n\n## Operational Runbook (Current)\n\n## Rebuild UI\n- URL: `/backend/totals_rebuild.php`\n- Recommended range for live rebuild window:\n - `2026-04-10` to current date\n\n## Next-day emails dry-run\n```bash\nphp8.3 /mnt/drive1/customerdb/backend/jobs/send_nextday_customer_emails.php --dry-run\n```\n\n## Square detail report\n- URL: `/backend/square_payments_report.php`\n\n## Customer-specific Square history from visit\n- Visit page button: `Square Customer`\n- Opens local page:\n - `/square_customer.html?...`\n\n---\n\n## Open Items / Pending (Next Session)\n\n1. Confirm production deployment of all new files and mirror routes.\n2. Validate next-day cron in production after schema-aware status fix.\n3. Add optional backend module card for direct access to customer Square history page.\n4. Add optional import/backfill utility for `square_customer_log` from existing `visit_activity_log` for partial historical visibility (if desired).\n5. Monitor today/tomorrow totals for drift and confirm no reintroduction of payment-date shifting.\n6. Validate front-desk workflow on range blur behavior across both frontend and backend visit editors.\n\n---\n\n## Risks / Notes\n\n- Existing repository is heavily dirty with many unrelated changes; work was targeted to requested paths only.\n- Some backend pages are mirrored through `webui/backend` thin loaders; ensure deployment copies both canonical and mirror files where required.\n- Square log is go-forward by design; historical completeness is not guaranteed unless a dedicated backfill is added.\n\n---\n\n## Quick Diff Focus (Today’s primary touched paths)\n\n- `frontend/api/totals_snapshot.php`\n- `frontend/api/totals_snapshot_live.php`\n- `webui/api/totals_snapshot.php`\n- `webui/api/totals_snapshot_live.php`\n- `backend/totals_rebuild.php`\n- `webui/backend/totals_rebuild.php`\n- `backend/index.php`\n- `backend/customer_totals_sync_service.php`\n- `frontend/api/visit_save.php`\n- `frontend/api/visit_delete.php`\n- `frontend/visit.html`\n- `backend/visit.php`\n- `backend/visit_service.php`\n- `backend/jobs/send_nextday_customer_emails.php`\n- `backend/square_payments_report.php`\n- `webui/backend/square_payments_report.php`\n- `frontend/api/square_customer_log.php`\n- `frontend/square_customer.html`\n\n---\n\n## Status Summary\n\n- **DONE**\n - Year comparison full-month fix\n - Today strict payment-date totals fix\n - Backend totals rebuild UI + link + mirror route\n - Range blur-only auto-fill and parser\n - Next-day email schema mismatch fix\n - Square details report page\n - Square go-forward per-customer log (table + writes + API + page + visit button integration)\n\n- **VERIFIED**\n - Syntax checks pass on all changed PHP paths\n - Workbook query analysis validated root causes and corrected logic direction\n - Year comparison now aligned to full-month model\n\n- **BLOCKED / NOT COMPLETED TODAY**\n - Historical Square backfill into new log table (not done by design; go-forward only)\n - Broader production smoke test across all modules after deployment\n\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"TOTALS_CUTOFF_2026-04-12.odt","mime_type":"application/vnd.oasis.opendocument.text"}
[]
{"backend_document_id":"16","document_type":"upload","title":"Codex - 04-13 All Customer Totals Tables and Logic","slug":"codex-04-13-all-customer-totals-tables-and-logic","summary_text":"Codex - 04-13 All Customer Totals Tables and Logic","content_markdown":null,"content_html":null,"file_name":"TOTALS_CUTOFF_2026-04-12.odt","stored_name":"20260412-190147-cdbfac59.odt","mime_type":"application/vnd.oasis.opendocument.text","file_size_bytes":"31229","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260412-190147-cdbfac59.odt","is_deleted":"0","created_at":"2026-04-12 15:01:47","updated_at":"2026-04-12 15:01:47","editor_content":"Totals Cutover Documentation Date: 2026-04-12 Cutover Date: 2026-04-10 Purpose This documents the totals logic now used by the dashboard snapshot endpoints after go-live. Rule: Through 2026-04-09 : use imported totals from customer_totals Starting 2026-04-10 : use live payment data from payments This change was applied to: frontend/api/totals_snapshot.php frontend/api/totals_snapshot_live.php webui/api/totals_snapshot.php webui/api/totals_snapshot_live.php Current Logic The application now uses this date split: If the requested range ends before 2026-04-10 Read totals from customer_totals Fallback to CustomerTotals only if customer_totals has no rows for that range If the requested range starts on or after 2026-04-10 Read totals from live payments joined to orders , visits , and customers If the requested range crosses the cutoff Sum customer_totals for the pre-cutover side Sum live payments for the post-cutover side Add the two result sets together Fallback behavior If neither side returns data, the code can still fall back to: customer_payment_transactions daily_customer_totals customer_totals CustomerTotals Those are safety fallbacks only, not the intended source-of-truth for this cutover rule Tables Used Primary tables: customer_totals Imported totals table Source of truth for dates through 2026-04-09 payments Live payment rows Source of truth for dates on and after 2026-04-10 orders Used to connect payments to visits visits Used for visit date and customer linkage customers Used for customer counts and is_new_customer Fallback and audit tables: CustomerTotals Legacy Access-style table name Fallback only daily_customer_totals Stored daily rollups Fallback/audit only customer_payment_transactions Ledger table Useful for reconciliation and auditing Not the primary source for the current dashboard cutover logic Totals Columns Imported totals columns: total_customers new_customers cash_sales check_sales credit_card_sales edwards total_sales total_sales_no_edwards Live payment totals are calculated into these output buckets: total_customers new_customers cash check credit tips edwards total_sales total_sales_no_edwards Notes: customer_totals does not have a dedicated tips column For imported totals, tips are effectively included wherever they were represented in the import values In live totals, tips are broken out separately and also still contribute to total_sales In live totals, credit excludes cash , check , and edwards No Application DB View Required The application does not currently require a database view for this logic. It is implemented directly in PHP in the four snapshot endpoints listed above. If you want a database-level validation helper, use the optional views below. Optional Validation Views These views are for manual validation only. They are not required by the app. 1. Imported totals through 2026-04-09 CREATE OR REPLACE VIEW vw_totals_import_pre_2026_04_10 AS SELECT DATE(customer_date) AS totals_date, SUM(COALESCE(total_customers, 0)) AS total_customers, SUM(COALESCE(new_customers, 0)) AS new_customers, ROUND(SUM(COALESCE(cash_sales, 0)), 2) AS cash, ROUND(SUM(COALESCE(check_sales, 0)), 2) AS `check`, ROUND(SUM(COALESCE(credit_card_sales, 0)), 2) AS credit, 0.00 AS tips, ROUND(SUM(COALESCE(edwards, 0)), 2) AS edwards, ROUND(SUM(COALESCE(total_sales, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0)+COALESCE(edwards,0))), 2) AS total_sales, ROUND(SUM(COALESCE(total_sales_no_edwards, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0))), 2) AS total_sales_no_edwards FROM customer_totals WHERE DATE(customer_date) <= '2026-04-09' GROUP BY DATE(customer_date); 2. Live totals from 2026-04-10 forward CREATE OR REPLACE VIEW vw_totals_live_post_2026_04_09 AS SELECT DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)) AS totals_date, COUNT(DISTINCT c.customer_id) AS total_customers, COUNT(DISTINCT CASE WHEN COALESCE(c.is_new_customer, 0) = 1 THEN c.customer_id END) AS new_customers, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS cash, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS `check`, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_type, '')) LIKE '%tip%' THEN p.amount ELSE 0 END), 2) AS tips, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') OR (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') OR LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE p.amount END), 2) AS credit, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN p.amount ELSE 0 END), 2) AS edwards, ROUND(SUM(COALESCE(p.amount, 0)), 2) AS total_sales, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE COALESCE(p.amount, 0) END), 2) AS total_sales_no_edwards FROM payments p LEFT JOIN orders o ON o.order_id = p.order_id LEFT JOIN visits v ON v.visit_id = o.visit_id LEFT JOIN customers c ON c.customer_id = v.customer_id WHERE DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)) >= '2026-04-10' GROUP BY DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)); 3. Combined validation view CREATE OR REPLACE VIEW vw_totals_cutover_2026_04_10 AS SELECT * FROM vw_totals_import_pre_2026_04_10 UNION ALL SELECT * FROM vw_totals_live_post_2026_04_09; Copy/Paste Validation Queries A. Check imported totals only for 2026-04-09 and earlier SELECT ROUND(SUM(COALESCE(total_sales, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0)+COALESCE(edwards,0))), 2) AS total_sales, ROUND(SUM(COALESCE(total_sales_no_edwards, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0))), 2) AS total_sales_no_edwards, ROUND(SUM(COALESCE(cash_sales, 0)), 2) AS cash, ROUND(SUM(COALESCE(check_sales, 0)), 2) AS `check`, ROUND(SUM(COALESCE(credit_card_sales, 0)), 2) AS credit, ROUND(SUM(COALESCE(edwards, 0)), 2) AS edwards, SUM(COALESCE(total_customers, 0)) AS total_customers, SUM(COALESCE(new_customers, 0)) AS new_customers FROM customer_totals WHERE DATE(customer_date) BETWEEN '2026-04-01' AND '2026-04-09'; B. Check live totals only for 2026-04-10 and later SELECT ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS cash, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS `check`, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_type, '')) LIKE '%tip%' THEN p.amount ELSE 0 END), 2) AS tips, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') OR (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') OR LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE p.amount END), 2) AS credit, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN p.amount ELSE 0 END), 2) AS edwards, ROUND(SUM(COALESCE(p.amount, 0)), 2) AS total_sales, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE COALESCE(p.amount, 0) END), 2) AS total_sales_no_edwards, COUNT(DISTINCT c.customer_id) AS total_customers, COUNT(DISTINCT CASE WHEN COALESCE(c.is_new_customer, 0) = 1 THEN c.customer_id END) AS new_customers FROM payments p LEFT JOIN orders o ON o.order_id = p.order_id LEFT JOIN visits v ON v.visit_id = o.visit_id LEFT JOIN customers c ON c.customer_id = v.customer_id WHERE DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)) BETWEEN '2026-04-10' AND '2026-04-12'; C. Check month total using the same cutover logic as the app SELECT ROUND(SUM(total_sales), 2) AS total_sales, ROUND(SUM(total_sales_no_edwards), 2) AS total_sales_no_edwards, ROUND(SUM(cash), 2) AS cash, ROUND(SUM(`check`), 2) AS `check`, ROUND(SUM(credit), 2) AS credit, ROUND(SUM(tips), 2) AS tips, ROUND(SUM(edwards), 2) AS edwards, SUM(total_customers) AS total_customers, SUM(new_customers) AS new_customers FROM ( SELECT ROUND(SUM(COALESCE(total_sales, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0)+COALESCE(edwards,0))), 2) AS total_sales, ROUND(SUM(COALESCE(total_sales_no_edwards, COALESCE(cash_sales,0)+COALESCE(check_sales,0)+COALESCE(credit_card_sales,0))), 2) AS total_sales_no_edwards, ROUND(SUM(COALESCE(cash_sales, 0)), 2) AS cash, ROUND(SUM(COALESCE(check_sales, 0)), 2) AS `check`, ROUND(SUM(COALESCE(credit_card_sales, 0)), 2) AS credit, 0.00 AS tips, ROUND(SUM(COALESCE(edwards, 0)), 2) AS edwards, SUM(COALESCE(total_customers, 0)) AS total_customers, SUM(COALESCE(new_customers, 0)) AS new_customers FROM customer_totals WHERE DATE(customer_date) BETWEEN '2026-04-01' AND '2026-04-09' UNION ALL SELECT ROUND(SUM(COALESCE(p.amount, 0)), 2) AS total_sales, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE COALESCE(p.amount, 0) END), 2) AS total_sales_no_edwards, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS cash, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') AND NOT ( LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' ) THEN p.amount ELSE 0 END), 2) AS `check`, ROUND(SUM(CASE WHEN (LOWER(COALESCE(p.payment_method, '')) LIKE '%cash%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%cash%') OR (LOWER(COALESCE(p.payment_method, '')) LIKE '%check%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%check%') OR LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN 0 ELSE p.amount END), 2) AS credit, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_type, '')) LIKE '%tip%' THEN p.amount ELSE 0 END), 2) AS tips, ROUND(SUM(CASE WHEN LOWER(COALESCE(p.payment_method, '')) LIKE '%edward%' OR LOWER(COALESCE(p.payment_type, '')) LIKE '%edward%' OR LOWER(COALESCE(o.order_status, '')) LIKE '%edward%' OR LOWER(COALESCE(v.visit_type, '')) LIKE '%edward%' OR LOWER(COALESCE(NULLIF(c.full_name, ''), v.customer_name_snapshot, '')) LIKE '%edward%' THEN p.amount ELSE 0 END), 2) AS edwards, COUNT(DISTINCT c.customer_id) AS total_customers, COUNT(DISTINCT CASE WHEN COALESCE(c.is_new_customer, 0) = 1 THEN c.customer_id END) AS new_customers FROM payments p LEFT JOIN orders o ON o.order_id = p.order_id LEFT JOIN visits v ON v.visit_id = o.visit_id LEFT JOIN customers c ON c.customer_id = v.customer_id WHERE DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)) BETWEEN '2026-04-10' AND '2026-04-12' ) t; Quick Daily Checks Check one day from imported totals: SELECT * FROM customer_totals WHERE DATE(customer_date) = '2026-04-09'; Check one day from live payments: SELECT p.payment_id, p.order_id, v.visit_id, c.customer_id, c.full_name, p.payment_date, p.payment_method, p.payment_type, p.amount FROM payments p LEFT JOIN orders o ON o.order_id = p.order_id LEFT JOIN visits v ON v.visit_id = o.visit_id LEFT JOIN customers c ON c.customer_id = v.customer_id WHERE DATE(COALESCE(p.payment_date, o.created_at, v.visit_date)) = '2026-04-10' ORDER BY COALESCE(p.payment_date, o.created_at, v.visit_date), p.payment_id; Related Files totals_snapshot.php totals_snapshot_live.php totals_snapshot.php totals_snapshot_live.php totals_service.php report_totals_audit.php","is_text_editable":1,"can_edit_inline":0}
{"file_name":"IMPLEMENTATION_STATUS_2026-04-12.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"15","document_type":"upload","title":"Codes - 04-13-2026 DONE / VERIFIED / BLOCKED","slug":"codes-04-13-2026-done-verified-blocked","summary_text":"Codes - 04-13-2026 DONE / VERIFIED / BLOCKED","content_markdown":null,"content_html":null,"file_name":"IMPLEMENTATION_STATUS_2026-04-12.md","stored_name":"20260412-164417-5a72fe80.md","mime_type":"application/octet-stream","file_size_bytes":"5341","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260412-164417-5a72fe80.md","is_deleted":"0","created_at":"2026-04-12 12:44:17","updated_at":"2026-04-12 12:44:17","editor_content":"# CustomerDB Implementation Status\n\nDate: 2026-04-12\nOwner: Codex + Front Desk Team\nFormat: DONE / VERIFIED / BLOCKED\n\n## DONE\n\n- Added cron smoke-test preflight for morning jobs:\n - `backend/jobs/smoke_morning_jobs.php`\n - Wired in cron manager command chain:\n - `backend/cron_manager.php`\n - `backend/README.md`\n\n- Fixed unified appointments SQL ambiguity (`customer_id`) and stabilized combined feed:\n - `backend/appointment_service.php`\n\n- Implemented Setmore import lock + booking source + stronger note extraction:\n - `backend/setmore_service.php`\n - `backend/morning_jobs_service.php`\n - `backend/install_schema.php`\n\n- Added single transaction ledger foundation (`customer_payment_transactions`) and tip support:\n - `backend/install_schema.php`\n - `backend/payment_ledger_service.php`\n - `backend/payment_ledger.php`\n - `backend/live_table_manager_service.php`\n\n- Added ledger rebuild and legacy CSV import tooling:\n - `backend/jobs/rebuild_payment_ledger.php`\n - `backend/jobs/import_customer_totals_csv_to_ledger.php`\n\n- Added reconciliation and audit backend modules:\n - `backend/report_reconciliation.php`\n - `backend/report_totals_audit.php`\n - `backend/report_unmatched.php`\n - Linked in:\n - `backend/reports.php`\n - `backend/index.php`\n\n- Switched report/snapshot paths to ledger-first behavior with fallbacks:\n - `backend/report_service.php`\n - `frontend/api/totals_snapshot.php`\n - `frontend/api/totals_snapshot_live.php`\n - `webui/api/totals_snapshot.php`\n - `webui/api/totals_snapshot_live.php`\n\n- Stabilized visit save and item type persistence:\n - `frontend/api/visit_save.php`\n - `frontend/api/visit_get.php`\n - `frontend/api/visit_report_generate.php`\n - `backend/visit_service.php`\n - migration:\n - `migrations/20260411_order_items_item_type_name_no_fk.sql`\n - schema:\n - `mariadb_core_schema.sql`\n\n- Updated receipt/work-order logic to prefer ledger totals:\n - `frontend/receipt.html`\n - `frontend/work_order.html`\n - `frontend/api/visit_get.php`\n - `frontend/api/visit_report_generate.php`\n\n- Updated visit email receipt generation to use live visit data and ledger-aware totals:\n - `frontend/visit.html`\n\n- Payroll robustness updates (Yana/Jana alias handling, paste parser hardening, clearer import diagnostics):\n - `backend/payroll_service.php`\n - `backend/payroll.php`\n\n- Added frontend activity monitor for backend checks:\n - `backend/frontend_activity.php`\n\n- Added frontend item type management page and APIs:\n - `frontend/item_types.html`\n - `frontend/api/item_type_update.php`\n - `frontend/api/item_type_delete.php`\n\n- Fixed front desk title/refresh weird text artifacts:\n - `frontend/index.html`\n\n- Introduced backend canonical loaders under `webui/backend/*` to reduce drift:\n - Example files:\n - `webui/backend/bootstrap.php`\n - `webui/backend/payment_ledger.php`\n - `webui/backend/report_reconciliation.php`\n\n## VERIFIED\n\n- Morning cron smoke chain is working from user-provided run output:\n - smoke test passes\n - morning jobs run completes with `Exit Code: 0`\n\n- Legacy import command executed successfully by user:\n - imported rows: `2757`\n - date range imported: `2023-12-24` to `2026-04-06`\n - forward resync rows: `55`\n\n- PHP syntax check passed for critical modules:\n - `backend/payment_ledger.php`\n - `backend/payment_ledger_service.php`\n - `backend/report_reconciliation.php`\n - `backend/report_totals_audit.php`\n - `backend/jobs/import_customer_totals_csv_to_ledger.php`\n - `backend/jobs/rebuild_payment_ledger.php`\n - `backend/jobs/smoke_morning_jobs.php`\n - `backend/morning_jobs_service.php`\n - `backend/appointment_service.php`\n - `frontend/api/visit_get.php`\n - `frontend/api/visit_save.php`\n - `frontend/api/visit_report_generate.php`\n - `backend/payroll_service.php`\n - `backend/payroll.php`\n\n## BLOCKED\n\n- Month-end totals still not fully matched to expected Access truth for selected periods:\n - April 2025 mismatch remains\n - April 2026 daily range mismatch remains for at least one target comparison\n - Edwards bucket attribution still has unresolved edge cases on some records\n\n- Full parity confirmation still needed for all frontend totals surfaces:\n - `reports.html`\n - dashboard `index.html`\n - `webui.html`\n - Goal: all must reconcile against `customer_payment_transactions` outputs\n\n- Receipt parity final QA still pending:\n - Need side-by-side validation on known problematic visits:\n - print receipt\n - email receipt\n - backend generated receipt report\n\n- Payroll API alias parity incomplete:\n - `backend/api/payroll_import.php` still strictly accepts only `Yana` or `Lola`\n - Should be aligned with alias handling in `backend/payroll_service.php`\n\n- Release hygiene blocked by broad workspace churn:\n - Many unrelated changed/deleted files exist in current worktree\n - Needs isolation/staging before safe production commit/deploy\n\n## Next Actions\n\n1. Run reconciliation for April 2025 and April 2026 target windows and fix row-level ledger bucket/date mismatches.\n2. Confirm dashboard and report UI totals are reading ledger-backed endpoints only.\n3. Execute receipt parity QA on named customer/visit samples and sign off.\n4. Align payroll import API alias handling with payroll service.\n5. Prepare a clean staged commit containing only intended production files.\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"Codex-Update-04-12-2026.odt","mime_type":"application/vnd.oasis.opendocument.text"}
[]
{"backend_document_id":"14","document_type":"upload","title":"Codex - Updates Fixes Issues 04-13-2026","slug":"codex-updates-fixes-issues-04-13-2026","summary_text":"Codex - Updates Fixes Issues 04-13-2026 Detailed Documentation","content_markdown":null,"content_html":null,"file_name":"Codex-Update-04-12-2026.odt","stored_name":"20260412-164224-b4b11b83.odt","mime_type":"application/vnd.oasis.opendocument.text","file_size_bytes":"49423","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260412-164224-b4b11b83.odt","is_deleted":"0","created_at":"2026-04-12 12:42:24","updated_at":"2026-04-12 12:42:24","editor_content":"Comprehensive implementation log (through April 12, 2026 ) and remaining work: Scope Snapshot The branch currently has very large churn ( 242 files changed ), including canonical backend work plus broad webui/backend/* compatibility-loader rewiring. Core functional work for your issues is concentrated in backend/frontend reporting, Setmore import, visit save/receipt logic, payroll import, and payment ledger. I also validated syntax for key new/modified PHP modules (all clean). What Was Implemented Cron hardening + morning smoke test Added cron-safe preflight script to fail fast when runtime prerequisites are missing: backend/jobs/smoke_morning_jobs.php Smoke test now explicitly checks mysqli extension and required functions (including app_unified_appointments ). Wired cron “Morning Import” and “Morning Jobs” commands to run smoke test first: backend/cron_manager.php Documented smoke command chain in backend docs: backend/README.md Fixed unified appointments SQL ambiguity / cron fatal Added/updated centralized unified appointment query service: backend/appointment_service.php Fixed ambiguous customer_id in union query by qualifying Setmore side as setmore_appointments.customer_id . Included setmore_import_locked in unified rows (used by workflow/import safeguards). Setmore import improvements (notes + booking source + lock flag) Implemented robust note extraction from multiple payload fields and flattened additional fields: backend/setmore_service.php Added booking source extraction ( booking_source / booked_from / parsed note line “Booked from ...”) and write-through to visits when column exists. Added import lock behavior ( setmore_import_locked ) so re-import does not overwrite locked visits. Added lock column bootstrap/ensure + bulk lock helper: backend/morning_jobs_service.php Added schema evolution for lock and ledger fields in installer: backend/install_schema.php Visit save 500 fix + item type reliability Added item_type_name persistence to order_items and updated reads to prefer stored label (fallback to item_types ): frontend/api/visit_save.php , frontend/api/visit_get.php , frontend/api/visit_report_generate.php , backend/visit_service.php Added migration to backfill item_type_name and drop fragile FK dependency on type table: migrations/20260411_order_items_item_type_name_no_fk.sql Updated base schema accordingly: mariadb_core_schema.sql Payment ledger architecture (single transaction table with tips) Added/expanded canonical ledger table customer_payment_transactions with tip_amount and indexes: backend/install_schema.php Implemented sync/rebuild services from payments with bucket classification ( cash/check/credit/edwards/tip ): backend/payment_ledger_service.php Added ledger rebuild CLI: backend/jobs/rebuild_payment_ledger.php Added ledger UI/report page with filters and summary cards: backend/payment_ledger.php Added ledger table to Live Table Manager map: backend/live_table_manager_service.php Legacy CustomerTotals import into ledger + forward resync Added CLI import from Access CSV into ledger ( source=legacy_customer_totals ) and optional forward rebuild from next date: backend/jobs/import_customer_totals_csv_to_ledger.php Your run imported 2757 rows for 2023-12-24 to 2026-04-06 and resynced 55 rows forward ( 2026-04-07 to 2026-04-12 ). Report totals now ledger-first (with fallback strategy) Added ledger-driven daily/period/month/year aggregation paths, including Edwards handling and totals-no-Edwards logic: backend/report_service.php Updated dashboard snapshot APIs to use ledger when range rows exist (instead of always preferring legacy tables): frontend/api/totals_snapshot.php , frontend/api/totals_snapshot_live.php , webui/api/totals_snapshot.php , webui/api/totals_snapshot_live.php New reconciliation / audit backend modules Added totals reconciliation page (report total vs ledger total vs daily mismatch rows + exact ledger rows): backend/report_reconciliation.php Added daily source-vs-source totals audit page: backend/report_totals_audit.php Added unmatched report attachment helper page: backend/report_unmatched.php Added navigation links in backend home/reports: backend/index.php , backend/reports.php Receipts and work orders moved toward ledger truth visit_get now returns ledger payment rows/totals (deposit/tip/paid-toward-balance): frontend/api/visit_get.php PDF receipt/work-order generator uses ledger-first totals and item_type_name fallback: frontend/api/visit_report_generate.php Frontend receipt/work-order screens updated to prefer ledger_totals : frontend/receipt.html , frontend/work_order.html Save flow includes tip safety net so Square tip is persisted as a Tip payment row if missing in payload: frontend/api/visit_save.php , backend/visit_service.php Email receipt vs print receipt mismatch Reworked email receipt generation path in visit page to build from live visit_get + settings_get data and ledger-aware totals, replacing simplified/legacy divergence path: frontend/visit.html Payroll module fixes Added alias normalization and clause support ( Jana/Janna -> Yana ) so counts/reports don’t zero out due name variants: backend/payroll_service.php Improved pasted Excel parsing robustness (delimiter/header detection, fallback parsing, skip-reason diagnostics): backend/payroll_service.php UI now surfaces import diagnostics and styled KPI cards: backend/payroll.php Frontend activity monitor Added backend page to monitor frontend edits/signatures/status changes using backend_change_log : backend/frontend_activity.php UI cleanup for weird characters Frontdesk title and refresh text normalized to plain readable values: frontend/index.html Item types management module Added standalone item type manager page and update/delete APIs: frontend/item_types.html , frontend/api/item_type_update.php , frontend/api/item_type_delete.php Backend/WebUI canonicalization Large set of webui/backend/* files converted to thin loaders pointing to canonical /backend/* files to reduce drift; examples: webui/backend/bootstrap.php , webui/backend/payment_ledger.php , webui/backend/report_reconciliation.php Validation Done Ran PHP lint on critical modules; all reported “No syntax errors detected”: backend/payment_ledger.php , backend/payment_ledger_service.php , backend/report_reconciliation.php , backend/report_totals_audit.php backend/jobs/import_customer_totals_csv_to_ledger.php , backend/jobs/rebuild_payment_ledger.php , backend/jobs/smoke_morning_jobs.php backend/morning_jobs_service.php , backend/appointment_service.php frontend/api/visit_get.php , frontend/api/visit_save.php , frontend/api/visit_report_generate.php backend/payroll_service.php , backend/payroll.php What Is Still Left To Do Finalize totals parity for April 2025 / April 2026 edge cases Remaining mismatch indicates ledger contents/classification still differ from your Access truth set for some rows (especially Edwards split + date bucketing). Action left: run reconciliation for target ranges and correct source rows/bucket rules until report total == ledger total == expected monthly total. Confirm all frontend totals pages are fully ledger-backed end-to-end You asked reports.html , dashboard , index.html , webui.html to use new table. Snapshot endpoints were updated, but full UI-path verification for each page still needs click-through + data diff. Receipt equivalence final QA Email receipt builder was unified in frontend/visit.html , but still needs real-record side-by-side test against print receipt on the problematic visits to confirm exact parity in totals/labels/lines. Payroll import API parity backend/api/payroll_import.php still hard-validates worker as only Yana/Lola (no alias normalization there). If that endpoint is used directly, aliases may still fail. Should align this API with service-layer alias logic. Ledger population completeness You imported Access CSV and forward-resynced, but full historical backfill strategy still needs final sign-off: Decide whether to trust imported Access rows as authoritative for all prior dates and keep live rebuild only forward. Run one full-range ledger audit after any additional historical imports. Potentially risky broad file churn review before release There are many unrelated changed/deleted files (including docs storage artifacts and backups). Needs staging/cleanup before commit/deploy so only intended production changes are shipped.","is_text_editable":1,"can_edit_inline":0}
{"file_name":"SESSION_LOG_2026-04-10.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"13","document_type":"upload","title":"Codex - Updates 04-10-2026","slug":"codex-updates-04-10-2026","summary_text":"All updates and documentation for Codex - Updates 04-10-2026","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-10.md","stored_name":"20260410-200855-4b6f846d.md","mime_type":"application/octet-stream","file_size_bytes":"9223","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260410-200855-4b6f846d.md","is_deleted":"0","created_at":"2026-04-10 16:08:55","updated_at":"2026-04-10 16:08:55","editor_content":"# Session Log - 2026-04-10\n\nThis document captures the main fixes and modules completed during the live-data/go-live support session.\n\n## Customer Go-Live Data\n\n- Reviewed the live load path for `customers`, `visits`, `orders`, `order_items`, and skipped `payments` because the bridge source only had summary-style `CustomerTotals`, not reliable one-row-per-payment detail.\n- Confirmed the remaining unmatched Setmore/import rows were considered junk and safe to ignore.\n- Identified the only manual staging fixes needed before the production load:\n - fix `Priscilla`\n - fix `Rae Sanders`\n - ignore `Maria Martinez`\n- Prepared the go-live sequence:\n - backup current live tables\n - refresh staging\n - apply manual staging fixes\n - run preview script\n - run production load\n - run post-load audits\n - spot-check real customers in the app\n- Provided a backup-table SQL pattern for pre-go-live snapshots and later cleanup.\n\n## Visit Page\n\n- Added a safe staff override path so staff can leave `visit.html` without forcing a customer signature when the customer is not present.\n- Kept signature requirements for print/email/report actions, so the override only affects navigation.\n- Added a Bridge Compare popup/grid to `visit.html`.\n- Added backend endpoint for bridge comparison:\n - `frontend/api/bridge_customer_compare.php`\n- Updated the bridge comparison query to match by current customer name only:\n - `CustomerTable` joined to `CustomerVisits1` on `CustomerName`\n - `CustomerTable` joined to `AlterationItems` on `CustomerName`\n- Fixed bridge popup JSON failures caused by missing helper/bootstrap loading.\n- Fixed visit type persistence so saved `Pick Up` visits do not revert back to `Drop Off` unless a user changes it.\n- Fixed visit save/payment handling so the frontend payment array is respected instead of collapsing into a single payment row.\n- Fixed order totals so deposits plus payments reduce the due balance correctly.\n\n## Reports And Customer Totals\n\n- Fixed `reports.html` ready/customer results so paid totals calculate from live `orders` and `payments` by `visit_id`.\n- Fixed `customer.html` so paid totals are recomputed from live `payments`.\n- Fixed top totals snapshots to prefer live `visits`, `orders`, and `payments`, falling back to `customer_totals` only when no live data exists.\n- Updated `webui/api/totals_snapshot_live.php` so `webui.html` totals are also live-data based.\n- Added stronger visual separation between Today totals and Month-to-Date totals.\n- Added a `Completed` report filter.\n- Added per-record `Completed` badge logic.\n- Deduplicated report cards by `customer_id` to avoid duplicate customer cards such as Tabitha Torres.\n- Linked reports to new import paths:\n - Morning Import\n - Imported Today\n - End Of Day Review\n\n## End Of Day Review\n\n- Added the End Of Day Review module:\n - `backend/end_of_day_review.php`\n - `webui/backend/end_of_day_review.php`\n- The module summarizes:\n - daily closeout totals\n - appointments needing review\n - imported rows for the selected day\n - recent cron/job runs\n - no-shows\n - final status mix\n- Linked it from Backend Home and Reports.\n\n## Customer Emails And Reviews\n\n- Expanded the Customer Emails & Reviews workflow:\n - Send Email\n - Mark Sent\n - Ignore\n - outreach status tracking\n - delivery/result tracking\n - change-log entries\n- Updated files:\n - `backend/cust_email.php`\n - `backend/cust_email_service.php`\n - `webui/backend/cust_email.php`\n - `webui/backend/cust_email_service.php`\n- Updated the history view into an Outreach Log.\n\n## Email Hub Date And Time\n\n- Improved loaded appointment context handling in Email Hub.\n- Added editable appointment fields:\n - combined appointment date/time\n - appointment date\n - appointment time\n- Confirmation drafts and Setmore note updates now merge in the edited appointment fields.\n- Updated files:\n - `backend/email.php`\n - `webui/backend/email.php`\n\n## Cron Logging And Notifications\n\n- Added cron run logging through a shared runner.\n- Added recent run history to Cron Manager.\n- Added wrapper-based cron execution:\n - `backend/jobs/run_cron_job.php`\n - `webui/backend/jobs/run_cron_job.php`\n- Added shared runtime service:\n - `backend/jobs/cron_runtime_service.php`\n - `webui/backend/jobs/cron_runtime_service.php`\n- Updated cron export so scheduled jobs call the wrapper rather than raw commands.\n- Added notification plumbing for:\n - Telegram\n - email\n - SMS/Twilio\n- Added settings support for cron notification email.\n\n## Database Backup Module\n\n- Added a real Database Backup backend module.\n- It snapshots the major five production workflow tables:\n - `customers`\n - `visits`\n - `orders`\n - `order_items`\n - `payments`\n- Snapshots go into a separate database, default:\n - `ellas_alterations_backup`\n- Snapshot table names use suffixes like:\n - `customers__snapshot_YYYYMMDD_HHMMSS`\n- Snapshot history is logged in:\n - `backup_database_snapshot_log`\n- Added delete support for backup sets so backup tables do not grow without control.\n- Delete Set drops all backup tables for that snapshot and removes the matching log rows.\n- Updated files:\n - `backend/backup_database.php`\n - `backend/backup_database_service.php`\n - `webui/backend/backup_database.php`\n - `webui/backend/backup_database_service.php`\n\n## Live Table Manager\n\n- Added a guarded live table manager for direct maintenance of the five workflow tables:\n - `customers`\n - `visits`\n - `orders`\n - `order_items`\n - `payments`\n- Supports:\n - search\n - add\n - edit\n - delete\n- Every save/delete writes to the change log.\n- Added backup warning and quick link to Database Backup.\n- New files:\n - `backend/live_table_manager.php`\n - `backend/live_table_manager_service.php`\n - `webui/backend/live_table_manager.php`\n - `webui/backend/live_table_manager_service.php`\n- Linked from Backend Home.\n\n## Change Logging\n\n- Expanded visit-save logging so related entities are logged more clearly:\n - customer\n - visit\n - order\n - alterations/order items\n - payments\n- Added change logging for backup snapshot create/delete.\n- Added change logging for live table manager save/delete.\n\n## Edwards Mileage Split\n\n- Added an `Edwards Type` field for customer totals:\n - mileage\n - maintenance\n - other\n- Added separate Edwards totals in the drill view.\n- Updated schema installer to include `edwards_type`.\n- Updated:\n - `backend/customer_totals.php`\n - `backend/customer_totals_service.php`\n - `webui/backend/customer_totals.php`\n - `webui/backend/customer_totals_service.php`\n - `backend/install_schema.php`\n - `webui/backend/install_schema.php`\n\n## Setmore And Bridge Freshness\n\n- Added Setmore/bridge freshness module:\n - `backend/setmore_import_status.php`\n - `webui/backend/setmore_import_status.php`\n- Shows local import status and bridge table freshness for:\n - `ellas_access_bridge.CustomerTable`\n - `ellas_access_bridge.CustomerVisits1`\n - `ellas_access_bridge.AlterationItems`\n - `ellas_access_bridge.CustomerTotals`\n- Updated Setmore service so it prefers `app_settings` values before older config-file values.\n\n## Umami\n\n- Diagnosed iframe failure:\n - Umami sends restrictive frame/CSP headers.\n - HTTP iframe inside HTTPS backend is blocked as mixed content.\n- Built Ella-specific Umami source from `C:\\code\\umami`.\n- Initially tested a split setup:\n - app on `192.168.7.202`\n - database on `192.168.7.206`\n- Found repeated database connection failures on the split app:\n - `Connection terminated unexpectedly`\n- Moved the Ella Umami app to `192.168.7.206` so app and database run together.\n- Current direct login:\n - `http://192.168.7.206:3006/login`\n- Shut down the unstable `202` mirror app.\n- Updated backend Umami launcher pages:\n - `backend/umami.php`\n - `webui/backend/umami.php`\n- The iframe now targets the planned proxied path:\n - `/backend/umami-embed/login`\n- Nginx still needs the reverse proxy block for true HTTPS embedding.\n\n## WebUI Dashboard\n\n- Added `Current Speed Dial` to `webui.html` above the Finance card.\n- Changed Speed Dial on `webui.html` into a compact spreadsheet-style table so it uses less vertical space.\n- The section loads live data from:\n - `/backend/api/speed_dial.php`\n- Updated:\n - `webui/webui.html`\n\n## Medication Module\n\n- Fixed black/dark button styling in the Medication module by adding explicit theme-safe button styling.\n- Updated:\n - `backend/medication.php`\n - `webui/backend/medication.php`\n\n## Backend Navigation\n\n- Renamed Bills Manager to Pay Bills in visible backend UI.\n- Added links/cards for:\n - Database Backup\n - Live Table Manager\n - End Of Day Review\n - Setmore/Bridge Freshness\n\n## Remaining Follow-Up\n\n- Deploy/sync changed files to the live server.\n- Run the schema installer before using Edwards type live.\n- Test the Backup Delete Set button with a disposable snapshot.\n- Test Live Table Manager carefully after taking a backup snapshot.\n- Add the Nginx `/backend/umami-embed/` reverse proxy block before expecting Umami iframe embedding to work.\n- Verify Twilio credentials end-to-end.\n- Verify Email Hub date/time behavior on real imported emails.\n- Continue improving printable Daily Detail and Daily Appointment Operations layouts if needed.\n","is_text_editable":1,"can_edit_inline":1}
{"document_type":"markdown"}
{"backend_document_id":"12","document_type":"upload","title":"Square - Telegram IDS","slug":"square-telegram-ids","summary_text":"Square - Telegram IDS tokens and chat information","content_markdown":null,"content_html":null,"file_name":"SquareTelegramInfo.txt","stored_name":"20260408-160751-b757b5ec.txt","mime_type":"text/plain","file_size_bytes":"1716","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260408-160751-b757b5ec.txt","is_deleted":"0","created_at":"2026-04-08 12:07:51","updated_at":"2026-04-08 12:07:51","editor_content":"The good news: everything is already built. No coding needed. Here's what you actually have to do:\r\n\r\nThe 6 steps:\r\nCreate a Square Developer Application at developer.squareup.com (free, ~2 min)\r\n\r\nCollect 3 credentials from the Square Developer dashboard:\r\n\r\nProduction Application ID (sq0idp-...)\r\nProduction Access Token (EAAAl...)\r\nLocation ID (L...)\r\nEnter in Settings page — the app's Settings screen has a Square Payments section. Put in the Access Token and Location ID there.\r\n\r\nFill in /frontend/api/config.php on the server — add all 3 credentials and change 'environment' => 'sandbox' to 'production'\r\n\r\nPair the Terminal device to your Square account/location via the device's own settings screen (if not already done)\r\n\r\nTest — open a visit, click \"Send to Terminal\", select the device, watch the Terminal screen light up\r\n\r\nThe document also includes the complete troubleshooting section for common issues (empty device list, auth failures, sandbox vs production mix-ups) and a quick-reference table showing which credentials go where.\r\n\r\n\r\nSandbox\r\nApplication ID\r\nsandbox-sq0idb-RADNkYqnTwtu2oXD-mJrHw\r\n\r\nAccess Token\r\nEAAAl7SwvhAwj63SPJuFnP0Bb2bbZQYwr_oLdiiXPprm3Yhf59YWfS6sQcTy7EVe\r\n\r\n\r\n\r\nLive\r\nApplication ID\r\nsq0idp-Ug5yEttIeIxgVk_FbfUXHw\r\n\r\nProduction Access Token\r\nEAAAlx3Bg1jm3Xa-qXGcy4BmucPfqmYTRqy1mISS5YeETWonr_4d8xSgnMU1_aQv\r\n\r\n\r\ndefault test account\r\nEAAAl7SwvhAwj63SPJuFnP0Bb2bbZQYwr_oLdiiXPprm3Yhf59YWfS6sQcTy7EVe\r\n\r\nSandbox\r\ntest Locations\r\nDefault Test Account (Main)\r\n1600 Pennsylvania Ave NW\r\nLQ9JH35T5GAYS\r\n7299\r\n\r\nProduction\r\nElla’s Alterations (Main)\r\n6986 Fort King Road\r\n5W9V4C95PWDVQ\r\n7230\r\n\r\n\r\ntelegram\r\n7700366760:AAEeLLs1XvbapLFx7biDSTC2quO8NzjfBac\r\n7990799442\r\n\r\n","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"12","document_type":"markdown","title":"Change Of Business Hours","slug":"change-of-business-hours","summary_text":"This is all the areas to change for hour change","content_markdown":"# **Hours**\r\nSetmore\r\nNew Customerdb\r\nWebsite\r\nFacebook\r\nNextdoor\r\nYelp","content_html":"<h1>**Hours**</h1>\n<p>Setmore</p>\n<p>New Customerdb</p>\n<p>Website</p>\n<p>Facebook</p>\n<p>Nextdoor</p>\n<p>Yelp</p>","file_name":"SquareTelegramInfo.txt","stored_name":"20260408-160751-b757b5ec.txt","mime_type":"text/plain","file_size_bytes":"1716","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260408-160751-b757b5ec.txt","is_deleted":"0","created_at":"2026-04-08 12:07:51","updated_at":"2026-04-10 13:20:34","editor_content":"# **Hours**\r\nSetmore\r\nNew Customerdb\r\nWebsite\r\nFacebook\r\nNextdoor\r\nYelp","is_text_editable":1,"can_edit_inline":1}
{"file_name":"SquareTelegramInfo.txt","mime_type":"text/plain"}
[]
{"backend_document_id":"12","document_type":"upload","title":"Square - Telegram IDS","slug":"square-telegram-ids","summary_text":"Square - Telegram IDS tokens and chat information","content_markdown":null,"content_html":null,"file_name":"SquareTelegramInfo.txt","stored_name":"20260408-160751-b757b5ec.txt","mime_type":"text/plain","file_size_bytes":"1716","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260408-160751-b757b5ec.txt","is_deleted":"0","created_at":"2026-04-08 12:07:51","updated_at":"2026-04-08 12:07:51","editor_content":"The good news: everything is already built. No coding needed. Here's what you actually have to do:\r\n\r\nThe 6 steps:\r\nCreate a Square Developer Application at developer.squareup.com (free, ~2 min)\r\n\r\nCollect 3 credentials from the Square Developer dashboard:\r\n\r\nProduction Application ID (sq0idp-...)\r\nProduction Access Token (EAAAl...)\r\nLocation ID (L...)\r\nEnter in Settings page — the app's Settings screen has a Square Payments section. Put in the Access Token and Location ID there.\r\n\r\nFill in /frontend/api/config.php on the server — add all 3 credentials and change 'environment' => 'sandbox' to 'production'\r\n\r\nPair the Terminal device to your Square account/location via the device's own settings screen (if not already done)\r\n\r\nTest — open a visit, click \"Send to Terminal\", select the device, watch the Terminal screen light up\r\n\r\nThe document also includes the complete troubleshooting section for common issues (empty device list, auth failures, sandbox vs production mix-ups) and a quick-reference table showing which credentials go where.\r\n\r\n\r\nSandbox\r\nApplication ID\r\nsandbox-sq0idb-RADNkYqnTwtu2oXD-mJrHw\r\n\r\nAccess Token\r\nEAAAl7SwvhAwj63SPJuFnP0Bb2bbZQYwr_oLdiiXPprm3Yhf59YWfS6sQcTy7EVe\r\n\r\n\r\n\r\nLive\r\nApplication ID\r\nsq0idp-Ug5yEttIeIxgVk_FbfUXHw\r\n\r\nProduction Access Token\r\nEAAAlx3Bg1jm3Xa-qXGcy4BmucPfqmYTRqy1mISS5YeETWonr_4d8xSgnMU1_aQv\r\n\r\n\r\ndefault test account\r\nEAAAl7SwvhAwj63SPJuFnP0Bb2bbZQYwr_oLdiiXPprm3Yhf59YWfS6sQcTy7EVe\r\n\r\nSandbox\r\ntest Locations\r\nDefault Test Account (Main)\r\n1600 Pennsylvania Ave NW\r\nLQ9JH35T5GAYS\r\n7299\r\n\r\nProduction\r\nElla’s Alterations (Main)\r\n6986 Fort King Road\r\n5W9V4C95PWDVQ\r\n7230\r\n\r\n\r\ntelegram\r\n7700366760:AAEeLLs1XvbapLFx7biDSTC2quO8NzjfBac\r\n7990799442\r\n\r\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"SESSION_LOG_2026-04-07.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"11","document_type":"upload","title":"Codex 04-07-2026 Documentation","slug":"codex-04-07-2026-documentation","summary_text":"Codex 04-07-2026 Documentation everything that has been completed today","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-07.md","stored_name":"20260407-191340-bcefed15.md","mime_type":"application/octet-stream","file_size_bytes":"12740","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260407-191340-bcefed15.md","is_deleted":"0","created_at":"2026-04-07 15:13:40","updated_at":"2026-04-07 15:13:40","editor_content":"# Session Log - 2026-04-07\n\n## Summary\n\nThis document records the work completed on `2026-04-07` for the `customerdb` system across:\n\n- frontend / front desk\n- webui / management\n- backend modules\n- reporting\n- signatures / Topaz\n- Square Terminal\n- documents editor / theming\n\nIt is meant to be the end-of-day handoff and operator reference used before quitting for the day.\n\n---\n\n## Main Outcomes\n\nThe main work completed today focused on:\n\n1. stabilizing visit save, payment, and signature behavior\n2. improving printed reports and work orders\n3. improving visit activity logging and history\n4. fixing queue/report visibility by visit instead of collapsing by customer\n5. building and pairing the Square Terminal API flow\n6. continuing backend theme conformity, especially the Documents module\n\n---\n\n## Completed Work\n\n## 1. Front Desk Visit Workflow\n\nFiles touched:\n\n- [visit.html](/C:/code/customerdb/frontend/visit.html)\n- [visit_save.php](/C:/code/customerdb/frontend/api/visit_save.php)\n- [visit_get.php](/C:/code/customerdb/frontend/api/visit_get.php)\n\n### 1.1 Topaz Signature Flow\n\nCompleted:\n\n- Topaz signature capture now auto-saves the visit immediately after a successful capture.\n- Receipt/work order/report actions require a signature for normal visit types.\n- Leaving the visit page prompts/blocks when a required signature is missing.\n- Report generation first saves the visit if the form is dirty, helping ensure the current signature is included.\n\nWhat this fixes:\n\n- signature captured on screen but not yet saved to the visit\n- report opened too quickly before signature save completed\n- missing signature on receipt/PDF after successful Topaz signing\n\n### 1.2 Visit Activity Log\n\nCompleted:\n\n- Added per-visit activity logging to the visit save pipeline.\n- Added display of activity history at the bottom of the visit form.\n- Log now records:\n - timestamp\n - actor\n - activity type\n - changed field names\n - before values\n - after values\n- Friendly field labels were added for log display.\n\nExamples of friendly labels:\n\n- Pickup Date\n- Deposit Amount\n- Amount Paid\n- Payment Method\n- Tip Amount\n- Total Before Tax\n- Total After Tax\n\n### 1.3 Square Metadata on Visit\n\nCompleted:\n\n- Added Square metadata capture on the visit form and save pipeline:\n - Square payment ID\n - Square receipt / confirmation number\n - card brand\n - last 4\n - checkout ID\n - Square tip amount\n - Square total amount\n- Added a visible `Square Reference` area on the visit page.\n- Added Square payment activity log entries.\n\n### 1.4 Payment and Save Logic\n\nCompleted:\n\n- Removed the redundant `No Tax (order)` checkbox from `Payment & Status`.\n- `Picked Up` logic was previously tightened so visit type should drive status rather than auto-forcing pickup.\n- Added a payment line log under `Payment & Status`.\n- Visit save now logs changes and payment events more clearly.\n\nKnown caution:\n\n- Continue live testing around edge-case payment edits involving deposits, split payments, and later pickups.\n\n---\n\n## 2. Printed Reports and Customer-Facing Reports\n\nFiles touched:\n\n- [receipt.html](/C:/code/customerdb/frontend/receipt.html)\n- [work_order.html](/C:/code/customerdb/frontend/work_order.html)\n- [customer_display.html](/C:/code/customerdb/frontend/customer_display.html)\n\n### 2.1 Receipt\n\nCompleted:\n\n- Added `Pickup Time`.\n- Added `Status`.\n- Receipt now uses normalized visit status, such as:\n - Drop Off\n - Pick Up\n - Canceled\n - No Show\n- Receipt shows pickup date/time more clearly.\n\n### 2.2 Customer Display\n\nCompleted:\n\n- Added `Pickup Time` to the customer-facing display.\n\n### 2.3 Work Order\n\nCompleted:\n\n- Added:\n - Pickup Time\n - Status\n - Total Before Tax\n - Tax\n - Total After Tax\n - Deposit\n - Balance\n- Explicit placeholders such as `$0.00` are shown when values are absent so staff can tell the difference between blank and zero.\n- Added `Visit History` to the work order.\n- Then refined `Visit History` to be concise and single-line only so the work order stays print-friendly.\n\nCurrent work-order history behavior:\n\n- only shows status changes and payment-type events\n- examples:\n - `Status Changed: Drop Off to Pick Up`\n - `Payment: Deposit · Cash · 40.00`\n - `Square Payment 85.00 · Confirmation # 123456`\n\nReason for this change:\n\n- the earlier detailed before/after layout was too large for the print page\n\n---\n\n## 3. Queue / Reports Fixes\n\nFiles touched:\n\n- [customer_data.php](/C:/code/customerdb/frontend/api/customer_data.php)\n- [customer_data.php](/C:/code/customerdb/webui/api/customer_data.php)\n\n### 3.1 Today / Ready Queue Logic\n\nCompleted:\n\n- Fixed queue grouping so operational lists now behave per visit rather than collapsing multiple same-customer visits into one row.\n- Fixed display names so queue views use `visits.customer_name_snapshot` when appropriate.\n\nThis fixed real cases where these visits were being merged or displayed incorrectly:\n\n- Peter Dixon\n- Kefa Dixon\n- Angela Dixon\n- Me Myexperttailor\n\n### 3.2 Dashboard and Ella Side Consistency\n\nCompleted:\n\n- `dashboard.floridaalterations.com/reports.html?type=ready` now shows separate visits correctly.\n- `ella.floridaalterations.com/reports.html?type=today` now mirrors the same corrected behavior.\n\n---\n\n## 4. Square Terminal Integration\n\nFiles touched:\n\n- [square_terminal.php](/C:/code/customerdb/frontend/api/square_terminal.php)\n- [square_terminal.php](/C:/code/customerdb/backend/square_terminal.php)\n- [square_terminal_service.php](/C:/code/customerdb/backend/square_terminal_service.php)\n- [settings.html](/C:/code/customerdb/webui/settings.html)\n- [settings_get.php](/C:/code/customerdb/webui/api/settings_get.php)\n- [settings_save.php](/C:/code/customerdb/webui/api/settings_save.php)\n\n### 4.1 What Was Built\n\nCompleted:\n\n- built a backend Square Terminal pairing page/module\n- added support for:\n - create device code\n - refresh code status\n - sync devices\n - store paired devices in MariaDB\n- added manual terminal device ID / label support in settings\n\nTables added earlier in this work:\n\n- `backend_square_terminal_code`\n- `backend_square_terminal_device`\n\n### 4.2 Pairing Result\n\nCompleted:\n\n- real terminal pairing succeeded\n- paired device returned by Square:\n - `device:343CS149B6001234`\n- terminal name:\n - `Ella`\n- device status:\n - `AVAILABLE`\n\n### 4.3 Visit Integration\n\nCompleted:\n\n- visit page can now talk to Square Terminal API using the configured device\n- checkout completion returns:\n - payment ID\n - base amount\n - total amount\n - tip amount\n - card brand\n - last 4\n - receipt number / confirmation number\n\nKnown caution:\n\n- continue live validation that tip handling exactly matches Square on every real terminal transaction\n\n---\n\n## 5. Backend Documents Module\n\nFiles touched:\n\n- [documents.php](/C:/code/customerdb/backend/documents.php)\n\n### 5.1 Functional State\n\nThe Documents backend already supported:\n\n- markdown editing\n- upload\n- document library\n- history / versions\n- restore version\n\n### 5.2 Theme / Color Work Completed Today\n\nCompleted:\n\n- extensive theme cleanup for Documents page\n- fixed dark/black areas in:\n - editor panel\n - library panel\n - upload panel\n - history panel\n - version list area\n - toolbar save button\n - save document button\n- aligned notices, buttons, inputs, editor chrome, headings, and versions to the backend theme\n\nFinal problem areas specifically addressed today:\n\n- `.doc-panel-head`\n- `.doc-panel-head-left`\n- `.doc-bottom-panel`\n- `.doc-lib-body`\n- `#versionList`\n- toolbar `Save`\n- form `Save Document`\n\nStatus at end of day:\n\n- user confirmed it is much better after the final passes\n\n---\n\n## 6. Work Order / Activity History Printing\n\nFiles touched:\n\n- [work_order.html](/C:/code/customerdb/frontend/work_order.html)\n\nCompleted:\n\n- condensed the history so it fits better on one page\n- removed verbose multi-row history in favor of concise operational lines\n\nUse case:\n\n- person working the garment can see what happened on the ticket without the sheet becoming too large to print\n\n---\n\n## How To Use - Daily Workflow\n\n## Morning\n\n1. Run Setmore import.\n2. Open today’s clients / ready queues and review all current visits.\n3. Open visits as needed and verify:\n - visit type\n - payment/deposit values\n - signature status\n - report status\n4. If using Square Terminal:\n - ensure the paired device is still available in backend Square Terminal page\n - run one small test only if needed\n\n## During the Day\n\n1. Open visit.\n2. Update alteration items, visit type, payment, or deposit as needed.\n3. Capture signature with Topaz.\n4. The visit should auto-save immediately after Topaz capture.\n5. Print/open:\n - receipt\n - customer display\n - work order\n6. Review activity log at bottom of visit if something looks off.\n\n## End of Day\n\n1. Review reports and operational queues.\n2. Check printed work orders / receipts for correct status and payment details.\n3. Review Documents page theme/behavior only if that module was used.\n4. If Square was used, confirm confirmation numbers and payment activity are present in the visit log.\n\n---\n\n## How To Test\n\n## A. Topaz / Report Test\n\nUse a real visit:\n\n1. Open visit.\n2. Capture signature with Topaz.\n3. Confirm the page auto-saves.\n4. Open receipt.\n5. Confirm signature is already present.\n\nExpected:\n\n- no extra manual save needed after successful Topaz signature\n\n## B. Visit Activity Log Test\n\n1. Open visit.\n2. Change:\n - pickup date\n - payment amount\n - visit type\n3. Save.\n4. Review bottom `Visit Activity Log`.\n\nExpected:\n\n- friendlier field labels\n- before/after values visible\n\n## C. Work Order Test\n\n1. Open work order.\n2. Verify it shows:\n - pickup date and time\n - status\n - total before tax\n - tax\n - total after tax\n - deposit\n - balance\n3. Check `Visit History`.\n\nExpected:\n\n- only concise status/payment entries\n- should be more likely to fit on one page\n\n## D. Queue Test\n\n1. Open dashboard ready page.\n2. Open Ella today page.\n3. Confirm each visit is shown separately.\n\nExpected:\n\n- no merging of same-customer visits\n\n## E. Square Terminal Test\n\n1. Open backend Square Terminal page.\n2. Confirm device status is available.\n3. Open a visit.\n4. Send amount to terminal.\n5. Complete payment.\n6. Verify visit page shows:\n - Square confirmation number\n - card info\n - payment metadata\n\nExpected:\n\n- payment returns to visit with matching Square identifiers\n\n## F. Documents Theme Test\n\n1. Open Documents page.\n2. Check:\n - editor header\n - library panel\n - upload panel\n - history panel\n - version list\n - save buttons\n\nExpected:\n\n- no black panels/buttons\n- same backend color family as other backend pages\n\n---\n\n## What Was Verified\n\nVerified during implementation:\n\n- local PHP lint on changed PHP files\n- remote PHP lint on deployed files\n- multiple live pages returning 200 during deployment\n- real Square Terminal pairing status reached `PAIRED`\n- Square device became available to the app\n\nUser-confirmed / observed improvements:\n\n- Documents page looked much better after later theme passes\n\n---\n\n## Open Items / Follow-Up\n\nThese are the main items still worth continued live testing, not necessarily known broken items:\n\n1. Square tip logic in real-world transactions\n2. payment edge cases involving deposit, later balance payment, and visit type changes\n3. printed report fit on one page for very large work orders\n4. any remaining Documents CSS drift if another black element appears\n\n---\n\n## Recommended First Tests Tomorrow\n\n1. Test one real visit from start to finish:\n - edit visit\n - Topaz sign\n - auto-save\n - receipt\n - work order\n2. Test one real Square Terminal payment.\n3. Check today/ready queue counts on both dashboard and Ella sides.\n4. Open Documents and verify the final themed state in normal browser and incognito.\n\n---\n\n## Key Pages\n\nFrontend:\n\n- `https://dashboard.floridaalterations.com/visit.html?visit_id=...&customer_id=...`\n- `https://dashboard.floridaalterations.com/receipt.html?visit_id=...`\n- `https://dashboard.floridaalterations.com/work_order.html?visit_id=...`\n- `https://dashboard.floridaalterations.com/reports.html?type=ready`\n\nManagement / Ella:\n\n- `https://ella.floridaalterations.com/reports.html?type=today`\n- `https://ella.floridaalterations.com/backend/documents.php`\n- `https://ella.floridaalterations.com/backend/square_terminal.php`\n\n---\n\n## Final Note\n\nToday’s work pushed the system closer to dependable daily operations in three major areas:\n\n- the visit/signature/report flow\n- the per-visit operational queue/report logic\n- the Square Terminal integration path\n\nThe biggest operational win of the day is that Topaz signature capture now writes back immediately, which should reduce missed signatures on receipts and reports.\n","is_text_editable":1,"can_edit_inline":1}
{"document_type":"upload"}
{"backend_document_id":"10","document_type":"upload","title":"Claude 04-07-2026","slug":"claude-04-07-2026","summary_text":"Claude 04-07-2026 fixing backend Document areas","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-07.md","stored_name":"20260407-190624-56f0e818.md","mime_type":"application/octet-stream","file_size_bytes":"21112","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260407-190624-56f0e818.md","is_deleted":"0","created_at":"2026-04-07 15:06:24","updated_at":"2026-04-07 15:06:24","editor_content":"# Session Log — 2026-04-07\n\nAll work completed in this session on the CustomerDB backend application.\n\n---\n\n## Summary of New Modules and Changes\n\n| Area | File(s) | Status |\n|------|---------|--------|\n| Bills Manager | `backend/bills_service.php`, `backend/bills.php` | New |\n| Next-Day Email Job | `backend/jobs/send_nextday_customer_emails.php` | New |\n| Cron Manager UI | `backend/cron_manager.php` | New |\n| Error Log UI | `backend/error_log.php` | New |\n| Change Log UI | `backend/change_log.php` | New |\n| Stocks & Crypto | `backend/stocks_crypto_service.php`, `backend/stocks_crypto.php` | New |\n| Backend Home | `backend/index.php` | Modified |\n| Documents Editor | `backend/documents.php` | Full rewrite |\n| Documents Service | `backend/document_service.php` | Bug fix |\n\n---\n\n## 1. Bills Manager\n\n### Files\n- `backend/bills_service.php` — 356 lines, service layer\n- `backend/bills.php` — 972 lines, UI page\n\n### Purpose\nReplaces a spreadsheet (`newbills2026.xlsx`) with a web-based bill payment tracker.\nTracks bank account opening balances, monthly bill payments, and computes running bank\nbalances in the same layout as the Excel spreadsheet.\n\n### Database Tables (auto-created on first load)\n\n**`bills_account`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_account_id` | INT PK AUTO | |\n| `name` | VARCHAR(120) | Bank name |\n| `sort_order` | INT | Display order |\n\n**`bills_item`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_item_id` | INT PK AUTO | |\n| `label` | VARCHAR(200) | Bill name |\n| `default_amount` | DECIMAL(12,2) | Pre-fills the Due column |\n| `default_account_id` | INT | Default bank to debit |\n| `notes` | TEXT | Instructions / URLs |\n| `sort_order` | INT | Row order |\n| `is_deleted` | TINYINT | Soft delete |\n\n**`bills_entry`**\nMonthly bill payment rows.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_entry_id` | INT PK AUTO | |\n| `bills_item_id` | INT | FK to `bills_item` |\n| `year`, `month` | INT | Billing period |\n| `amount_due` | DECIMAL(12,2) | Amount billed |\n| `amount_paid` | DECIMAL(12,2) | Amount actually paid |\n| `account_id` | INT | Bank used to pay |\n\n**`bills_balance`**\nOpening bank balances per month.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_balance_id` | INT PK AUTO | |\n| `account_id` | INT | FK to `bills_account` |\n| `year`, `month` | INT | Period |\n| `opening_balance` | DECIMAL(14,2) | Balance at start of month |\n\n### Seed Data\nOn first load (empty table), the following are auto-seeded from the original Excel file:\n\n**Accounts:** USAA, MIDFLORIDA, Centenial, CapitalOne\n\n**Bills (11 items):** MidFlorida Mortgage, MidFlorida Credit Card, USAA Credit Card,\nCentenial Bank, Electricity, Waste Management, Comcast/Xfinity, Insurance, Water,\nCapitalOne #1, CapitalOne #2\n\n### Service Functions\n\n```php\napp_install_bills_schema(mysqli $conn): void\napp_bills_seed_defaults(mysqli $conn): void\napp_bills_accounts(mysqli $conn): array\napp_bills_all_accounts(mysqli $conn): array\napp_bills_items(mysqli $conn): array\napp_bills_balances(mysqli $conn, int $year, int $month): array // map: account_id → float\napp_bills_entries(mysqli $conn, int $year, int $month): array // map: item_id → [due, paid, account_id]\napp_bills_active_months(mysqli $conn, int $year): array\napp_bills_save_balance(mysqli $conn, int $year, int $month, int $accountId, float $balance): void\napp_bills_save_entry(mysqli $conn, int $year, int $month, int $itemId, ?float $due, ?float $paid, ?int $accountId): void\napp_bills_save_account(mysqli $conn, array $data): int\napp_bills_delete_account(mysqli $conn, int $id): void\napp_bills_save_item(mysqli $conn, array $data): int\napp_bills_delete_item(mysqli $conn, int $id): void\napp_bills_reorder_item(mysqli $conn, int $id, string $dir): void\napp_bills_copy_month(mysqli $conn, int $fromYear, int $fromMonth, int $toYear, int $toMonth): int\n```\n\n### UI Features (`bills.php`)\n- Month/year navigation with ◀ / ▶ buttons and active-month pills\n- **Bank Balances section** — editable opening balance per account, inline Save button\n- **Bills table** — spreadsheet-style rows with:\n - Amount Due (editable, pre-filled from default)\n - Amount Paid (editable, AJAX auto-save after 600 ms debounce)\n - Account selector (which bank pays this bill)\n - Balance After column (computed client-side in real time)\n - Up/Down sort buttons\n - Edit and Delete per row\n- **Sticky running totals sidebar** — shows each bank's current balance as bills are entered\n- **Copy from previous month** — modal to copy all entries from a prior month to the current one\n- **Add Bill / Edit Bill modals**\n- **Add Account / Edit Account modals**\n- All saves use POST/Redirect/GET; bill entry changes auto-save via `fetch()` AJAX\n\n### Running Total Calculation\nJavaScript function `recalcTotals()` walks bills in DOM order, maintains a\n`bal[accountId]` map starting from opening balances, subtracts `amount_paid` from the\nselected account for each bill, and updates the \"Balance After\" column and sidebar panel\nin real time — matching the Excel spreadsheet formula logic.\n\n---\n\n## 2. Next-Day Customer Email Job\n\n### File\n- `backend/jobs/send_nextday_customer_emails.php` — 158 lines\n\n### Purpose\nPHP CLI translation of the VBA macro `RunNextDayCustomerEmails()`.\nQueries `setmore_appointments` for appointments scheduled for tomorrow\n(status ≠ Cancelled) and sends a reminder email to each customer.\n\n### Usage\n```bash\n# Normal run (sends real emails)\nphp backend/jobs/send_nextday_customer_emails.php\n\n# Dry run — prints what would be sent, no emails sent\nphp backend/jobs/send_nextday_customer_emails.php --dry-run\n\n# Override date (for testing a specific date)\nphp backend/jobs/send_nextday_customer_emails.php --date=2026-04-09\n```\n\n### Recommended Cron Schedule\n```\n0 20 * * * php /path/to/backend/jobs/send_nextday_customer_emails.php\n```\nRuns at 8:00 PM every evening, sends reminders for the following day's appointments.\n\n### Email Content\n- Subject: `Reminder: Your appointment at Ella's Alterations tomorrow`\n- Body matches the original VBA email with full address, phone, 5-line award\n signature block, and a plain-text fallback\n- Uses `app_send_mail()` / PHPMailer\n\n---\n\n## 3. Cron Manager UI\n\n### File\n- `backend/cron_manager.php` — 482 lines\n\n### Purpose\nWeb UI for managing scheduled background jobs. Stores job definitions in the database,\nallows toggling on/off, running immediately, editing schedules, and exporting to\n`/etc/cron.d/` format.\n\n### Database Table (auto-created)\n\n**`cron_job`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `cron_job_id` | INT PK AUTO | |\n| `label` | VARCHAR(120) | Human name |\n| `description` | TEXT | What the job does |\n| `schedule` | VARCHAR(100) | Cron expression e.g. `0 20 * * *` |\n| `command` | VARCHAR(500) | Shell command to run |\n| `enabled` | TINYINT | 0 = disabled, 1 = enabled |\n| `last_run_at` | DATETIME | Updated on \"Run Now\" |\n| `last_output` | TEXT | Captured stdout/stderr |\n| `sort_order` | INT | Display order |\n\n### Seeded Jobs (on first install)\n1. **Next-Day Customer Emails** — `0 20 * * *` — `php backend/jobs/send_nextday_customer_emails.php`\n2. **Morning Jobs** — `0 7 * * *` — `php backend/jobs/morning_jobs.php`\n3. **Nightly Reports** — `0 23 * * *` — `php backend/jobs/nightly_reports.php`\n4. **DB Backup** — `0 2 * * *` — `bash backend/bin/db_backup.sh`\n5. **Daily Cleanup** — `30 3 * * *` — `php backend/jobs/daily_cleanup.php`\n6. **Add Name Records Auto Delete** — `0 4 * * *` — `php backend/jobs/cleanup_name_records.php`\n\nNew jobs are added idempotently (checked by label before inserting) so existing\ninstallations are not re-seeded.\n\n### UI Features\n- Table listing all jobs with schedule, enabled toggle, last run time\n- **Toggle** — flip enabled/disabled immediately (POST/redirect)\n- **▶ Run Now** — executes the command via `exec()`, captures output, stores to DB, shows in modal\n- **Add / Edit** — form with label, description, command, schedule, enabled flag\n- **Schedule presets** — quick-select buttons (hourly, daily at common times, weekly, monthly)\n- `cron_describe(string $expr): string` — converts `0 20 * * *` → \"Daily at 20:00\"\n- **Export Crontab** — writes `/tmp/customerdb_crontab_export.txt` in `/etc/cron.d/` format\n\n---\n\n## 4. Error Log UI\n\n### File\n- `backend/error_log.php` — 262 lines\n\n### Purpose\nDedicated UI for browsing `backend_error_log` records with filtering, pagination,\nand per-entry management.\n\n### Features\n- **Stats bar** — Total errors, Today's errors, Latest error timestamp\n- **Filter bar** — Search (message/context), Source dropdown, Severity dropdown\n- **Pagination** — 50 per page with smart ellipsis navigator\n- **Per-row delete** — removes individual log entries\n- **Clear All button** — truncates the entire log (with confirmation)\n- JSON context blocks displayed in `<details>` collapsibles with dark monospace styling\n- URL: `/backend/error_log.php`\n\n---\n\n## 5. Change Log UI\n\n### File\n- `backend/change_log.php` — 248 lines\n\n### Purpose\nDedicated UI for browsing `backend_change_log` records — audit trail of all\ndata changes across the application.\n\n### Features\n- **Filter bar** — Search, Area, Action type, Entity type dropdowns\n- **Pagination** — smart ellipsis navigator\n- **Per-row detail panels:**\n - Before / After / Context — collapsed JSON blocks\n - Changed Data — open by default, shows the changed fields summary\n- Action badges color-coded by type (create, update, delete)\n- URL: `/backend/change_log.php`\n\n---\n\n## 6. Stocks & Crypto Module\n\n### Files\n- `backend/stocks_crypto_service.php` — 269 lines, service layer\n- `backend/stocks_crypto.php` — 667 lines, UI page\n\n### Purpose\nTrack a personal investment portfolio — individual stocks, ETFs, precious metals,\nand crypto. Supports live price fetching, manual daily price entry, monthly crypto\nbuy tracking, and portfolio gain/loss analysis.\n\n### Database Tables (auto-created)\n\n**`stocks_asset`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `stocks_asset_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | e.g. `AAPL`, `BTC`, `GLD` |\n| `name` | VARCHAR(120) | Display name |\n| `asset_type` | ENUM | `stock`, `etf`, `crypto`, `metal`, `other` |\n| `shares` | DECIMAL(18,8) | Quantity owned |\n| `cost_basis_total` | DECIMAL(14,6) | Total purchase cost |\n| `is_active` | TINYINT | Soft hide |\n\n**`stocks_price`**\nDaily price records.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `stocks_price_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | |\n| `price_date` | DATE | |\n| `price` | DECIMAL(14,6) | |\n| `source` | VARCHAR(40) | `manual` or `yahoo` |\n\nUnique constraint on `(ticker, price_date)` — upserts with `ON DUPLICATE KEY UPDATE`.\n\n**`crypto_monthly_buy`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `crypto_monthly_buy_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | |\n| `year`, `month` | INT | |\n| `quantity` | DECIMAL(18,8) | Crypto units purchased |\n| `amount_usd` | DECIMAL(14,2) | USD spent |\n| `notes` | VARCHAR(255) | |\n\n### Service Functions\n\n```php\napp_install_stocks_schema(mysqli $conn): void\napp_stocks_assets(mysqli $conn, string $type = ''): array\napp_stocks_save_asset(mysqli $conn, array $data): int\napp_stocks_delete_asset(mysqli $conn, int $id): void\napp_stocks_save_price(mysqli $conn, string $ticker, string $date, float $price, string $source = 'manual'): void\napp_stocks_save_prices_bulk(mysqli $conn, string $date, array $prices): int\napp_stocks_latest_prices(mysqli $conn): array // map: ticker → [price, date]\napp_stocks_price_history(mysqli $conn, string $ticker, int $limit = 90): array\napp_stocks_fetch_live_price(string $ticker): ?float // Yahoo Finance API, 8s timeout\napp_stocks_fetch_all_live(mysqli $conn): array // fetches all active tickers\napp_crypto_monthly_buys(mysqli $conn, int $year, int $month = 0): array\napp_crypto_save_monthly_buy(mysqli $conn, array $data): void\napp_crypto_delete_monthly_buy(mysqli $conn, int $id): void\napp_stocks_dashboard(mysqli $conn): array // cost_basis, current_value, gain_loss, gain_pct per asset\napp_stocks_stats(mysqli $conn): array // total portfolio stats\n```\n\n### Live Price Source\nYahoo Finance unofficial API:\n```\nhttps://query1.finance.yahoo.com/v8/finance/chart/{TICKER}?interval=1d&range=1d\n```\n8-second timeout, reads `.chart.result[0].meta.regularMarketPrice`.\n\n### UI Tabs (`stocks_crypto.php`)\n\n1. **Portfolio** — dashboard table with ticker, shares, cost basis, current value,\n gain/loss, gain %, latest price date. Stats bar shows total value, total cost,\n total gain/loss. \"Fetch Live Prices\" button calls Yahoo Finance for all tickers.\n\n2. **Enter Prices** — manual daily price entry grid for all active assets,\n date picker (defaults to today), bulk save.\n\n3. **Crypto Buys** — monthly crypto purchase log. Month/year selector, table of\n ticker/quantity/USD/notes rows, add and delete per entry.\n\n4. **Add Asset** — form to add a new stock, ETF, crypto, metal, or other asset.\n\n### Auto-Refresh\nA checkbox above the stats bar auto-refreshes the Portfolio tab every 10 seconds.\nState persists in `localStorage` key `sc_auto_refresh`. On reload, strips `?ok=`\nand `?warn=` query params to prevent the notice from repeating every 10 seconds.\n\n---\n\n## 7. Backend Home — New Cards (`index.php`)\n\nSix new cards added to the backend home dashboard:\n\n| Card | Link | Secondary Action |\n|------|------|-----------------|\n| Customer Totals | `/backend/customer_totals.php` | — |\n| Ambient Weather | `/backend/ambient_weather.php` | — |\n| Bills Manager | `/backend/bills.php` | — |\n| Stocks & Crypto | `/backend/stocks_crypto.php` | — |\n| Cron Manager | `/backend/cron_manager.php` | — |\n| Error Log | `/backend/error_log.php` | Change Log |\n\n---\n\n## 8. Documents Editor — Full Rewrite (`documents.php`)\n\n### Problem\nGitHub Copilot introduced several bugs into the previous version:\n- `docLoad()` had mismatched closing braces, breaking the entire function\n- The document library was constrained to ~40% page width inside a grid column\n- The editor was a plain `<textarea>` with no formatting support\n\n### Solution\nComplete rewrite of all HTML, CSS, and JavaScript. PHP POST handlers were kept\nidentical — only the front-end was replaced.\n\n### Editor: EasyMDE\nReplaced the plain textarea with **EasyMDE** (Easy Markdown Editor) via CDN:\n```html\n<link rel=\"stylesheet\" href=\"https://unpkg.com/easymde/dist/easymde.min.css\">\n<script src=\"https://unpkg.com/easymde/dist/easymde.min.js\"></script>\n```\n\nEasyMDE wraps the `<textarea id=\"content_markdown\">` and syncs content on form submit.\n`ta.value = easyMDE.value()` is called explicitly in `submitDoc()` before `form.submit()`.\n\n### Toolbar Buttons\nSave (custom gradient btn) | Bold · Italic · Strikethrough | H1 · H2 · H3 |\nUnordered List · Ordered List · Checklist (custom) | Quote · Code · Table |\nHR · Link · Image URL (custom) | Undo · Redo | Fullscreen | Guide\n\n- **Save** button in toolbar calls `submitDoc()`, same as the Save Document button below\n- **Ctrl+S / Cmd+S** keyboard shortcut via CodeMirror `extraKeys`\n- **Checklist** inserts `- [ ] ` prefix on selected text\n- **Image URL** prompts for URL and alt text, inserts `` syntax\n\n### Preview Toggle\nEasyMDE's built-in `preview` and `side-by-side` buttons were **removed** to prevent\nboth raw markdown and rendered HTML showing simultaneously.\n\nA custom **Preview** button in the panel header replaces them:\n- Uses `marked.js` (CDN) to render the markdown client-side\n- **Preview on**: hides `EasyMDEContainer` and highlight strip; shows `#doc-preview-pane`\n- **Edit on**: hides preview pane; shows EasyMDE and highlight strip; calls `codemirror.refresh()`\n- Loading a document via `docLoad()` automatically returns to edit mode\n- Button state: pill shape, switches between \"👁 Preview\" and \"✏ Edit\" labels\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\"></script>\n```\n\n### `docLoad()` — Clean Rewrite\nOld function had mismatched braces and redundant `editor.value` assignments.\nNew function:\n1. Returns to edit mode if preview is on\n2. Sets heading to \"Loading…\"\n3. Highlights active `<tr>` and `<a>` in library immediately\n4. `fetch('/backend/api/documents.php?backend_document_id=' + id)`\n5. Populates all meta fields (type, title, slug, summary)\n6. Sets `easyMDE.value(content)` from `editor_content` or `content_markdown`\n7. Shows/hides file notice\n8. Calls `renderVersionList(data.versions)`\n9. Updates URL via `history.replaceState()`\n10. Flashes panel border with accent color for 800 ms\n\n### Library Panel — Full Width\nOld layout: library was inside a 2-column grid alongside the editor.\nNew layout:\n```\n[Editor Panel — full width]\n[Library Panel — full width, max-height: 460px scrollable]\n[Upload | Versions — 2-column row]\n```\n\nLive client-side search filters table rows by `textContent` without a page reload.\n\n### Word Count & Highlights\n- Word count and char count update on every keystroke via `codemirror.on('change', ...)`\n- 6 highlight color swatches + a clear option wrap selected text in\n `<span class=\"hl\" style=\"background:COLOR\">...</span>`\n- Clear highlight strips existing `<span class=\"hl\">` wrappers from selected text\n\n### CSS Theme Conformity (visit.html standard)\nAll custom CSS was updated to match the application theme:\n\n| Property | Before | After |\n|----------|--------|-------|\n| Form input border-radius | `10px` | `12px` (matches `--radius-sm`) |\n| Form input padding | `8px 10px` | `11px 14px` |\n| Field label font-size | `0.73rem` + uppercase | `0.82rem`, no forced uppercase |\n| Field label letter-spacing | `0.07em` | `0.04em` |\n| Focus ring | `outline: 2px solid var(--accent)` | `outline: 2px solid rgba(175,77,49,0.3)` |\n| Panel header background | `rgba(244,239,231,0.6)` hardcoded | `var(--panel)` |\n| Toolbar background | `rgba(244,239,231,0.7)` hardcoded | `rgba(255,255,255,0.55)` |\n| Action bar background | `rgba(244,239,231,0.4)` hardcoded | `rgba(255,255,255,0.25)` |\n| Save button | `background: var(--accent)` | `linear-gradient(135deg, var(--accent), var(--accent-deep))` |\n| Action buttons | `border-radius: 7px` | `border-radius: 999px` pill shape |\n| Active library row tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.07)` accent |\n| Type badge (markdown) | `rgba(26,122,154,0.1)` teal | `rgba(175,77,49,0.1)` accent |\n| File notice tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.05)` accent |\n\n---\n\n## 9. `document_service.php` — Parse Error Fix\n\n### Problem\nThe function `app_backend_documents_dir()` had its closing `return $path; }` accidentally\ndisplaced to after `app_document_can_edit_inline()` (lines 147–148), with the rest of the\nfile's functions (`app_libreoffice_convert_to_html`, etc.) nested inside the unclosed\nfunction body. This caused a PHP parse error on every request to any page that\n`require_once`'s `document_service.php`, making `app_document_can_edit_inline()`\nappear undefined.\n\n### Fix\nTwo edits to `document_service.php`:\n\n1. Closed `app_backend_documents_dir()` properly after the `mkdir` block:\n```php\nfunction app_backend_documents_dir(): string\n{\n $path = __DIR__ . '/documents_storage';\n if (!is_dir($path)) {\n mkdir($path, 0775, true);\n }\n return $path; // ← added\n} // ← added\n```\n\n2. Removed the orphaned `return $path; }` that had been displaced to after\n`app_document_can_edit_inline()` at lines 147–148.\n\n---\n\n## Access URLs\n\n| Module | URL |\n|--------|-----|\n| Backend Home | `/backend/` |\n| Bills Manager | `/backend/bills.php` |\n| Stocks & Crypto | `/backend/stocks_crypto.php` |\n| Cron Manager | `/backend/cron_manager.php` |\n| Error Log | `/backend/error_log.php` |\n| Change Log | `/backend/change_log.php` |\n| Documents | `/backend/documents.php` |\n| Documents API | `/backend/api/documents.php` |\n\n---\n\n## Technology Used\n\n| Library | Version | Purpose |\n|---------|---------|---------|\n| EasyMDE | latest (unpkg CDN) | Rich markdown editor |\n| marked.js | latest (jsdelivr CDN) | Client-side markdown → HTML rendering |\n| Yahoo Finance API | v8 (unofficial) | Live stock/ETF price fetching |\n| PHPMailer | (existing) | Email sending via `app_send_mail()` |\n\n---\n\n## Coding Conventions (consistent with existing codebase)\n\n- `declare(strict_types=1)` in all PHP files\n- `app_db()` for database connections, `mysqli` throughout\n- `app_query_all()`, `app_query_one()`, `app_h()`, `app_send_json()` helpers\n- `app_log_backend_exception()` for error logging\n- POST/Redirect/GET for all form submissions\n- `ON DUPLICATE KEY UPDATE` for safe upserts\n- `app_backend_render_header()` / `app_backend_render_footer()` for page chrome\n- CSS custom properties: `--accent`, `--panel`, `--line`, `--text`, `--muted`, `--shadow`\n- All new tables use `CREATE TABLE IF NOT EXISTS` (idempotent, no separate migration needed)\n","is_text_editable":1,"can_edit_inline":1}
{"backend_document_id":"10","document_type":"upload","title":"Claude 04-07-2026","slug":"claude-04-07-2026","summary_text":"Claude 04-07-2026 fixing backend Document areas","content_markdown":"# Session Log — 2026-04-07\r\n\r\nAll work completed in this session on the CustomerDB backend application.\r\n\r\n---\r\n\r\n## Summary of New Modules and Changes\r\n\r\n| Area | File(s) | Status |\r\n|------|---------|--------|\r\n| Bills Manager | `backend/bills_service.php`, `backend/bills.php` | New |\r\n| Next-Day Email Job | `backend/jobs/send_nextday_customer_emails.php` | New |\r\n| Cron Manager UI | `backend/cron_manager.php` | New |\r\n| Error Log UI | `backend/error_log.php` | New |\r\n| Change Log UI | `backend/change_log.php` | New |\r\n| Stocks & Crypto | `backend/stocks_crypto_service.php`, `backend/stocks_crypto.php` | New |\r\n| Backend Home | `backend/index.php` | Modified |\r\n| Documents Editor | `backend/documents.php` | Full rewrite |\r\n| Documents Service | `backend/document_service.php` | Bug fix |\r\n\r\n---\r\n\r\n## 1. Bills Manager\r\n\r\n### Files\r\n- `backend/bills_service.php` — 356 lines, service layer\r\n- `backend/bills.php` — 972 lines, UI page\r\n\r\n### Purpose\r\nReplaces a spreadsheet (`newbills2026.xlsx`) with a web-based bill payment tracker.\r\nTracks bank account opening balances, monthly bill payments, and computes running bank\r\nbalances in the same layout as the Excel spreadsheet.\r\n\r\n### Database Tables (auto-created on first load)\r\n\r\n**`bills_account`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_account_id` | INT PK AUTO | |\r\n| `name` | VARCHAR(120) | Bank name |\r\n| `sort_order` | INT | Display order |\r\n\r\n**`bills_item`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_item_id` | INT PK AUTO | |\r\n| `label` | VARCHAR(200) | Bill name |\r\n| `default_amount` | DECIMAL(12,2) | Pre-fills the Due column |\r\n| `default_account_id` | INT | Default bank to debit |\r\n| `notes` | TEXT | Instructions / URLs |\r\n| `sort_order` | INT | Row order |\r\n| `is_deleted` | TINYINT | Soft delete |\r\n\r\n**`bills_entry`**\r\nMonthly bill payment rows.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_entry_id` | INT PK AUTO | |\r\n| `bills_item_id` | INT | FK to `bills_item` |\r\n| `year`, `month` | INT | Billing period |\r\n| `amount_due` | DECIMAL(12,2) | Amount billed |\r\n| `amount_paid` | DECIMAL(12,2) | Amount actually paid |\r\n| `account_id` | INT | Bank used to pay |\r\n\r\n**`bills_balance`**\r\nOpening bank balances per month.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_balance_id` | INT PK AUTO | |\r\n| `account_id` | INT | FK to `bills_account` |\r\n| `year`, `month` | INT | Period |\r\n| `opening_balance` | DECIMAL(14,2) | Balance at start of month |\r\n\r\n### Seed Data\r\nOn first load (empty table), the following are auto-seeded from the original Excel file:\r\n\r\n**Accounts:** USAA, MIDFLORIDA, Centenial, CapitalOne\r\n\r\n**Bills (11 items):** MidFlorida Mortgage, MidFlorida Credit Card, USAA Credit Card,\r\nCentenial Bank, Electricity, Waste Management, Comcast/Xfinity, Insurance, Water,\r\nCapitalOne #1, CapitalOne #2\r\n\r\n### Service Functions\r\n\r\n```php\r\napp_install_bills_schema(mysqli $conn): void\r\napp_bills_seed_defaults(mysqli $conn): void\r\napp_bills_accounts(mysqli $conn): array\r\napp_bills_all_accounts(mysqli $conn): array\r\napp_bills_items(mysqli $conn): array\r\napp_bills_balances(mysqli $conn, int $year, int $month): array // map: account_id → float\r\napp_bills_entries(mysqli $conn, int $year, int $month): array // map: item_id → [due, paid, account_id]\r\napp_bills_active_months(mysqli $conn, int $year): array\r\napp_bills_save_balance(mysqli $conn, int $year, int $month, int $accountId, float $balance): void\r\napp_bills_save_entry(mysqli $conn, int $year, int $month, int $itemId, ?float $due, ?float $paid, ?int $accountId): void\r\napp_bills_save_account(mysqli $conn, array $data): int\r\napp_bills_delete_account(mysqli $conn, int $id): void\r\napp_bills_save_item(mysqli $conn, array $data): int\r\napp_bills_delete_item(mysqli $conn, int $id): void\r\napp_bills_reorder_item(mysqli $conn, int $id, string $dir): void\r\napp_bills_copy_month(mysqli $conn, int $fromYear, int $fromMonth, int $toYear, int $toMonth): int\r\n```\r\n\r\n### UI Features (`bills.php`)\r\n- Month/year navigation with ◀ / ▶ buttons and active-month pills\r\n- **Bank Balances section** — editable opening balance per account, inline Save button\r\n- **Bills table** — spreadsheet-style rows with:\r\n - Amount Due (editable, pre-filled from default)\r\n - Amount Paid (editable, AJAX auto-save after 600 ms debounce)\r\n - Account selector (which bank pays this bill)\r\n - Balance After column (computed client-side in real time)\r\n - Up/Down sort buttons\r\n - Edit and Delete per row\r\n- **Sticky running totals sidebar** — shows each bank's current balance as bills are entered\r\n- **Copy from previous month** — modal to copy all entries from a prior month to the current one\r\n- **Add Bill / Edit Bill modals**\r\n- **Add Account / Edit Account modals**\r\n- All saves use POST/Redirect/GET; bill entry changes auto-save via `fetch()` AJAX\r\n\r\n### Running Total Calculation\r\nJavaScript function `recalcTotals()` walks bills in DOM order, maintains a\r\n`bal[accountId]` map starting from opening balances, subtracts `amount_paid` from the\r\nselected account for each bill, and updates the \"Balance After\" column and sidebar panel\r\nin real time — matching the Excel spreadsheet formula logic.\r\n\r\n---\r\n\r\n## 2. Next-Day Customer Email Job\r\n\r\n### File\r\n- `backend/jobs/send_nextday_customer_emails.php` — 158 lines\r\n\r\n### Purpose\r\nPHP CLI translation of the VBA macro `RunNextDayCustomerEmails()`.\r\nQueries `setmore_appointments` for appointments scheduled for tomorrow\r\n(status ≠ Cancelled) and sends a reminder email to each customer.\r\n\r\n### Usage\r\n```bash\r\n# Normal run (sends real emails)\r\nphp backend/jobs/send_nextday_customer_emails.php\r\n\r\n# Dry run — prints what would be sent, no emails sent\r\nphp backend/jobs/send_nextday_customer_emails.php --dry-run\r\n\r\n# Override date (for testing a specific date)\r\nphp backend/jobs/send_nextday_customer_emails.php --date=2026-04-09\r\n```\r\n\r\n### Recommended Cron Schedule\r\n```\r\n0 20 * * * php /path/to/backend/jobs/send_nextday_customer_emails.php\r\n```\r\nRuns at 8:00 PM every evening, sends reminders for the following day's appointments.\r\n\r\n### Email Content\r\n- Subject: `Reminder: Your appointment at Ella's Alterations tomorrow`\r\n- Body matches the original VBA email with full address, phone, 5-line award\r\n signature block, and a plain-text fallback\r\n- Uses `app_send_mail()` / PHPMailer\r\n\r\n---\r\n\r\n## 3. Cron Manager UI\r\n\r\n### File\r\n- `backend/cron_manager.php` — 482 lines\r\n\r\n### Purpose\r\nWeb UI for managing scheduled background jobs. Stores job definitions in the database,\r\nallows toggling on/off, running immediately, editing schedules, and exporting to\r\n`/etc/cron.d/` format.\r\n\r\n### Database Table (auto-created)\r\n\r\n**`cron_job`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `cron_job_id` | INT PK AUTO | |\r\n| `label` | VARCHAR(120) | Human name |\r\n| `description` | TEXT | What the job does |\r\n| `schedule` | VARCHAR(100) | Cron expression e.g. `0 20 * * *` |\r\n| `command` | VARCHAR(500) | Shell command to run |\r\n| `enabled` | TINYINT | 0 = disabled, 1 = enabled |\r\n| `last_run_at` | DATETIME | Updated on \"Run Now\" |\r\n| `last_output` | TEXT | Captured stdout/stderr |\r\n| `sort_order` | INT | Display order |\r\n\r\n### Seeded Jobs (on first install)\r\n1. **Next-Day Customer Emails** — `0 20 * * *` — `php backend/jobs/send_nextday_customer_emails.php`\r\n2. **Morning Jobs** — `0 7 * * *` — `php backend/jobs/morning_jobs.php`\r\n3. **Nightly Reports** — `0 23 * * *` — `php backend/jobs/nightly_reports.php`\r\n4. **DB Backup** — `0 2 * * *` — `bash backend/bin/db_backup.sh`\r\n5. **Daily Cleanup** — `30 3 * * *` — `php backend/jobs/daily_cleanup.php`\r\n6. **Add Name Records Auto Delete** — `0 4 * * *` — `php backend/jobs/cleanup_name_records.php`\r\n\r\nNew jobs are added idempotently (checked by label before inserting) so existing\r\ninstallations are not re-seeded.\r\n\r\n### UI Features\r\n- Table listing all jobs with schedule, enabled toggle, last run time\r\n- **Toggle** — flip enabled/disabled immediately (POST/redirect)\r\n- **▶ Run Now** — executes the command via `exec()`, captures output, stores to DB, shows in modal\r\n- **Add / Edit** — form with label, description, command, schedule, enabled flag\r\n- **Schedule presets** — quick-select buttons (hourly, daily at common times, weekly, monthly)\r\n- `cron_describe(string $expr): string` — converts `0 20 * * *` → \"Daily at 20:00\"\r\n- **Export Crontab** — writes `/tmp/customerdb_crontab_export.txt` in `/etc/cron.d/` format\r\n\r\n---\r\n\r\n## 4. Error Log UI\r\n\r\n### File\r\n- `backend/error_log.php` — 262 lines\r\n\r\n### Purpose\r\nDedicated UI for browsing `backend_error_log` records with filtering, pagination,\r\nand per-entry management.\r\n\r\n### Features\r\n- **Stats bar** — Total errors, Today's errors, Latest error timestamp\r\n- **Filter bar** — Search (message/context), Source dropdown, Severity dropdown\r\n- **Pagination** — 50 per page with smart ellipsis navigator\r\n- **Per-row delete** — removes individual log entries\r\n- **Clear All button** — truncates the entire log (with confirmation)\r\n- JSON context blocks displayed in `<details>` collapsibles with dark monospace styling\r\n- URL: `/backend/error_log.php`\r\n\r\n---\r\n\r\n## 5. Change Log UI\r\n\r\n### File\r\n- `backend/change_log.php` — 248 lines\r\n\r\n### Purpose\r\nDedicated UI for browsing `backend_change_log` records — audit trail of all\r\ndata changes across the application.\r\n\r\n### Features\r\n- **Filter bar** — Search, Area, Action type, Entity type dropdowns\r\n- **Pagination** — smart ellipsis navigator\r\n- **Per-row detail panels:**\r\n - Before / After / Context — collapsed JSON blocks\r\n - Changed Data — open by default, shows the changed fields summary\r\n- Action badges color-coded by type (create, update, delete)\r\n- URL: `/backend/change_log.php`\r\n\r\n---\r\n\r\n## 6. Stocks & Crypto Module\r\n\r\n### Files\r\n- `backend/stocks_crypto_service.php` — 269 lines, service layer\r\n- `backend/stocks_crypto.php` — 667 lines, UI page\r\n\r\n### Purpose\r\nTrack a personal investment portfolio — individual stocks, ETFs, precious metals,\r\nand crypto. Supports live price fetching, manual daily price entry, monthly crypto\r\nbuy tracking, and portfolio gain/loss analysis.\r\n\r\n### Database Tables (auto-created)\r\n\r\n**`stocks_asset`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `stocks_asset_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | e.g. `AAPL`, `BTC`, `GLD` |\r\n| `name` | VARCHAR(120) | Display name |\r\n| `asset_type` | ENUM | `stock`, `etf`, `crypto`, `metal`, `other` |\r\n| `shares` | DECIMAL(18,8) | Quantity owned |\r\n| `cost_basis_total` | DECIMAL(14,6) | Total purchase cost |\r\n| `is_active` | TINYINT | Soft hide |\r\n\r\n**`stocks_price`**\r\nDaily price records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `stocks_price_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | |\r\n| `price_date` | DATE | |\r\n| `price` | DECIMAL(14,6) | |\r\n| `source` | VARCHAR(40) | `manual` or `yahoo` |\r\n\r\nUnique constraint on `(ticker, price_date)` — upserts with `ON DUPLICATE KEY UPDATE`.\r\n\r\n**`crypto_monthly_buy`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `crypto_monthly_buy_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | |\r\n| `year`, `month` | INT | |\r\n| `quantity` | DECIMAL(18,8) | Crypto units purchased |\r\n| `amount_usd` | DECIMAL(14,2) | USD spent |\r\n| `notes` | VARCHAR(255) | |\r\n\r\n### Service Functions\r\n\r\n```php\r\napp_install_stocks_schema(mysqli $conn): void\r\napp_stocks_assets(mysqli $conn, string $type = ''): array\r\napp_stocks_save_asset(mysqli $conn, array $data): int\r\napp_stocks_delete_asset(mysqli $conn, int $id): void\r\napp_stocks_save_price(mysqli $conn, string $ticker, string $date, float $price, string $source = 'manual'): void\r\napp_stocks_save_prices_bulk(mysqli $conn, string $date, array $prices): int\r\napp_stocks_latest_prices(mysqli $conn): array // map: ticker → [price, date]\r\napp_stocks_price_history(mysqli $conn, string $ticker, int $limit = 90): array\r\napp_stocks_fetch_live_price(string $ticker): ?float // Yahoo Finance API, 8s timeout\r\napp_stocks_fetch_all_live(mysqli $conn): array // fetches all active tickers\r\napp_crypto_monthly_buys(mysqli $conn, int $year, int $month = 0): array\r\napp_crypto_save_monthly_buy(mysqli $conn, array $data): void\r\napp_crypto_delete_monthly_buy(mysqli $conn, int $id): void\r\napp_stocks_dashboard(mysqli $conn): array // cost_basis, current_value, gain_loss, gain_pct per asset\r\napp_stocks_stats(mysqli $conn): array // total portfolio stats\r\n```\r\n\r\n### Live Price Source\r\nYahoo Finance unofficial API:\r\n```\r\nhttps://query1.finance.yahoo.com/v8/finance/chart/{TICKER}?interval=1d&range=1d\r\n```\r\n8-second timeout, reads `.chart.result[0].meta.regularMarketPrice`.\r\n\r\n### UI Tabs (`stocks_crypto.php`)\r\n\r\n1. **Portfolio** — dashboard table with ticker, shares, cost basis, current value,\r\n gain/loss, gain %, latest price date. Stats bar shows total value, total cost,\r\n total gain/loss. \"Fetch Live Prices\" button calls Yahoo Finance for all tickers.\r\n\r\n2. **Enter Prices** — manual daily price entry grid for all active assets,\r\n date picker (defaults to today), bulk save.\r\n\r\n3. **Crypto Buys** — monthly crypto purchase log. Month/year selector, table of\r\n ticker/quantity/USD/notes rows, add and delete per entry.\r\n\r\n4. **Add Asset** — form to add a new stock, ETF, crypto, metal, or other asset.\r\n\r\n### Auto-Refresh\r\nA checkbox above the stats bar auto-refreshes the Portfolio tab every 10 seconds.\r\nState persists in `localStorage` key `sc_auto_refresh`. On reload, strips `?ok=`\r\nand `?warn=` query params to prevent the notice from repeating every 10 seconds.\r\n\r\n---\r\n\r\n## 7. Backend Home — New Cards (`index.php`)\r\n\r\nSix new cards added to the backend home dashboard:\r\n\r\n| Card | Link | Secondary Action |\r\n|------|------|-----------------|\r\n| Customer Totals | `/backend/customer_totals.php` | — |\r\n| Ambient Weather | `/backend/ambient_weather.php` | — |\r\n| Bills Manager | `/backend/bills.php` | — |\r\n| Stocks & Crypto | `/backend/stocks_crypto.php` | — |\r\n| Cron Manager | `/backend/cron_manager.php` | — |\r\n| Error Log | `/backend/error_log.php` | Change Log |\r\n\r\n---\r\n\r\n## 8. Documents Editor — Full Rewrite (`documents.php`)\r\n\r\n### Problem\r\nGitHub Copilot introduced several bugs into the previous version:\r\n- `docLoad()` had mismatched closing braces, breaking the entire function\r\n- The document library was constrained to ~40% page width inside a grid column\r\n- The editor was a plain `<textarea>` with no formatting support\r\n\r\n### Solution\r\nComplete rewrite of all HTML, CSS, and JavaScript. PHP POST handlers were kept\r\nidentical — only the front-end was replaced.\r\n\r\n### Editor: EasyMDE\r\nReplaced the plain textarea with **EasyMDE** (Easy Markdown Editor) via CDN:\r\n```html\r\n<link rel=\"stylesheet\" href=\"https://unpkg.com/easymde/dist/easymde.min.css\">\r\n<script src=\"https://unpkg.com/easymde/dist/easymde.min.js\"></script>\r\n```\r\n\r\nEasyMDE wraps the `<textarea id=\"content_markdown\">` and syncs content on form submit.\r\n`ta.value = easyMDE.value()` is called explicitly in `submitDoc()` before `form.submit()`.\r\n\r\n### Toolbar Buttons\r\nSave (custom gradient btn) | Bold · Italic · Strikethrough | H1 · H2 · H3 |\r\nUnordered List · Ordered List · Checklist (custom) | Quote · Code · Table |\r\nHR · Link · Image URL (custom) | Undo · Redo | Fullscreen | Guide\r\n\r\n- **Save** button in toolbar calls `submitDoc()`, same as the Save Document button below\r\n- **Ctrl+S / Cmd+S** keyboard shortcut via CodeMirror `extraKeys`\r\n- **Checklist** inserts `- [ ] ` prefix on selected text\r\n- **Image URL** prompts for URL and alt text, inserts `` syntax\r\n\r\n### Preview Toggle\r\nEasyMDE's built-in `preview` and `side-by-side` buttons were **removed** to prevent\r\nboth raw markdown and rendered HTML showing simultaneously.\r\n\r\nA custom **Preview** button in the panel header replaces them:\r\n- Uses `marked.js` (CDN) to render the markdown client-side\r\n- **Preview on**: hides `EasyMDEContainer` and highlight strip; shows `#doc-preview-pane`\r\n- **Edit on**: hides preview pane; shows EasyMDE and highlight strip; calls `codemirror.refresh()`\r\n- Loading a document via `docLoad()` automatically returns to edit mode\r\n- Button state: pill shape, switches between \"👁 Preview\" and \"✏ Edit\" labels\r\n\r\n```html\r\n<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\"></script>\r\n```\r\n\r\n### `docLoad()` — Clean Rewrite\r\nOld function had mismatched braces and redundant `editor.value` assignments.\r\nNew function:\r\n1. Returns to edit mode if preview is on\r\n2. Sets heading to \"Loading…\"\r\n3. Highlights active `<tr>` and `<a>` in library immediately\r\n4. `fetch('/backend/api/documents.php?backend_document_id=' + id)`\r\n5. Populates all meta fields (type, title, slug, summary)\r\n6. Sets `easyMDE.value(content)` from `editor_content` or `content_markdown`\r\n7. Shows/hides file notice\r\n8. Calls `renderVersionList(data.versions)`\r\n9. Updates URL via `history.replaceState()`\r\n10. Flashes panel border with accent color for 800 ms\r\n\r\n### Library Panel — Full Width\r\nOld layout: library was inside a 2-column grid alongside the editor.\r\nNew layout:\r\n```\r\n[Editor Panel — full width]\r\n[Library Panel — full width, max-height: 460px scrollable]\r\n[Upload | Versions — 2-column row]\r\n```\r\n\r\nLive client-side search filters table rows by `textContent` without a page reload.\r\n\r\n### Word Count & Highlights\r\n- Word count and char count update on every keystroke via `codemirror.on('change', ...)`\r\n- 6 highlight color swatches + a clear option wrap selected text in\r\n `<span class=\"hl\" style=\"background:COLOR\">...</span>`\r\n- Clear highlight strips existing `<span class=\"hl\">` wrappers from selected text\r\n\r\n### CSS Theme Conformity (visit.html standard)\r\nAll custom CSS was updated to match the application theme:\r\n\r\n| Property | Before | After |\r\n|----------|--------|-------|\r\n| Form input border-radius | `10px` | `12px` (matches `--radius-sm`) |\r\n| Form input padding | `8px 10px` | `11px 14px` |\r\n| Field label font-size | `0.73rem` + uppercase | `0.82rem`, no forced uppercase |\r\n| Field label letter-spacing | `0.07em` | `0.04em` |\r\n| Focus ring | `outline: 2px solid var(--accent)` | `outline: 2px solid rgba(175,77,49,0.3)` |\r\n| Panel header background | `rgba(244,239,231,0.6)` hardcoded | `var(--panel)` |\r\n| Toolbar background | `rgba(244,239,231,0.7)` hardcoded | `rgba(255,255,255,0.55)` |\r\n| Action bar background | `rgba(244,239,231,0.4)` hardcoded | `rgba(255,255,255,0.25)` |\r\n| Save button | `background: var(--accent)` | `linear-gradient(135deg, var(--accent), var(--accent-deep))` |\r\n| Action buttons | `border-radius: 7px` | `border-radius: 999px` pill shape |\r\n| Active library row tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.07)` accent |\r\n| Type badge (markdown) | `rgba(26,122,154,0.1)` teal | `rgba(175,77,49,0.1)` accent |\r\n| File notice tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.05)` accent |\r\n\r\n---\r\n\r\n## 9. `document_service.php` — Parse Error Fix\r\n\r\n### Problem\r\nThe function `app_backend_documents_dir()` had its closing `return $path; }` accidentally\r\ndisplaced to after `app_document_can_edit_inline()` (lines 147–148), with the rest of the\r\nfile's functions (`app_libreoffice_convert_to_html`, etc.) nested inside the unclosed\r\nfunction body. This caused a PHP parse error on every request to any page that\r\n`require_once`'s `document_service.php`, making `app_document_can_edit_inline()`\r\nappear undefined.\r\n\r\n### Fix\r\nTwo edits to `document_service.php`:\r\n\r\n1. Closed `app_backend_documents_dir()` properly after the `mkdir` block:\r\n```php\r\nfunction app_backend_documents_dir(): string\r\n{\r\n $path = __DIR__ . '/documents_storage';\r\n if (!is_dir($path)) {\r\n mkdir($path, 0775, true);\r\n }\r\n return $path; // ← added\r\n} // ← added\r\n```\r\n\r\n2. Removed the orphaned `return $path; }` that had been displaced to after\r\n`app_document_can_edit_inline()` at lines 147–148.\r\n\r\n---\r\n\r\n## Access URLs\r\n\r\n| Module | URL |\r\n|--------|-----|\r\n| Backend Home | `/backend/` |\r\n| Bills Manager | `/backend/bills.php` |\r\n| Stocks & Crypto | `/backend/stocks_crypto.php` |\r\n| Cron Manager | `/backend/cron_manager.php` |\r\n| Error Log | `/backend/error_log.php` |\r\n| Change Log | `/backend/change_log.php` |\r\n| Documents | `/backend/documents.php` |\r\n| Documents API | `/backend/api/documents.php` |\r\n\r\n---\r\n\r\n## Technology Used\r\n\r\n| Library | Version | Purpose |\r\n|---------|---------|---------|\r\n| EasyMDE | latest (unpkg CDN) | Rich markdown editor |\r\n| marked.js | latest (jsdelivr CDN) | Client-side markdown → HTML rendering |\r\n| Yahoo Finance API | v8 (unofficial) | Live stock/ETF price fetching |\r\n| PHPMailer | (existing) | Email sending via `app_send_mail()` |\r\n\r\n---\r\n\r\n## Coding Conventions (consistent with existing codebase)\r\n\r\n- `declare(strict_types=1)` in all PHP files\r\n- `app_db()` for database connections, `mysqli` throughout\r\n- `app_query_all()`, `app_query_one()`, `app_h()`, `app_send_json()` helpers\r\n- `app_log_backend_exception()` for error logging\r\n- POST/Redirect/GET for all form submissions\r\n- `ON DUPLICATE KEY UPDATE` for safe upserts\r\n- `app_backend_render_header()` / `app_backend_render_footer()` for page chrome\r\n- CSS custom properties: `--accent`, `--panel`, `--line`, `--text`, `--muted`, `--shadow`\r\n- All new tables use `CREATE TABLE IF NOT EXISTS` (idempotent, no separate migration needed)","content_html":"<h1>Session Log — 2026-04-07</h1>\n<p>All work completed in this session on the CustomerDB backend application.</p>\n<p>---</p>\n<h2>Summary of New Modules and Changes</h2>\n<p>| Area | File(s) | Status |</p>\n<p>|------|---------|--------|</p>\n<p>| Bills Manager | `backend/bills_service.php`, `backend/bills.php` | New |</p>\n<p>| Next-Day Email Job | `backend/jobs/send_nextday_customer_emails.php` | New |</p>\n<p>| Cron Manager UI | `backend/cron_manager.php` | New |</p>\n<p>| Error Log UI | `backend/error_log.php` | New |</p>\n<p>| Change Log UI | `backend/change_log.php` | New |</p>\n<p>| Stocks & Crypto | `backend/stocks_crypto_service.php`, `backend/stocks_crypto.php` | New |</p>\n<p>| Backend Home | `backend/index.php` | Modified |</p>\n<p>| Documents Editor | `backend/documents.php` | Full rewrite |</p>\n<p>| Documents Service | `backend/document_service.php` | Bug fix |</p>\n<p>---</p>\n<h2>1. Bills Manager</h2>\n<h3>Files</h3>\n<ul>\n<li>`backend/bills_service.php` — 356 lines, service layer</li>\n<li>`backend/bills.php` — 972 lines, UI page</li>\n</ul>\n<h3>Purpose</h3>\n<p>Replaces a spreadsheet (`newbills2026.xlsx`) with a web-based bill payment tracker.</p>\n<p>Tracks bank account opening balances, monthly bill payments, and computes running bank</p>\n<p>balances in the same layout as the Excel spreadsheet.</p>\n<h3>Database Tables (auto-created on first load)</h3>\n<p><strong>`bills_account`</strong></p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `bills_account_id` | INT PK AUTO | |</p>\n<p>| `name` | VARCHAR(120) | Bank name |</p>\n<p>| `sort_order` | INT | Display order |</p>\n<p><strong>`bills_item`</strong></p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `bills_item_id` | INT PK AUTO | |</p>\n<p>| `label` | VARCHAR(200) | Bill name |</p>\n<p>| `default_amount` | DECIMAL(12,2) | Pre-fills the Due column |</p>\n<p>| `default_account_id` | INT | Default bank to debit |</p>\n<p>| `notes` | TEXT | Instructions / URLs |</p>\n<p>| `sort_order` | INT | Row order |</p>\n<p>| `is_deleted` | TINYINT | Soft delete |</p>\n<p><strong>`bills_entry`</strong></p>\n<p>Monthly bill payment rows.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `bills_entry_id` | INT PK AUTO | |</p>\n<p>| `bills_item_id` | INT | FK to `bills_item` |</p>\n<p>| `year`, `month` | INT | Billing period |</p>\n<p>| `amount_due` | DECIMAL(12,2) | Amount billed |</p>\n<p>| `amount_paid` | DECIMAL(12,2) | Amount actually paid |</p>\n<p>| `account_id` | INT | Bank used to pay |</p>\n<p><strong>`bills_balance`</strong></p>\n<p>Opening bank balances per month.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `bills_balance_id` | INT PK AUTO | |</p>\n<p>| `account_id` | INT | FK to `bills_account` |</p>\n<p>| `year`, `month` | INT | Period |</p>\n<p>| `opening_balance` | DECIMAL(14,2) | Balance at start of month |</p>\n<h3>Seed Data</h3>\n<p>On first load (empty table), the following are auto-seeded from the original Excel file:</p>\n<p><strong>Accounts:</strong> USAA, MIDFLORIDA, Centenial, CapitalOne</p>\n<p><strong>Bills (11 items):</strong> MidFlorida Mortgage, MidFlorida Credit Card, USAA Credit Card,</p>\n<p>Centenial Bank, Electricity, Waste Management, Comcast/Xfinity, Insurance, Water,</p>\n<p>CapitalOne #1, CapitalOne #2</p>\n<h3>Service Functions</h3>\n<p>```php</p>\n<p>app_install_bills_schema(mysqli $conn): void</p>\n<p>app_bills_seed_defaults(mysqli $conn): void</p>\n<p>app_bills_accounts(mysqli $conn): array</p>\n<p>app_bills_all_accounts(mysqli $conn): array</p>\n<p>app_bills_items(mysqli $conn): array</p>\n<p>app_bills_balances(mysqli $conn, int $year, int $month): array // map: account_id → float</p>\n<p>app_bills_entries(mysqli $conn, int $year, int $month): array // map: item_id → [due, paid, account_id]</p>\n<p>app_bills_active_months(mysqli $conn, int $year): array</p>\n<p>app_bills_save_balance(mysqli $conn, int $year, int $month, int $accountId, float $balance): void</p>\n<p>app_bills_save_entry(mysqli $conn, int $year, int $month, int $itemId, ?float $due, ?float $paid, ?int $accountId): void</p>\n<p>app_bills_save_account(mysqli $conn, array $data): int</p>\n<p>app_bills_delete_account(mysqli $conn, int $id): void</p>\n<p>app_bills_save_item(mysqli $conn, array $data): int</p>\n<p>app_bills_delete_item(mysqli $conn, int $id): void</p>\n<p>app_bills_reorder_item(mysqli $conn, int $id, string $dir): void</p>\n<p>app_bills_copy_month(mysqli $conn, int $fromYear, int $fromMonth, int $toYear, int $toMonth): int</p>\n<p>```</p>\n<h3>UI Features (`bills.php`)</h3>\n<ul>\n<li>Month/year navigation with ◀ / ▶ buttons and active-month pills</li>\n<li><strong>Bank Balances section</strong> — editable opening balance per account, inline Save button</li>\n<li><strong>Bills table</strong> — spreadsheet-style rows with:</li>\n<li>Amount Due (editable, pre-filled from default)</li>\n<li>Amount Paid (editable, AJAX auto-save after 600 ms debounce)</li>\n<li>Account selector (which bank pays this bill)</li>\n<li>Balance After column (computed client-side in real time)</li>\n<li>Up/Down sort buttons</li>\n<li>Edit and Delete per row</li>\n<li><strong>Sticky running totals sidebar</strong> — shows each bank's current balance as bills are entered</li>\n<li><strong>Copy from previous month</strong> — modal to copy all entries from a prior month to the current one</li>\n<li><strong>Add Bill / Edit Bill modals</strong></li>\n<li><strong>Add Account / Edit Account modals</strong></li>\n<li>All saves use POST/Redirect/GET; bill entry changes auto-save via `fetch()` AJAX</li>\n</ul>\n<h3>Running Total Calculation</h3>\n<p>JavaScript function `recalcTotals()` walks bills in DOM order, maintains a</p>\n<p>`bal[accountId]` map starting from opening balances, subtracts `amount_paid` from the</p>\n<p>selected account for each bill, and updates the "Balance After" column and sidebar panel</p>\n<p>in real time — matching the Excel spreadsheet formula logic.</p>\n<p>---</p>\n<h2>2. Next-Day Customer Email Job</h2>\n<h3>File</h3>\n<ul>\n<li>`backend/jobs/send_nextday_customer_emails.php` — 158 lines</li>\n</ul>\n<h3>Purpose</h3>\n<p>PHP CLI translation of the VBA macro `RunNextDayCustomerEmails()`.</p>\n<p>Queries `setmore_appointments` for appointments scheduled for tomorrow</p>\n<p>(status ≠ Cancelled) and sends a reminder email to each customer.</p>\n<h3>Usage</h3>\n<p>```bash</p>\n<h1>Normal run (sends real emails)</h1>\n<p>php backend/jobs/send_nextday_customer_emails.php</p>\n<h1>Dry run — prints what would be sent, no emails sent</h1>\n<p>php backend/jobs/send_nextday_customer_emails.php --dry-run</p>\n<h1>Override date (for testing a specific date)</h1>\n<p>php backend/jobs/send_nextday_customer_emails.php --date=2026-04-09</p>\n<p>```</p>\n<h3>Recommended Cron Schedule</h3>\n<p>```</p>\n<p>0 20 <em> </em> * php /path/to/backend/jobs/send_nextday_customer_emails.php</p>\n<p>```</p>\n<p>Runs at 8:00 PM every evening, sends reminders for the following day's appointments.</p>\n<h3>Email Content</h3>\n<ul>\n<li>Subject: `Reminder: Your appointment at Ella's Alterations tomorrow`</li>\n<li>Body matches the original VBA email with full address, phone, 5-line award</li>\n</ul>\n<p>signature block, and a plain-text fallback</p>\n<ul>\n<li>Uses `app_send_mail()` / PHPMailer</li>\n</ul>\n<p>---</p>\n<h2>3. Cron Manager UI</h2>\n<h3>File</h3>\n<ul>\n<li>`backend/cron_manager.php` — 482 lines</li>\n</ul>\n<h3>Purpose</h3>\n<p>Web UI for managing scheduled background jobs. Stores job definitions in the database,</p>\n<p>allows toggling on/off, running immediately, editing schedules, and exporting to</p>\n<p>`/etc/cron.d/` format.</p>\n<h3>Database Table (auto-created)</h3>\n<p><strong>`cron_job`</strong></p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `cron_job_id` | INT PK AUTO | |</p>\n<p>| `label` | VARCHAR(120) | Human name |</p>\n<p>| `description` | TEXT | What the job does |</p>\n<p>| `schedule` | VARCHAR(100) | Cron expression e.g. `0 20 <em> </em> *` |</p>\n<p>| `command` | VARCHAR(500) | Shell command to run |</p>\n<p>| `enabled` | TINYINT | 0 = disabled, 1 = enabled |</p>\n<p>| `last_run_at` | DATETIME | Updated on "Run Now" |</p>\n<p>| `last_output` | TEXT | Captured stdout/stderr |</p>\n<p>| `sort_order` | INT | Display order |</p>\n<h3>Seeded Jobs (on first install)</h3>\n<p>1. <strong>Next-Day Customer Emails</strong> — `0 20 <em> </em> *` — `php backend/jobs/send_nextday_customer_emails.php`</p>\n<p>2. <strong>Morning Jobs</strong> — `0 7 <em> </em> *` — `php backend/jobs/morning_jobs.php`</p>\n<p>3. <strong>Nightly Reports</strong> — `0 23 <em> </em> *` — `php backend/jobs/nightly_reports.php`</p>\n<p>4. <strong>DB Backup</strong> — `0 2 <em> </em> *` — `bash backend/bin/db_backup.sh`</p>\n<p>5. <strong>Daily Cleanup</strong> — `30 3 <em> </em> *` — `php backend/jobs/daily_cleanup.php`</p>\n<p>6. <strong>Add Name Records Auto Delete</strong> — `0 4 <em> </em> *` — `php backend/jobs/cleanup_name_records.php`</p>\n<p>New jobs are added idempotently (checked by label before inserting) so existing</p>\n<p>installations are not re-seeded.</p>\n<h3>UI Features</h3>\n<ul>\n<li>Table listing all jobs with schedule, enabled toggle, last run time</li>\n<li><strong>Toggle</strong> — flip enabled/disabled immediately (POST/redirect)</li>\n<li><strong>▶ Run Now</strong> — executes the command via `exec()`, captures output, stores to DB, shows in modal</li>\n<li><strong>Add / Edit</strong> — form with label, description, command, schedule, enabled flag</li>\n<li><strong>Schedule presets</strong> — quick-select buttons (hourly, daily at common times, weekly, monthly)</li>\n<li>`cron_describe(string $expr): string` — converts `0 20 <em> </em> *` → "Daily at 20:00"</li>\n<li><strong>Export Crontab</strong> — writes `/tmp/customerdb_crontab_export.txt` in `/etc/cron.d/` format</li>\n</ul>\n<p>---</p>\n<h2>4. Error Log UI</h2>\n<h3>File</h3>\n<ul>\n<li>`backend/error_log.php` — 262 lines</li>\n</ul>\n<h3>Purpose</h3>\n<p>Dedicated UI for browsing `backend_error_log` records with filtering, pagination,</p>\n<p>and per-entry management.</p>\n<h3>Features</h3>\n<ul>\n<li><strong>Stats bar</strong> — Total errors, Today's errors, Latest error timestamp</li>\n<li><strong>Filter bar</strong> — Search (message/context), Source dropdown, Severity dropdown</li>\n<li><strong>Pagination</strong> — 50 per page with smart ellipsis navigator</li>\n<li><strong>Per-row delete</strong> — removes individual log entries</li>\n<li><strong>Clear All button</strong> — truncates the entire log (with confirmation)</li>\n<li>JSON context blocks displayed in `<details>` collapsibles with dark monospace styling</li>\n<li>URL: `/backend/error_log.php`</li>\n</ul>\n<p>---</p>\n<h2>5. Change Log UI</h2>\n<h3>File</h3>\n<ul>\n<li>`backend/change_log.php` — 248 lines</li>\n</ul>\n<h3>Purpose</h3>\n<p>Dedicated UI for browsing `backend_change_log` records — audit trail of all</p>\n<p>data changes across the application.</p>\n<h3>Features</h3>\n<ul>\n<li><strong>Filter bar</strong> — Search, Area, Action type, Entity type dropdowns</li>\n<li><strong>Pagination</strong> — smart ellipsis navigator</li>\n<li><strong>Per-row detail panels:</strong></li>\n<li>Before / After / Context — collapsed JSON blocks</li>\n<li>Changed Data — open by default, shows the changed fields summary</li>\n<li>Action badges color-coded by type (create, update, delete)</li>\n<li>URL: `/backend/change_log.php`</li>\n</ul>\n<p>---</p>\n<h2>6. Stocks & Crypto Module</h2>\n<h3>Files</h3>\n<ul>\n<li>`backend/stocks_crypto_service.php` — 269 lines, service layer</li>\n<li>`backend/stocks_crypto.php` — 667 lines, UI page</li>\n</ul>\n<h3>Purpose</h3>\n<p>Track a personal investment portfolio — individual stocks, ETFs, precious metals,</p>\n<p>and crypto. Supports live price fetching, manual daily price entry, monthly crypto</p>\n<p>buy tracking, and portfolio gain/loss analysis.</p>\n<h3>Database Tables (auto-created)</h3>\n<p><strong>`stocks_asset`</strong></p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `stocks_asset_id` | INT PK AUTO | |</p>\n<p>| `ticker` | VARCHAR(20) | e.g. `AAPL`, `BTC`, `GLD` |</p>\n<p>| `name` | VARCHAR(120) | Display name |</p>\n<p>| `asset_type` | ENUM | `stock`, `etf`, `crypto`, `metal`, `other` |</p>\n<p>| `shares` | DECIMAL(18,8) | Quantity owned |</p>\n<p>| `cost_basis_total` | DECIMAL(14,6) | Total purchase cost |</p>\n<p>| `is_active` | TINYINT | Soft hide |</p>\n<p><strong>`stocks_price`</strong></p>\n<p>Daily price records.</p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `stocks_price_id` | INT PK AUTO | |</p>\n<p>| `ticker` | VARCHAR(20) | |</p>\n<p>| `price_date` | DATE | |</p>\n<p>| `price` | DECIMAL(14,6) | |</p>\n<p>| `source` | VARCHAR(40) | `manual` or `yahoo` |</p>\n<p>Unique constraint on `(ticker, price_date)` — upserts with `ON DUPLICATE KEY UPDATE`.</p>\n<p><strong>`crypto_monthly_buy`</strong></p>\n<p>| Column | Type | Notes |</p>\n<p>|--------|------|-------|</p>\n<p>| `crypto_monthly_buy_id` | INT PK AUTO | |</p>\n<p>| `ticker` | VARCHAR(20) | |</p>\n<p>| `year`, `month` | INT | |</p>\n<p>| `quantity` | DECIMAL(18,8) | Crypto units purchased |</p>\n<p>| `amount_usd` | DECIMAL(14,2) | USD spent |</p>\n<p>| `notes` | VARCHAR(255) | |</p>\n<h3>Service Functions</h3>\n<p>```php</p>\n<p>app_install_stocks_schema(mysqli $conn): void</p>\n<p>app_stocks_assets(mysqli $conn, string $type = ''): array</p>\n<p>app_stocks_save_asset(mysqli $conn, array $data): int</p>\n<p>app_stocks_delete_asset(mysqli $conn, int $id): void</p>\n<p>app_stocks_save_price(mysqli $conn, string $ticker, string $date, float $price, string $source = 'manual'): void</p>\n<p>app_stocks_save_prices_bulk(mysqli $conn, string $date, array $prices): int</p>\n<p>app_stocks_latest_prices(mysqli $conn): array // map: ticker → [price, date]</p>\n<p>app_stocks_price_history(mysqli $conn, string $ticker, int $limit = 90): array</p>\n<p>app_stocks_fetch_live_price(string $ticker): ?float // Yahoo Finance API, 8s timeout</p>\n<p>app_stocks_fetch_all_live(mysqli $conn): array // fetches all active tickers</p>\n<p>app_crypto_monthly_buys(mysqli $conn, int $year, int $month = 0): array</p>\n<p>app_crypto_save_monthly_buy(mysqli $conn, array $data): void</p>\n<p>app_crypto_delete_monthly_buy(mysqli $conn, int $id): void</p>\n<p>app_stocks_dashboard(mysqli $conn): array // cost_basis, current_value, gain_loss, gain_pct per asset</p>\n<p>app_stocks_stats(mysqli $conn): array // total portfolio stats</p>\n<p>```</p>\n<h3>Live Price Source</h3>\n<p>Yahoo Finance unofficial API:</p>\n<p>```</p>\n<p>https://query1.finance.yahoo.com/v8/finance/chart/{TICKER}?interval=1d&range=1d</p>\n<p>```</p>\n<p>8-second timeout, reads `.chart.result[0].meta.regularMarketPrice`.</p>\n<h3>UI Tabs (`stocks_crypto.php`)</h3>\n<p>1. <strong>Portfolio</strong> — dashboard table with ticker, shares, cost basis, current value,</p>\n<p>gain/loss, gain %, latest price date. Stats bar shows total value, total cost,</p>\n<p>total gain/loss. "Fetch Live Prices" button calls Yahoo Finance for all tickers.</p>\n<p>2. <strong>Enter Prices</strong> — manual daily price entry grid for all active assets,</p>\n<p>date picker (defaults to today), bulk save.</p>\n<p>3. <strong>Crypto Buys</strong> — monthly crypto purchase log. Month/year selector, table of</p>\n<p>ticker/quantity/USD/notes rows, add and delete per entry.</p>\n<p>4. <strong>Add Asset</strong> — form to add a new stock, ETF, crypto, metal, or other asset.</p>\n<h3>Auto-Refresh</h3>\n<p>A checkbox above the stats bar auto-refreshes the Portfolio tab every 10 seconds.</p>\n<p>State persists in `localStorage` key `sc_auto_refresh`. On reload, strips `?ok=`</p>\n<p>and `?warn=` query params to prevent the notice from repeating every 10 seconds.</p>\n<p>---</p>\n<h2>7. Backend Home — New Cards (`index.php`)</h2>\n<p>Six new cards added to the backend home dashboard:</p>\n<p>| Card | Link | Secondary Action |</p>\n<p>|------|------|-----------------|</p>\n<p>| Customer Totals | `/backend/customer_totals.php` | — |</p>\n<p>| Ambient Weather | `/backend/ambient_weather.php` | — |</p>\n<p>| Bills Manager | `/backend/bills.php` | — |</p>\n<p>| Stocks & Crypto | `/backend/stocks_crypto.php` | — |</p>\n<p>| Cron Manager | `/backend/cron_manager.php` | — |</p>\n<p>| Error Log | `/backend/error_log.php` | Change Log |</p>\n<p>---</p>\n<h2>8. Documents Editor — Full Rewrite (`documents.php`)</h2>\n<h3>Problem</h3>\n<p>GitHub Copilot introduced several bugs into the previous version:</p>\n<ul>\n<li>`docLoad()` had mismatched closing braces, breaking the entire function</li>\n<li>The document library was constrained to ~40% page width inside a grid column</li>\n<li>The editor was a plain `<textarea>` with no formatting support</li>\n</ul>\n<h3>Solution</h3>\n<p>Complete rewrite of all HTML, CSS, and JavaScript. PHP POST handlers were kept</p>\n<p>identical — only the front-end was replaced.</p>\n<h3>Editor: EasyMDE</h3>\n<p>Replaced the plain textarea with <strong>EasyMDE</strong> (Easy Markdown Editor) via CDN:</p>\n<p>```html</p>\n<p><link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css"></p>\n<p><script src="https://unpkg.com/easymde/dist/easymde.min.js"></script></p>\n<p>```</p>\n<p>EasyMDE wraps the `<textarea id="content_markdown">` and syncs content on form submit.</p>\n<p>`ta.value = easyMDE.value()` is called explicitly in `submitDoc()` before `form.submit()`.</p>\n<h3>Toolbar Buttons</h3>\n<p>Save (custom gradient btn) | Bold · Italic · Strikethrough | H1 · H2 · H3 |</p>\n<p>Unordered List · Ordered List · Checklist (custom) | Quote · Code · Table |</p>\n<p>HR · Link · Image URL (custom) | Undo · Redo | Fullscreen | Guide</p>\n<ul>\n<li><strong>Save</strong> button in toolbar calls `submitDoc()`, same as the Save Document button below</li>\n<li><strong>Ctrl+S / Cmd+S</strong> keyboard shortcut via CodeMirror `extraKeys`</li>\n<li><strong>Checklist</strong> inserts `- [ ] ` prefix on selected text</li>\n<li><strong>Image URL</strong> prompts for URL and alt text, inserts `` syntax</li>\n</ul>\n<h3>Preview Toggle</h3>\n<p>EasyMDE's built-in `preview` and `side-by-side` buttons were <strong>removed</strong> to prevent</p>\n<p>both raw markdown and rendered HTML showing simultaneously.</p>\n<p>A custom <strong>Preview</strong> button in the panel header replaces them:</p>\n<ul>\n<li>Uses `marked.js` (CDN) to render the markdown client-side</li>\n<li><strong>Preview on</strong>: hides `EasyMDEContainer` and highlight strip; shows `#doc-preview-pane`</li>\n<li><strong>Edit on</strong>: hides preview pane; shows EasyMDE and highlight strip; calls `codemirror.refresh()`</li>\n<li>Loading a document via `docLoad()` automatically returns to edit mode</li>\n<li>Button state: pill shape, switches between "👁 Preview" and "✏ Edit" labels</li>\n</ul>\n<p>```html</p>\n<p><script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script></p>\n<p>```</p>\n<h3>`docLoad()` — Clean Rewrite</h3>\n<p>Old function had mismatched braces and redundant `editor.value` assignments.</p>\n<p>New function:</p>\n<p>1. Returns to edit mode if preview is on</p>\n<p>2. Sets heading to "Loading…"</p>\n<p>3. Highlights active `<tr>` and `<a>` in library immediately</p>\n<p>4. `fetch('/backend/api/documents.php?backend_document_id=' + id)`</p>\n<p>5. Populates all meta fields (type, title, slug, summary)</p>\n<p>6. Sets `easyMDE.value(content)` from `editor_content` or `content_markdown`</p>\n<p>7. Shows/hides file notice</p>\n<p>8. Calls `renderVersionList(data.versions)`</p>\n<p>9. Updates URL via `history.replaceState()`</p>\n<p>10. Flashes panel border with accent color for 800 ms</p>\n<h3>Library Panel — Full Width</h3>\n<p>Old layout: library was inside a 2-column grid alongside the editor.</p>\n<p>New layout:</p>\n<p>```</p>\n<p>[Editor Panel — full width]</p>\n<p>[Library Panel — full width, max-height: 460px scrollable]</p>\n<p>[Upload | Versions — 2-column row]</p>\n<p>```</p>\n<p>Live client-side search filters table rows by `textContent` without a page reload.</p>\n<h3>Word Count & Highlights</h3>\n<ul>\n<li>Word count and char count update on every keystroke via `codemirror.on('change', ...)`</li>\n<li>6 highlight color swatches + a clear option wrap selected text in</li>\n</ul>\n<p>`<span class="hl" style="background:COLOR">...</span>`</p>\n<ul>\n<li>Clear highlight strips existing `<span class="hl">` wrappers from selected text</li>\n</ul>\n<h3>CSS Theme Conformity (visit.html standard)</h3>\n<p>All custom CSS was updated to match the application theme:</p>\n<p>| Property | Before | After |</p>\n<p>|----------|--------|-------|</p>\n<p>| Form input border-radius | `10px` | `12px` (matches `--radius-sm`) |</p>\n<p>| Form input padding | `8px 10px` | `11px 14px` |</p>\n<p>| Field label font-size | `0.73rem` + uppercase | `0.82rem`, no forced uppercase |</p>\n<p>| Field label letter-spacing | `0.07em` | `0.04em` |</p>\n<p>| Focus ring | `outline: 2px solid var(--accent)` | `outline: 2px solid rgba(175,77,49,0.3)` |</p>\n<p>| Panel header background | `rgba(244,239,231,0.6)` hardcoded | `var(--panel)` |</p>\n<p>| Toolbar background | `rgba(244,239,231,0.7)` hardcoded | `rgba(255,255,255,0.55)` |</p>\n<p>| Action bar background | `rgba(244,239,231,0.4)` hardcoded | `rgba(255,255,255,0.25)` |</p>\n<p>| Save button | `background: var(--accent)` | `linear-gradient(135deg, var(--accent), var(--accent-deep))` |</p>\n<p>| Action buttons | `border-radius: 7px` | `border-radius: 999px` pill shape |</p>\n<p>| Active library row tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.07)` accent |</p>\n<p>| Type badge (markdown) | `rgba(26,122,154,0.1)` teal | `rgba(175,77,49,0.1)` accent |</p>\n<p>| File notice tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.05)` accent |</p>\n<p>---</p>\n<h2>9. `document_service.php` — Parse Error Fix</h2>\n<h3>Problem</h3>\n<p>The function `app_backend_documents_dir()` had its closing `return $path; }` accidentally</p>\n<p>displaced to after `app_document_can_edit_inline()` (lines 147–148), with the rest of the</p>\n<p>file's functions (`app_libreoffice_convert_to_html`, etc.) nested inside the unclosed</p>\n<p>function body. This caused a PHP parse error on every request to any page that</p>\n<p>`require_once`'s `document_service.php`, making `app_document_can_edit_inline()`</p>\n<p>appear undefined.</p>\n<h3>Fix</h3>\n<p>Two edits to `document_service.php`:</p>\n<p>1. Closed `app_backend_documents_dir()` properly after the `mkdir` block:</p>\n<p>```php</p>\n<p>function app_backend_documents_dir(): string</p>\n<p>{</p>\n<p>$path = __DIR__ . '/documents_storage';</p>\n<p>if (!is_dir($path)) {</p>\n<p>mkdir($path, 0775, true);</p>\n<p>}</p>\n<p>return $path; // ← added</p>\n<p>} // ← added</p>\n<p>```</p>\n<p>2. Removed the orphaned `return $path; }` that had been displaced to after</p>\n<p>`app_document_can_edit_inline()` at lines 147–148.</p>\n<p>---</p>\n<h2>Access URLs</h2>\n<p>| Module | URL |</p>\n<p>|--------|-----|</p>\n<p>| Backend Home | `/backend/` |</p>\n<p>| Bills Manager | `/backend/bills.php` |</p>\n<p>| Stocks & Crypto | `/backend/stocks_crypto.php` |</p>\n<p>| Cron Manager | `/backend/cron_manager.php` |</p>\n<p>| Error Log | `/backend/error_log.php` |</p>\n<p>| Change Log | `/backend/change_log.php` |</p>\n<p>| Documents | `/backend/documents.php` |</p>\n<p>| Documents API | `/backend/api/documents.php` |</p>\n<p>---</p>\n<h2>Technology Used</h2>\n<p>| Library | Version | Purpose |</p>\n<p>|---------|---------|---------|</p>\n<p>| EasyMDE | latest (unpkg CDN) | Rich markdown editor |</p>\n<p>| marked.js | latest (jsdelivr CDN) | Client-side markdown → HTML rendering |</p>\n<p>| Yahoo Finance API | v8 (unofficial) | Live stock/ETF price fetching |</p>\n<p>| PHPMailer | (existing) | Email sending via `app_send_mail()` |</p>\n<p>---</p>\n<h2>Coding Conventions (consistent with existing codebase)</h2>\n<ul>\n<li>`declare(strict_types=1)` in all PHP files</li>\n<li>`app_db()` for database connections, `mysqli` throughout</li>\n<li>`app_query_all()`, `app_query_one()`, `app_h()`, `app_send_json()` helpers</li>\n<li>`app_log_backend_exception()` for error logging</li>\n<li>POST/Redirect/GET for all form submissions</li>\n<li>`ON DUPLICATE KEY UPDATE` for safe upserts</li>\n<li>`app_backend_render_header()` / `app_backend_render_footer()` for page chrome</li>\n<li>CSS custom properties: `--accent`, `--panel`, `--line`, `--text`, `--muted`, `--shadow`</li>\n<li>All new tables use `CREATE TABLE IF NOT EXISTS` (idempotent, no separate migration needed)</li>\n</ul>","file_name":"SESSION_LOG_2026-04-07.md","stored_name":"20260407-190624-56f0e818.md","mime_type":"application/octet-stream","file_size_bytes":"21112","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260407-190624-56f0e818.md","is_deleted":"0","created_at":"2026-04-07 15:06:24","updated_at":"2026-04-07 15:08:09","editor_content":"# Session Log — 2026-04-07\r\n\r\nAll work completed in this session on the CustomerDB backend application.\r\n\r\n---\r\n\r\n## Summary of New Modules and Changes\r\n\r\n| Area | File(s) | Status |\r\n|------|---------|--------|\r\n| Bills Manager | `backend/bills_service.php`, `backend/bills.php` | New |\r\n| Next-Day Email Job | `backend/jobs/send_nextday_customer_emails.php` | New |\r\n| Cron Manager UI | `backend/cron_manager.php` | New |\r\n| Error Log UI | `backend/error_log.php` | New |\r\n| Change Log UI | `backend/change_log.php` | New |\r\n| Stocks & Crypto | `backend/stocks_crypto_service.php`, `backend/stocks_crypto.php` | New |\r\n| Backend Home | `backend/index.php` | Modified |\r\n| Documents Editor | `backend/documents.php` | Full rewrite |\r\n| Documents Service | `backend/document_service.php` | Bug fix |\r\n\r\n---\r\n\r\n## 1. Bills Manager\r\n\r\n### Files\r\n- `backend/bills_service.php` — 356 lines, service layer\r\n- `backend/bills.php` — 972 lines, UI page\r\n\r\n### Purpose\r\nReplaces a spreadsheet (`newbills2026.xlsx`) with a web-based bill payment tracker.\r\nTracks bank account opening balances, monthly bill payments, and computes running bank\r\nbalances in the same layout as the Excel spreadsheet.\r\n\r\n### Database Tables (auto-created on first load)\r\n\r\n**`bills_account`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_account_id` | INT PK AUTO | |\r\n| `name` | VARCHAR(120) | Bank name |\r\n| `sort_order` | INT | Display order |\r\n\r\n**`bills_item`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_item_id` | INT PK AUTO | |\r\n| `label` | VARCHAR(200) | Bill name |\r\n| `default_amount` | DECIMAL(12,2) | Pre-fills the Due column |\r\n| `default_account_id` | INT | Default bank to debit |\r\n| `notes` | TEXT | Instructions / URLs |\r\n| `sort_order` | INT | Row order |\r\n| `is_deleted` | TINYINT | Soft delete |\r\n\r\n**`bills_entry`**\r\nMonthly bill payment rows.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_entry_id` | INT PK AUTO | |\r\n| `bills_item_id` | INT | FK to `bills_item` |\r\n| `year`, `month` | INT | Billing period |\r\n| `amount_due` | DECIMAL(12,2) | Amount billed |\r\n| `amount_paid` | DECIMAL(12,2) | Amount actually paid |\r\n| `account_id` | INT | Bank used to pay |\r\n\r\n**`bills_balance`**\r\nOpening bank balances per month.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `bills_balance_id` | INT PK AUTO | |\r\n| `account_id` | INT | FK to `bills_account` |\r\n| `year`, `month` | INT | Period |\r\n| `opening_balance` | DECIMAL(14,2) | Balance at start of month |\r\n\r\n### Seed Data\r\nOn first load (empty table), the following are auto-seeded from the original Excel file:\r\n\r\n**Accounts:** USAA, MIDFLORIDA, Centenial, CapitalOne\r\n\r\n**Bills (11 items):** MidFlorida Mortgage, MidFlorida Credit Card, USAA Credit Card,\r\nCentenial Bank, Electricity, Waste Management, Comcast/Xfinity, Insurance, Water,\r\nCapitalOne #1, CapitalOne #2\r\n\r\n### Service Functions\r\n\r\n```php\r\napp_install_bills_schema(mysqli $conn): void\r\napp_bills_seed_defaults(mysqli $conn): void\r\napp_bills_accounts(mysqli $conn): array\r\napp_bills_all_accounts(mysqli $conn): array\r\napp_bills_items(mysqli $conn): array\r\napp_bills_balances(mysqli $conn, int $year, int $month): array // map: account_id → float\r\napp_bills_entries(mysqli $conn, int $year, int $month): array // map: item_id → [due, paid, account_id]\r\napp_bills_active_months(mysqli $conn, int $year): array\r\napp_bills_save_balance(mysqli $conn, int $year, int $month, int $accountId, float $balance): void\r\napp_bills_save_entry(mysqli $conn, int $year, int $month, int $itemId, ?float $due, ?float $paid, ?int $accountId): void\r\napp_bills_save_account(mysqli $conn, array $data): int\r\napp_bills_delete_account(mysqli $conn, int $id): void\r\napp_bills_save_item(mysqli $conn, array $data): int\r\napp_bills_delete_item(mysqli $conn, int $id): void\r\napp_bills_reorder_item(mysqli $conn, int $id, string $dir): void\r\napp_bills_copy_month(mysqli $conn, int $fromYear, int $fromMonth, int $toYear, int $toMonth): int\r\n```\r\n\r\n### UI Features (`bills.php`)\r\n- Month/year navigation with ◀ / ▶ buttons and active-month pills\r\n- **Bank Balances section** — editable opening balance per account, inline Save button\r\n- **Bills table** — spreadsheet-style rows with:\r\n - Amount Due (editable, pre-filled from default)\r\n - Amount Paid (editable, AJAX auto-save after 600 ms debounce)\r\n - Account selector (which bank pays this bill)\r\n - Balance After column (computed client-side in real time)\r\n - Up/Down sort buttons\r\n - Edit and Delete per row\r\n- **Sticky running totals sidebar** — shows each bank's current balance as bills are entered\r\n- **Copy from previous month** — modal to copy all entries from a prior month to the current one\r\n- **Add Bill / Edit Bill modals**\r\n- **Add Account / Edit Account modals**\r\n- All saves use POST/Redirect/GET; bill entry changes auto-save via `fetch()` AJAX\r\n\r\n### Running Total Calculation\r\nJavaScript function `recalcTotals()` walks bills in DOM order, maintains a\r\n`bal[accountId]` map starting from opening balances, subtracts `amount_paid` from the\r\nselected account for each bill, and updates the \"Balance After\" column and sidebar panel\r\nin real time — matching the Excel spreadsheet formula logic.\r\n\r\n---\r\n\r\n## 2. Next-Day Customer Email Job\r\n\r\n### File\r\n- `backend/jobs/send_nextday_customer_emails.php` — 158 lines\r\n\r\n### Purpose\r\nPHP CLI translation of the VBA macro `RunNextDayCustomerEmails()`.\r\nQueries `setmore_appointments` for appointments scheduled for tomorrow\r\n(status ≠ Cancelled) and sends a reminder email to each customer.\r\n\r\n### Usage\r\n```bash\r\n# Normal run (sends real emails)\r\nphp backend/jobs/send_nextday_customer_emails.php\r\n\r\n# Dry run — prints what would be sent, no emails sent\r\nphp backend/jobs/send_nextday_customer_emails.php --dry-run\r\n\r\n# Override date (for testing a specific date)\r\nphp backend/jobs/send_nextday_customer_emails.php --date=2026-04-09\r\n```\r\n\r\n### Recommended Cron Schedule\r\n```\r\n0 20 * * * php /path/to/backend/jobs/send_nextday_customer_emails.php\r\n```\r\nRuns at 8:00 PM every evening, sends reminders for the following day's appointments.\r\n\r\n### Email Content\r\n- Subject: `Reminder: Your appointment at Ella's Alterations tomorrow`\r\n- Body matches the original VBA email with full address, phone, 5-line award\r\n signature block, and a plain-text fallback\r\n- Uses `app_send_mail()` / PHPMailer\r\n\r\n---\r\n\r\n## 3. Cron Manager UI\r\n\r\n### File\r\n- `backend/cron_manager.php` — 482 lines\r\n\r\n### Purpose\r\nWeb UI for managing scheduled background jobs. Stores job definitions in the database,\r\nallows toggling on/off, running immediately, editing schedules, and exporting to\r\n`/etc/cron.d/` format.\r\n\r\n### Database Table (auto-created)\r\n\r\n**`cron_job`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `cron_job_id` | INT PK AUTO | |\r\n| `label` | VARCHAR(120) | Human name |\r\n| `description` | TEXT | What the job does |\r\n| `schedule` | VARCHAR(100) | Cron expression e.g. `0 20 * * *` |\r\n| `command` | VARCHAR(500) | Shell command to run |\r\n| `enabled` | TINYINT | 0 = disabled, 1 = enabled |\r\n| `last_run_at` | DATETIME | Updated on \"Run Now\" |\r\n| `last_output` | TEXT | Captured stdout/stderr |\r\n| `sort_order` | INT | Display order |\r\n\r\n### Seeded Jobs (on first install)\r\n1. **Next-Day Customer Emails** — `0 20 * * *` — `php backend/jobs/send_nextday_customer_emails.php`\r\n2. **Morning Jobs** — `0 7 * * *` — `php backend/jobs/morning_jobs.php`\r\n3. **Nightly Reports** — `0 23 * * *` — `php backend/jobs/nightly_reports.php`\r\n4. **DB Backup** — `0 2 * * *` — `bash backend/bin/db_backup.sh`\r\n5. **Daily Cleanup** — `30 3 * * *` — `php backend/jobs/daily_cleanup.php`\r\n6. **Add Name Records Auto Delete** — `0 4 * * *` — `php backend/jobs/cleanup_name_records.php`\r\n\r\nNew jobs are added idempotently (checked by label before inserting) so existing\r\ninstallations are not re-seeded.\r\n\r\n### UI Features\r\n- Table listing all jobs with schedule, enabled toggle, last run time\r\n- **Toggle** — flip enabled/disabled immediately (POST/redirect)\r\n- **▶ Run Now** — executes the command via `exec()`, captures output, stores to DB, shows in modal\r\n- **Add / Edit** — form with label, description, command, schedule, enabled flag\r\n- **Schedule presets** — quick-select buttons (hourly, daily at common times, weekly, monthly)\r\n- `cron_describe(string $expr): string` — converts `0 20 * * *` → \"Daily at 20:00\"\r\n- **Export Crontab** — writes `/tmp/customerdb_crontab_export.txt` in `/etc/cron.d/` format\r\n\r\n---\r\n\r\n## 4. Error Log UI\r\n\r\n### File\r\n- `backend/error_log.php` — 262 lines\r\n\r\n### Purpose\r\nDedicated UI for browsing `backend_error_log` records with filtering, pagination,\r\nand per-entry management.\r\n\r\n### Features\r\n- **Stats bar** — Total errors, Today's errors, Latest error timestamp\r\n- **Filter bar** — Search (message/context), Source dropdown, Severity dropdown\r\n- **Pagination** — 50 per page with smart ellipsis navigator\r\n- **Per-row delete** — removes individual log entries\r\n- **Clear All button** — truncates the entire log (with confirmation)\r\n- JSON context blocks displayed in `<details>` collapsibles with dark monospace styling\r\n- URL: `/backend/error_log.php`\r\n\r\n---\r\n\r\n## 5. Change Log UI\r\n\r\n### File\r\n- `backend/change_log.php` — 248 lines\r\n\r\n### Purpose\r\nDedicated UI for browsing `backend_change_log` records — audit trail of all\r\ndata changes across the application.\r\n\r\n### Features\r\n- **Filter bar** — Search, Area, Action type, Entity type dropdowns\r\n- **Pagination** — smart ellipsis navigator\r\n- **Per-row detail panels:**\r\n - Before / After / Context — collapsed JSON blocks\r\n - Changed Data — open by default, shows the changed fields summary\r\n- Action badges color-coded by type (create, update, delete)\r\n- URL: `/backend/change_log.php`\r\n\r\n---\r\n\r\n## 6. Stocks & Crypto Module\r\n\r\n### Files\r\n- `backend/stocks_crypto_service.php` — 269 lines, service layer\r\n- `backend/stocks_crypto.php` — 667 lines, UI page\r\n\r\n### Purpose\r\nTrack a personal investment portfolio — individual stocks, ETFs, precious metals,\r\nand crypto. Supports live price fetching, manual daily price entry, monthly crypto\r\nbuy tracking, and portfolio gain/loss analysis.\r\n\r\n### Database Tables (auto-created)\r\n\r\n**`stocks_asset`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `stocks_asset_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | e.g. `AAPL`, `BTC`, `GLD` |\r\n| `name` | VARCHAR(120) | Display name |\r\n| `asset_type` | ENUM | `stock`, `etf`, `crypto`, `metal`, `other` |\r\n| `shares` | DECIMAL(18,8) | Quantity owned |\r\n| `cost_basis_total` | DECIMAL(14,6) | Total purchase cost |\r\n| `is_active` | TINYINT | Soft hide |\r\n\r\n**`stocks_price`**\r\nDaily price records.\r\n\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `stocks_price_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | |\r\n| `price_date` | DATE | |\r\n| `price` | DECIMAL(14,6) | |\r\n| `source` | VARCHAR(40) | `manual` or `yahoo` |\r\n\r\nUnique constraint on `(ticker, price_date)` — upserts with `ON DUPLICATE KEY UPDATE`.\r\n\r\n**`crypto_monthly_buy`**\r\n| Column | Type | Notes |\r\n|--------|------|-------|\r\n| `crypto_monthly_buy_id` | INT PK AUTO | |\r\n| `ticker` | VARCHAR(20) | |\r\n| `year`, `month` | INT | |\r\n| `quantity` | DECIMAL(18,8) | Crypto units purchased |\r\n| `amount_usd` | DECIMAL(14,2) | USD spent |\r\n| `notes` | VARCHAR(255) | |\r\n\r\n### Service Functions\r\n\r\n```php\r\napp_install_stocks_schema(mysqli $conn): void\r\napp_stocks_assets(mysqli $conn, string $type = ''): array\r\napp_stocks_save_asset(mysqli $conn, array $data): int\r\napp_stocks_delete_asset(mysqli $conn, int $id): void\r\napp_stocks_save_price(mysqli $conn, string $ticker, string $date, float $price, string $source = 'manual'): void\r\napp_stocks_save_prices_bulk(mysqli $conn, string $date, array $prices): int\r\napp_stocks_latest_prices(mysqli $conn): array // map: ticker → [price, date]\r\napp_stocks_price_history(mysqli $conn, string $ticker, int $limit = 90): array\r\napp_stocks_fetch_live_price(string $ticker): ?float // Yahoo Finance API, 8s timeout\r\napp_stocks_fetch_all_live(mysqli $conn): array // fetches all active tickers\r\napp_crypto_monthly_buys(mysqli $conn, int $year, int $month = 0): array\r\napp_crypto_save_monthly_buy(mysqli $conn, array $data): void\r\napp_crypto_delete_monthly_buy(mysqli $conn, int $id): void\r\napp_stocks_dashboard(mysqli $conn): array // cost_basis, current_value, gain_loss, gain_pct per asset\r\napp_stocks_stats(mysqli $conn): array // total portfolio stats\r\n```\r\n\r\n### Live Price Source\r\nYahoo Finance unofficial API:\r\n```\r\nhttps://query1.finance.yahoo.com/v8/finance/chart/{TICKER}?interval=1d&range=1d\r\n```\r\n8-second timeout, reads `.chart.result[0].meta.regularMarketPrice`.\r\n\r\n### UI Tabs (`stocks_crypto.php`)\r\n\r\n1. **Portfolio** — dashboard table with ticker, shares, cost basis, current value,\r\n gain/loss, gain %, latest price date. Stats bar shows total value, total cost,\r\n total gain/loss. \"Fetch Live Prices\" button calls Yahoo Finance for all tickers.\r\n\r\n2. **Enter Prices** — manual daily price entry grid for all active assets,\r\n date picker (defaults to today), bulk save.\r\n\r\n3. **Crypto Buys** — monthly crypto purchase log. Month/year selector, table of\r\n ticker/quantity/USD/notes rows, add and delete per entry.\r\n\r\n4. **Add Asset** — form to add a new stock, ETF, crypto, metal, or other asset.\r\n\r\n### Auto-Refresh\r\nA checkbox above the stats bar auto-refreshes the Portfolio tab every 10 seconds.\r\nState persists in `localStorage` key `sc_auto_refresh`. On reload, strips `?ok=`\r\nand `?warn=` query params to prevent the notice from repeating every 10 seconds.\r\n\r\n---\r\n\r\n## 7. Backend Home — New Cards (`index.php`)\r\n\r\nSix new cards added to the backend home dashboard:\r\n\r\n| Card | Link | Secondary Action |\r\n|------|------|-----------------|\r\n| Customer Totals | `/backend/customer_totals.php` | — |\r\n| Ambient Weather | `/backend/ambient_weather.php` | — |\r\n| Bills Manager | `/backend/bills.php` | — |\r\n| Stocks & Crypto | `/backend/stocks_crypto.php` | — |\r\n| Cron Manager | `/backend/cron_manager.php` | — |\r\n| Error Log | `/backend/error_log.php` | Change Log |\r\n\r\n---\r\n\r\n## 8. Documents Editor — Full Rewrite (`documents.php`)\r\n\r\n### Problem\r\nGitHub Copilot introduced several bugs into the previous version:\r\n- `docLoad()` had mismatched closing braces, breaking the entire function\r\n- The document library was constrained to ~40% page width inside a grid column\r\n- The editor was a plain `<textarea>` with no formatting support\r\n\r\n### Solution\r\nComplete rewrite of all HTML, CSS, and JavaScript. PHP POST handlers were kept\r\nidentical — only the front-end was replaced.\r\n\r\n### Editor: EasyMDE\r\nReplaced the plain textarea with **EasyMDE** (Easy Markdown Editor) via CDN:\r\n```html\r\n<link rel=\"stylesheet\" href=\"https://unpkg.com/easymde/dist/easymde.min.css\">\r\n<script src=\"https://unpkg.com/easymde/dist/easymde.min.js\"></script>\r\n```\r\n\r\nEasyMDE wraps the `<textarea id=\"content_markdown\">` and syncs content on form submit.\r\n`ta.value = easyMDE.value()` is called explicitly in `submitDoc()` before `form.submit()`.\r\n\r\n### Toolbar Buttons\r\nSave (custom gradient btn) | Bold · Italic · Strikethrough | H1 · H2 · H3 |\r\nUnordered List · Ordered List · Checklist (custom) | Quote · Code · Table |\r\nHR · Link · Image URL (custom) | Undo · Redo | Fullscreen | Guide\r\n\r\n- **Save** button in toolbar calls `submitDoc()`, same as the Save Document button below\r\n- **Ctrl+S / Cmd+S** keyboard shortcut via CodeMirror `extraKeys`\r\n- **Checklist** inserts `- [ ] ` prefix on selected text\r\n- **Image URL** prompts for URL and alt text, inserts `` syntax\r\n\r\n### Preview Toggle\r\nEasyMDE's built-in `preview` and `side-by-side` buttons were **removed** to prevent\r\nboth raw markdown and rendered HTML showing simultaneously.\r\n\r\nA custom **Preview** button in the panel header replaces them:\r\n- Uses `marked.js` (CDN) to render the markdown client-side\r\n- **Preview on**: hides `EasyMDEContainer` and highlight strip; shows `#doc-preview-pane`\r\n- **Edit on**: hides preview pane; shows EasyMDE and highlight strip; calls `codemirror.refresh()`\r\n- Loading a document via `docLoad()` automatically returns to edit mode\r\n- Button state: pill shape, switches between \"👁 Preview\" and \"✏ Edit\" labels\r\n\r\n```html\r\n<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\"></script>\r\n```\r\n\r\n### `docLoad()` — Clean Rewrite\r\nOld function had mismatched braces and redundant `editor.value` assignments.\r\nNew function:\r\n1. Returns to edit mode if preview is on\r\n2. Sets heading to \"Loading…\"\r\n3. Highlights active `<tr>` and `<a>` in library immediately\r\n4. `fetch('/backend/api/documents.php?backend_document_id=' + id)`\r\n5. Populates all meta fields (type, title, slug, summary)\r\n6. Sets `easyMDE.value(content)` from `editor_content` or `content_markdown`\r\n7. Shows/hides file notice\r\n8. Calls `renderVersionList(data.versions)`\r\n9. Updates URL via `history.replaceState()`\r\n10. Flashes panel border with accent color for 800 ms\r\n\r\n### Library Panel — Full Width\r\nOld layout: library was inside a 2-column grid alongside the editor.\r\nNew layout:\r\n```\r\n[Editor Panel — full width]\r\n[Library Panel — full width, max-height: 460px scrollable]\r\n[Upload | Versions — 2-column row]\r\n```\r\n\r\nLive client-side search filters table rows by `textContent` without a page reload.\r\n\r\n### Word Count & Highlights\r\n- Word count and char count update on every keystroke via `codemirror.on('change', ...)`\r\n- 6 highlight color swatches + a clear option wrap selected text in\r\n `<span class=\"hl\" style=\"background:COLOR\">...</span>`\r\n- Clear highlight strips existing `<span class=\"hl\">` wrappers from selected text\r\n\r\n### CSS Theme Conformity (visit.html standard)\r\nAll custom CSS was updated to match the application theme:\r\n\r\n| Property | Before | After |\r\n|----------|--------|-------|\r\n| Form input border-radius | `10px` | `12px` (matches `--radius-sm`) |\r\n| Form input padding | `8px 10px` | `11px 14px` |\r\n| Field label font-size | `0.73rem` + uppercase | `0.82rem`, no forced uppercase |\r\n| Field label letter-spacing | `0.07em` | `0.04em` |\r\n| Focus ring | `outline: 2px solid var(--accent)` | `outline: 2px solid rgba(175,77,49,0.3)` |\r\n| Panel header background | `rgba(244,239,231,0.6)` hardcoded | `var(--panel)` |\r\n| Toolbar background | `rgba(244,239,231,0.7)` hardcoded | `rgba(255,255,255,0.55)` |\r\n| Action bar background | `rgba(244,239,231,0.4)` hardcoded | `rgba(255,255,255,0.25)` |\r\n| Save button | `background: var(--accent)` | `linear-gradient(135deg, var(--accent), var(--accent-deep))` |\r\n| Action buttons | `border-radius: 7px` | `border-radius: 999px` pill shape |\r\n| Active library row tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.07)` accent |\r\n| Type badge (markdown) | `rgba(26,122,154,0.1)` teal | `rgba(175,77,49,0.1)` accent |\r\n| File notice tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.05)` accent |\r\n\r\n---\r\n\r\n## 9. `document_service.php` — Parse Error Fix\r\n\r\n### Problem\r\nThe function `app_backend_documents_dir()` had its closing `return $path; }` accidentally\r\ndisplaced to after `app_document_can_edit_inline()` (lines 147–148), with the rest of the\r\nfile's functions (`app_libreoffice_convert_to_html`, etc.) nested inside the unclosed\r\nfunction body. This caused a PHP parse error on every request to any page that\r\n`require_once`'s `document_service.php`, making `app_document_can_edit_inline()`\r\nappear undefined.\r\n\r\n### Fix\r\nTwo edits to `document_service.php`:\r\n\r\n1. Closed `app_backend_documents_dir()` properly after the `mkdir` block:\r\n```php\r\nfunction app_backend_documents_dir(): string\r\n{\r\n $path = __DIR__ . '/documents_storage';\r\n if (!is_dir($path)) {\r\n mkdir($path, 0775, true);\r\n }\r\n return $path; // ← added\r\n} // ← added\r\n```\r\n\r\n2. Removed the orphaned `return $path; }` that had been displaced to after\r\n`app_document_can_edit_inline()` at lines 147–148.\r\n\r\n---\r\n\r\n## Access URLs\r\n\r\n| Module | URL |\r\n|--------|-----|\r\n| Backend Home | `/backend/` |\r\n| Bills Manager | `/backend/bills.php` |\r\n| Stocks & Crypto | `/backend/stocks_crypto.php` |\r\n| Cron Manager | `/backend/cron_manager.php` |\r\n| Error Log | `/backend/error_log.php` |\r\n| Change Log | `/backend/change_log.php` |\r\n| Documents | `/backend/documents.php` |\r\n| Documents API | `/backend/api/documents.php` |\r\n\r\n---\r\n\r\n## Technology Used\r\n\r\n| Library | Version | Purpose |\r\n|---------|---------|---------|\r\n| EasyMDE | latest (unpkg CDN) | Rich markdown editor |\r\n| marked.js | latest (jsdelivr CDN) | Client-side markdown → HTML rendering |\r\n| Yahoo Finance API | v8 (unofficial) | Live stock/ETF price fetching |\r\n| PHPMailer | (existing) | Email sending via `app_send_mail()` |\r\n\r\n---\r\n\r\n## Coding Conventions (consistent with existing codebase)\r\n\r\n- `declare(strict_types=1)` in all PHP files\r\n- `app_db()` for database connections, `mysqli` throughout\r\n- `app_query_all()`, `app_query_one()`, `app_h()`, `app_send_json()` helpers\r\n- `app_log_backend_exception()` for error logging\r\n- POST/Redirect/GET for all form submissions\r\n- `ON DUPLICATE KEY UPDATE` for safe upserts\r\n- `app_backend_render_header()` / `app_backend_render_footer()` for page chrome\r\n- CSS custom properties: `--accent`, `--panel`, `--line`, `--text`, `--muted`, `--shadow`\r\n- All new tables use `CREATE TABLE IF NOT EXISTS` (idempotent, no separate migration needed)","is_text_editable":1,"can_edit_inline":1}
{"file_name":"SESSION_LOG_2026-04-07.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"10","document_type":"upload","title":"Claude 04-07-2026","slug":"claude-04-07-2026","summary_text":"Claude 04-07-2026 fixing backend Document areas","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-07.md","stored_name":"20260407-190624-56f0e818.md","mime_type":"application/octet-stream","file_size_bytes":"21112","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260407-190624-56f0e818.md","is_deleted":"0","created_at":"2026-04-07 15:06:24","updated_at":"2026-04-07 15:06:24","editor_content":"# Session Log — 2026-04-07\n\nAll work completed in this session on the CustomerDB backend application.\n\n---\n\n## Summary of New Modules and Changes\n\n| Area | File(s) | Status |\n|------|---------|--------|\n| Bills Manager | `backend/bills_service.php`, `backend/bills.php` | New |\n| Next-Day Email Job | `backend/jobs/send_nextday_customer_emails.php` | New |\n| Cron Manager UI | `backend/cron_manager.php` | New |\n| Error Log UI | `backend/error_log.php` | New |\n| Change Log UI | `backend/change_log.php` | New |\n| Stocks & Crypto | `backend/stocks_crypto_service.php`, `backend/stocks_crypto.php` | New |\n| Backend Home | `backend/index.php` | Modified |\n| Documents Editor | `backend/documents.php` | Full rewrite |\n| Documents Service | `backend/document_service.php` | Bug fix |\n\n---\n\n## 1. Bills Manager\n\n### Files\n- `backend/bills_service.php` — 356 lines, service layer\n- `backend/bills.php` — 972 lines, UI page\n\n### Purpose\nReplaces a spreadsheet (`newbills2026.xlsx`) with a web-based bill payment tracker.\nTracks bank account opening balances, monthly bill payments, and computes running bank\nbalances in the same layout as the Excel spreadsheet.\n\n### Database Tables (auto-created on first load)\n\n**`bills_account`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_account_id` | INT PK AUTO | |\n| `name` | VARCHAR(120) | Bank name |\n| `sort_order` | INT | Display order |\n\n**`bills_item`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_item_id` | INT PK AUTO | |\n| `label` | VARCHAR(200) | Bill name |\n| `default_amount` | DECIMAL(12,2) | Pre-fills the Due column |\n| `default_account_id` | INT | Default bank to debit |\n| `notes` | TEXT | Instructions / URLs |\n| `sort_order` | INT | Row order |\n| `is_deleted` | TINYINT | Soft delete |\n\n**`bills_entry`**\nMonthly bill payment rows.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_entry_id` | INT PK AUTO | |\n| `bills_item_id` | INT | FK to `bills_item` |\n| `year`, `month` | INT | Billing period |\n| `amount_due` | DECIMAL(12,2) | Amount billed |\n| `amount_paid` | DECIMAL(12,2) | Amount actually paid |\n| `account_id` | INT | Bank used to pay |\n\n**`bills_balance`**\nOpening bank balances per month.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `bills_balance_id` | INT PK AUTO | |\n| `account_id` | INT | FK to `bills_account` |\n| `year`, `month` | INT | Period |\n| `opening_balance` | DECIMAL(14,2) | Balance at start of month |\n\n### Seed Data\nOn first load (empty table), the following are auto-seeded from the original Excel file:\n\n**Accounts:** USAA, MIDFLORIDA, Centenial, CapitalOne\n\n**Bills (11 items):** MidFlorida Mortgage, MidFlorida Credit Card, USAA Credit Card,\nCentenial Bank, Electricity, Waste Management, Comcast/Xfinity, Insurance, Water,\nCapitalOne #1, CapitalOne #2\n\n### Service Functions\n\n```php\napp_install_bills_schema(mysqli $conn): void\napp_bills_seed_defaults(mysqli $conn): void\napp_bills_accounts(mysqli $conn): array\napp_bills_all_accounts(mysqli $conn): array\napp_bills_items(mysqli $conn): array\napp_bills_balances(mysqli $conn, int $year, int $month): array // map: account_id → float\napp_bills_entries(mysqli $conn, int $year, int $month): array // map: item_id → [due, paid, account_id]\napp_bills_active_months(mysqli $conn, int $year): array\napp_bills_save_balance(mysqli $conn, int $year, int $month, int $accountId, float $balance): void\napp_bills_save_entry(mysqli $conn, int $year, int $month, int $itemId, ?float $due, ?float $paid, ?int $accountId): void\napp_bills_save_account(mysqli $conn, array $data): int\napp_bills_delete_account(mysqli $conn, int $id): void\napp_bills_save_item(mysqli $conn, array $data): int\napp_bills_delete_item(mysqli $conn, int $id): void\napp_bills_reorder_item(mysqli $conn, int $id, string $dir): void\napp_bills_copy_month(mysqli $conn, int $fromYear, int $fromMonth, int $toYear, int $toMonth): int\n```\n\n### UI Features (`bills.php`)\n- Month/year navigation with ◀ / ▶ buttons and active-month pills\n- **Bank Balances section** — editable opening balance per account, inline Save button\n- **Bills table** — spreadsheet-style rows with:\n - Amount Due (editable, pre-filled from default)\n - Amount Paid (editable, AJAX auto-save after 600 ms debounce)\n - Account selector (which bank pays this bill)\n - Balance After column (computed client-side in real time)\n - Up/Down sort buttons\n - Edit and Delete per row\n- **Sticky running totals sidebar** — shows each bank's current balance as bills are entered\n- **Copy from previous month** — modal to copy all entries from a prior month to the current one\n- **Add Bill / Edit Bill modals**\n- **Add Account / Edit Account modals**\n- All saves use POST/Redirect/GET; bill entry changes auto-save via `fetch()` AJAX\n\n### Running Total Calculation\nJavaScript function `recalcTotals()` walks bills in DOM order, maintains a\n`bal[accountId]` map starting from opening balances, subtracts `amount_paid` from the\nselected account for each bill, and updates the \"Balance After\" column and sidebar panel\nin real time — matching the Excel spreadsheet formula logic.\n\n---\n\n## 2. Next-Day Customer Email Job\n\n### File\n- `backend/jobs/send_nextday_customer_emails.php` — 158 lines\n\n### Purpose\nPHP CLI translation of the VBA macro `RunNextDayCustomerEmails()`.\nQueries `setmore_appointments` for appointments scheduled for tomorrow\n(status ≠ Cancelled) and sends a reminder email to each customer.\n\n### Usage\n```bash\n# Normal run (sends real emails)\nphp backend/jobs/send_nextday_customer_emails.php\n\n# Dry run — prints what would be sent, no emails sent\nphp backend/jobs/send_nextday_customer_emails.php --dry-run\n\n# Override date (for testing a specific date)\nphp backend/jobs/send_nextday_customer_emails.php --date=2026-04-09\n```\n\n### Recommended Cron Schedule\n```\n0 20 * * * php /path/to/backend/jobs/send_nextday_customer_emails.php\n```\nRuns at 8:00 PM every evening, sends reminders for the following day's appointments.\n\n### Email Content\n- Subject: `Reminder: Your appointment at Ella's Alterations tomorrow`\n- Body matches the original VBA email with full address, phone, 5-line award\n signature block, and a plain-text fallback\n- Uses `app_send_mail()` / PHPMailer\n\n---\n\n## 3. Cron Manager UI\n\n### File\n- `backend/cron_manager.php` — 482 lines\n\n### Purpose\nWeb UI for managing scheduled background jobs. Stores job definitions in the database,\nallows toggling on/off, running immediately, editing schedules, and exporting to\n`/etc/cron.d/` format.\n\n### Database Table (auto-created)\n\n**`cron_job`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `cron_job_id` | INT PK AUTO | |\n| `label` | VARCHAR(120) | Human name |\n| `description` | TEXT | What the job does |\n| `schedule` | VARCHAR(100) | Cron expression e.g. `0 20 * * *` |\n| `command` | VARCHAR(500) | Shell command to run |\n| `enabled` | TINYINT | 0 = disabled, 1 = enabled |\n| `last_run_at` | DATETIME | Updated on \"Run Now\" |\n| `last_output` | TEXT | Captured stdout/stderr |\n| `sort_order` | INT | Display order |\n\n### Seeded Jobs (on first install)\n1. **Next-Day Customer Emails** — `0 20 * * *` — `php backend/jobs/send_nextday_customer_emails.php`\n2. **Morning Jobs** — `0 7 * * *` — `php backend/jobs/morning_jobs.php`\n3. **Nightly Reports** — `0 23 * * *` — `php backend/jobs/nightly_reports.php`\n4. **DB Backup** — `0 2 * * *` — `bash backend/bin/db_backup.sh`\n5. **Daily Cleanup** — `30 3 * * *` — `php backend/jobs/daily_cleanup.php`\n6. **Add Name Records Auto Delete** — `0 4 * * *` — `php backend/jobs/cleanup_name_records.php`\n\nNew jobs are added idempotently (checked by label before inserting) so existing\ninstallations are not re-seeded.\n\n### UI Features\n- Table listing all jobs with schedule, enabled toggle, last run time\n- **Toggle** — flip enabled/disabled immediately (POST/redirect)\n- **▶ Run Now** — executes the command via `exec()`, captures output, stores to DB, shows in modal\n- **Add / Edit** — form with label, description, command, schedule, enabled flag\n- **Schedule presets** — quick-select buttons (hourly, daily at common times, weekly, monthly)\n- `cron_describe(string $expr): string` — converts `0 20 * * *` → \"Daily at 20:00\"\n- **Export Crontab** — writes `/tmp/customerdb_crontab_export.txt` in `/etc/cron.d/` format\n\n---\n\n## 4. Error Log UI\n\n### File\n- `backend/error_log.php` — 262 lines\n\n### Purpose\nDedicated UI for browsing `backend_error_log` records with filtering, pagination,\nand per-entry management.\n\n### Features\n- **Stats bar** — Total errors, Today's errors, Latest error timestamp\n- **Filter bar** — Search (message/context), Source dropdown, Severity dropdown\n- **Pagination** — 50 per page with smart ellipsis navigator\n- **Per-row delete** — removes individual log entries\n- **Clear All button** — truncates the entire log (with confirmation)\n- JSON context blocks displayed in `<details>` collapsibles with dark monospace styling\n- URL: `/backend/error_log.php`\n\n---\n\n## 5. Change Log UI\n\n### File\n- `backend/change_log.php` — 248 lines\n\n### Purpose\nDedicated UI for browsing `backend_change_log` records — audit trail of all\ndata changes across the application.\n\n### Features\n- **Filter bar** — Search, Area, Action type, Entity type dropdowns\n- **Pagination** — smart ellipsis navigator\n- **Per-row detail panels:**\n - Before / After / Context — collapsed JSON blocks\n - Changed Data — open by default, shows the changed fields summary\n- Action badges color-coded by type (create, update, delete)\n- URL: `/backend/change_log.php`\n\n---\n\n## 6. Stocks & Crypto Module\n\n### Files\n- `backend/stocks_crypto_service.php` — 269 lines, service layer\n- `backend/stocks_crypto.php` — 667 lines, UI page\n\n### Purpose\nTrack a personal investment portfolio — individual stocks, ETFs, precious metals,\nand crypto. Supports live price fetching, manual daily price entry, monthly crypto\nbuy tracking, and portfolio gain/loss analysis.\n\n### Database Tables (auto-created)\n\n**`stocks_asset`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `stocks_asset_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | e.g. `AAPL`, `BTC`, `GLD` |\n| `name` | VARCHAR(120) | Display name |\n| `asset_type` | ENUM | `stock`, `etf`, `crypto`, `metal`, `other` |\n| `shares` | DECIMAL(18,8) | Quantity owned |\n| `cost_basis_total` | DECIMAL(14,6) | Total purchase cost |\n| `is_active` | TINYINT | Soft hide |\n\n**`stocks_price`**\nDaily price records.\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `stocks_price_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | |\n| `price_date` | DATE | |\n| `price` | DECIMAL(14,6) | |\n| `source` | VARCHAR(40) | `manual` or `yahoo` |\n\nUnique constraint on `(ticker, price_date)` — upserts with `ON DUPLICATE KEY UPDATE`.\n\n**`crypto_monthly_buy`**\n| Column | Type | Notes |\n|--------|------|-------|\n| `crypto_monthly_buy_id` | INT PK AUTO | |\n| `ticker` | VARCHAR(20) | |\n| `year`, `month` | INT | |\n| `quantity` | DECIMAL(18,8) | Crypto units purchased |\n| `amount_usd` | DECIMAL(14,2) | USD spent |\n| `notes` | VARCHAR(255) | |\n\n### Service Functions\n\n```php\napp_install_stocks_schema(mysqli $conn): void\napp_stocks_assets(mysqli $conn, string $type = ''): array\napp_stocks_save_asset(mysqli $conn, array $data): int\napp_stocks_delete_asset(mysqli $conn, int $id): void\napp_stocks_save_price(mysqli $conn, string $ticker, string $date, float $price, string $source = 'manual'): void\napp_stocks_save_prices_bulk(mysqli $conn, string $date, array $prices): int\napp_stocks_latest_prices(mysqli $conn): array // map: ticker → [price, date]\napp_stocks_price_history(mysqli $conn, string $ticker, int $limit = 90): array\napp_stocks_fetch_live_price(string $ticker): ?float // Yahoo Finance API, 8s timeout\napp_stocks_fetch_all_live(mysqli $conn): array // fetches all active tickers\napp_crypto_monthly_buys(mysqli $conn, int $year, int $month = 0): array\napp_crypto_save_monthly_buy(mysqli $conn, array $data): void\napp_crypto_delete_monthly_buy(mysqli $conn, int $id): void\napp_stocks_dashboard(mysqli $conn): array // cost_basis, current_value, gain_loss, gain_pct per asset\napp_stocks_stats(mysqli $conn): array // total portfolio stats\n```\n\n### Live Price Source\nYahoo Finance unofficial API:\n```\nhttps://query1.finance.yahoo.com/v8/finance/chart/{TICKER}?interval=1d&range=1d\n```\n8-second timeout, reads `.chart.result[0].meta.regularMarketPrice`.\n\n### UI Tabs (`stocks_crypto.php`)\n\n1. **Portfolio** — dashboard table with ticker, shares, cost basis, current value,\n gain/loss, gain %, latest price date. Stats bar shows total value, total cost,\n total gain/loss. \"Fetch Live Prices\" button calls Yahoo Finance for all tickers.\n\n2. **Enter Prices** — manual daily price entry grid for all active assets,\n date picker (defaults to today), bulk save.\n\n3. **Crypto Buys** — monthly crypto purchase log. Month/year selector, table of\n ticker/quantity/USD/notes rows, add and delete per entry.\n\n4. **Add Asset** — form to add a new stock, ETF, crypto, metal, or other asset.\n\n### Auto-Refresh\nA checkbox above the stats bar auto-refreshes the Portfolio tab every 10 seconds.\nState persists in `localStorage` key `sc_auto_refresh`. On reload, strips `?ok=`\nand `?warn=` query params to prevent the notice from repeating every 10 seconds.\n\n---\n\n## 7. Backend Home — New Cards (`index.php`)\n\nSix new cards added to the backend home dashboard:\n\n| Card | Link | Secondary Action |\n|------|------|-----------------|\n| Customer Totals | `/backend/customer_totals.php` | — |\n| Ambient Weather | `/backend/ambient_weather.php` | — |\n| Bills Manager | `/backend/bills.php` | — |\n| Stocks & Crypto | `/backend/stocks_crypto.php` | — |\n| Cron Manager | `/backend/cron_manager.php` | — |\n| Error Log | `/backend/error_log.php` | Change Log |\n\n---\n\n## 8. Documents Editor — Full Rewrite (`documents.php`)\n\n### Problem\nGitHub Copilot introduced several bugs into the previous version:\n- `docLoad()` had mismatched closing braces, breaking the entire function\n- The document library was constrained to ~40% page width inside a grid column\n- The editor was a plain `<textarea>` with no formatting support\n\n### Solution\nComplete rewrite of all HTML, CSS, and JavaScript. PHP POST handlers were kept\nidentical — only the front-end was replaced.\n\n### Editor: EasyMDE\nReplaced the plain textarea with **EasyMDE** (Easy Markdown Editor) via CDN:\n```html\n<link rel=\"stylesheet\" href=\"https://unpkg.com/easymde/dist/easymde.min.css\">\n<script src=\"https://unpkg.com/easymde/dist/easymde.min.js\"></script>\n```\n\nEasyMDE wraps the `<textarea id=\"content_markdown\">` and syncs content on form submit.\n`ta.value = easyMDE.value()` is called explicitly in `submitDoc()` before `form.submit()`.\n\n### Toolbar Buttons\nSave (custom gradient btn) | Bold · Italic · Strikethrough | H1 · H2 · H3 |\nUnordered List · Ordered List · Checklist (custom) | Quote · Code · Table |\nHR · Link · Image URL (custom) | Undo · Redo | Fullscreen | Guide\n\n- **Save** button in toolbar calls `submitDoc()`, same as the Save Document button below\n- **Ctrl+S / Cmd+S** keyboard shortcut via CodeMirror `extraKeys`\n- **Checklist** inserts `- [ ] ` prefix on selected text\n- **Image URL** prompts for URL and alt text, inserts `` syntax\n\n### Preview Toggle\nEasyMDE's built-in `preview` and `side-by-side` buttons were **removed** to prevent\nboth raw markdown and rendered HTML showing simultaneously.\n\nA custom **Preview** button in the panel header replaces them:\n- Uses `marked.js` (CDN) to render the markdown client-side\n- **Preview on**: hides `EasyMDEContainer` and highlight strip; shows `#doc-preview-pane`\n- **Edit on**: hides preview pane; shows EasyMDE and highlight strip; calls `codemirror.refresh()`\n- Loading a document via `docLoad()` automatically returns to edit mode\n- Button state: pill shape, switches between \"👁 Preview\" and \"✏ Edit\" labels\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\"></script>\n```\n\n### `docLoad()` — Clean Rewrite\nOld function had mismatched braces and redundant `editor.value` assignments.\nNew function:\n1. Returns to edit mode if preview is on\n2. Sets heading to \"Loading…\"\n3. Highlights active `<tr>` and `<a>` in library immediately\n4. `fetch('/backend/api/documents.php?backend_document_id=' + id)`\n5. Populates all meta fields (type, title, slug, summary)\n6. Sets `easyMDE.value(content)` from `editor_content` or `content_markdown`\n7. Shows/hides file notice\n8. Calls `renderVersionList(data.versions)`\n9. Updates URL via `history.replaceState()`\n10. Flashes panel border with accent color for 800 ms\n\n### Library Panel — Full Width\nOld layout: library was inside a 2-column grid alongside the editor.\nNew layout:\n```\n[Editor Panel — full width]\n[Library Panel — full width, max-height: 460px scrollable]\n[Upload | Versions — 2-column row]\n```\n\nLive client-side search filters table rows by `textContent` without a page reload.\n\n### Word Count & Highlights\n- Word count and char count update on every keystroke via `codemirror.on('change', ...)`\n- 6 highlight color swatches + a clear option wrap selected text in\n `<span class=\"hl\" style=\"background:COLOR\">...</span>`\n- Clear highlight strips existing `<span class=\"hl\">` wrappers from selected text\n\n### CSS Theme Conformity (visit.html standard)\nAll custom CSS was updated to match the application theme:\n\n| Property | Before | After |\n|----------|--------|-------|\n| Form input border-radius | `10px` | `12px` (matches `--radius-sm`) |\n| Form input padding | `8px 10px` | `11px 14px` |\n| Field label font-size | `0.73rem` + uppercase | `0.82rem`, no forced uppercase |\n| Field label letter-spacing | `0.07em` | `0.04em` |\n| Focus ring | `outline: 2px solid var(--accent)` | `outline: 2px solid rgba(175,77,49,0.3)` |\n| Panel header background | `rgba(244,239,231,0.6)` hardcoded | `var(--panel)` |\n| Toolbar background | `rgba(244,239,231,0.7)` hardcoded | `rgba(255,255,255,0.55)` |\n| Action bar background | `rgba(244,239,231,0.4)` hardcoded | `rgba(255,255,255,0.25)` |\n| Save button | `background: var(--accent)` | `linear-gradient(135deg, var(--accent), var(--accent-deep))` |\n| Action buttons | `border-radius: 7px` | `border-radius: 999px` pill shape |\n| Active library row tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.07)` accent |\n| Type badge (markdown) | `rgba(26,122,154,0.1)` teal | `rgba(175,77,49,0.1)` accent |\n| File notice tint | `rgba(26,122,154,0.06)` teal | `rgba(175,77,49,0.05)` accent |\n\n---\n\n## 9. `document_service.php` — Parse Error Fix\n\n### Problem\nThe function `app_backend_documents_dir()` had its closing `return $path; }` accidentally\ndisplaced to after `app_document_can_edit_inline()` (lines 147–148), with the rest of the\nfile's functions (`app_libreoffice_convert_to_html`, etc.) nested inside the unclosed\nfunction body. This caused a PHP parse error on every request to any page that\n`require_once`'s `document_service.php`, making `app_document_can_edit_inline()`\nappear undefined.\n\n### Fix\nTwo edits to `document_service.php`:\n\n1. Closed `app_backend_documents_dir()` properly after the `mkdir` block:\n```php\nfunction app_backend_documents_dir(): string\n{\n $path = __DIR__ . '/documents_storage';\n if (!is_dir($path)) {\n mkdir($path, 0775, true);\n }\n return $path; // ← added\n} // ← added\n```\n\n2. Removed the orphaned `return $path; }` that had been displaced to after\n`app_document_can_edit_inline()` at lines 147–148.\n\n---\n\n## Access URLs\n\n| Module | URL |\n|--------|-----|\n| Backend Home | `/backend/` |\n| Bills Manager | `/backend/bills.php` |\n| Stocks & Crypto | `/backend/stocks_crypto.php` |\n| Cron Manager | `/backend/cron_manager.php` |\n| Error Log | `/backend/error_log.php` |\n| Change Log | `/backend/change_log.php` |\n| Documents | `/backend/documents.php` |\n| Documents API | `/backend/api/documents.php` |\n\n---\n\n## Technology Used\n\n| Library | Version | Purpose |\n|---------|---------|---------|\n| EasyMDE | latest (unpkg CDN) | Rich markdown editor |\n| marked.js | latest (jsdelivr CDN) | Client-side markdown → HTML rendering |\n| Yahoo Finance API | v8 (unofficial) | Live stock/ETF price fetching |\n| PHPMailer | (existing) | Email sending via `app_send_mail()` |\n\n---\n\n## Coding Conventions (consistent with existing codebase)\n\n- `declare(strict_types=1)` in all PHP files\n- `app_db()` for database connections, `mysqli` throughout\n- `app_query_all()`, `app_query_one()`, `app_h()`, `app_send_json()` helpers\n- `app_log_backend_exception()` for error logging\n- POST/Redirect/GET for all form submissions\n- `ON DUPLICATE KEY UPDATE` for safe upserts\n- `app_backend_render_header()` / `app_backend_render_footer()` for page chrome\n- CSS custom properties: `--accent`, `--panel`, `--line`, `--text`, `--muted`, `--shadow`\n- All new tables use `CREATE TABLE IF NOT EXISTS` (idempotent, no separate migration needed)\n","is_text_editable":1,"can_edit_inline":1}
{"file_name":"README-libreoffice-conversion.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"9","document_type":"upload","title":"Copilot - document changes","slug":"copilot-document-changes","summary_text":"Copilot - document changes to this module","content_markdown":null,"content_html":null,"file_name":"README-libreoffice-conversion.md","stored_name":"20260407-155337-5741b116.md","mime_type":"application/octet-stream","file_size_bytes":"10541","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260407-155337-5741b116.md","is_deleted":"0","created_at":"2026-04-07 11:53:37","updated_at":"2026-04-07 11:53:37","editor_content":"# LibreOffice Conversion & Editor Integration\r\n\r\nThis document describes the features added to support server-side LibreOffice conversions, the asynchronous conversion queue and worker, frontend changes, and deployment/run instructions.\r\n\r\nIMPORTANT: This document assumes the project root is the repo root (e.g. `C:\\code\\customerdb`) and paths below are relative to it.\r\n\r\n---\r\n\r\n## Overview\r\n\r\nSummary of what was added or changed:\r\n\r\n- Frontend/editor\r\n - `backend/documents.php`\r\n - Replaced simple markdown renderer with `marked.js` and added Prism highlighting.\r\n - Toolbar: Save button, view toggles, fullscreen, color highlight swatches that wrap selected text in a `<span class=\"hl\" style=\"background:...\">`.\r\n - Files that can be edited inline show an **Edit** button and load into the editor; non-inline files show **Open** (open stored file in new tab).\r\n - When a file is non-inline and conversion is pending, the preview shows a conversion status block and polls the API until `content_html` becomes available.\r\n\r\n- Document extraction & conversion helpers\r\n - `backend/document_service.php`\r\n - Added `app_document_can_edit_inline()` to detect files editable directly in the textarea.\r\n - Implemented lightweight extraction for `docx`, `odt`, `rtf`, `pptx`, `xlsx` where possible (Zip-based parsing for Office formats).\r\n - Added LibreOffice helpers: `app_libreoffice_find_command()`, `app_libreoffice_convert_to_html()`, `app_libreoffice_convert_to_text()`.\r\n - Added queuing hook `app_conversion_enqueue()` to create file-based jobs and insert DB job rows.\r\n\r\n- Asynchronous conversion queue and worker\r\n - Job queue storage: `backend/documents_storage/convert_queue/job-*.json`.\r\n - DB tracking: table `backend_conversion_job` to track job lifecycle (`queued`, `processing`, `done`, `error`, `cancelled`).\r\n - Worker script: `backend/bin/convert_worker.php` ? processes job files, runs LibreOffice conversions, saves results to `backend_document.content_html` and updates job status in DB.\r\n - Example systemd unit: `backend/systemd/convert-worker.service` (adjust paths/user before enabling).\r\n\r\n- API to manage jobs\r\n - `backend/api/conversion_jobs.php` ? list jobs and `POST` actions: `retry`, `cancel`.\r\n\r\n- Migration and helper tooling\r\n - Migration SQL: `migrations/20260407_add_conversion_job.sql` ? creates job table and ensures `content_html` on `backend_document`.\r\n - Migration runner: `backend/bin/run_migration.php` ? executes the migration SQL using the app DB connection.\r\n\r\n---\r\n\r\n## File map (key files)\r\n\r\n- `backend/documents.php` ? Editor UI, preview handling, polling, highlight UI.\r\n- `backend/document_service.php` ? Extraction/queue helpers and LibreOffice integration.\r\n- `backend/api/documents.php` ? Document detail API used by frontend.\r\n- `backend/api/conversion_jobs.php` ? New API to list/manage conversion jobs.\r\n- `backend/bin/convert_worker.php` ? Worker that runs LibreOffice and updates DB.\r\n- `backend/bin/run_migration.php` ? Helper to run SQL migration with application DB config.\r\n- `migrations/20260407_add_conversion_job.sql` ? SQL migration file to apply.\r\n- `backend/systemd/convert-worker.service` ? example systemd unit to run worker as daemon.\r\n\r\n---\r\n\r\n## Database changes\r\n\r\nThe migration will:\r\n\r\n1. Create table `backend_conversion_job` with columns:\r\n - `conversion_job_id` (PK)\r\n - `backend_document_id` (FK-ish)\r\n - `status` (VARCHAR) values: `queued`, `processing`, `done`, `error`, `cancelled`\r\n - `storage_path`, `output_path`, `error_text`, `created_at`, `updated_at`\r\n\r\n2. Ensure `backend_document.content_html` LONGTEXT column exists (used for serving converted HTML previews).\r\n\r\nMigration file: `migrations/20260407_add_conversion_job.sql`\r\n\r\n### Apply migration\r\n\r\nTwo options:\r\n\r\n- Run via the application DB connection (uses `app_db()` settings in `bootstrap.php`):\r\n\r\n - From repository root:\r\n\r\n ```bash\r\n php backend/bin/run_migration.php\r\n ```\r\n\r\n - This script reads `migrations/20260407_add_conversion_job.sql` and executes the statements.\r\n\r\n- Run directly with MariaDB client (replace placeholders):\r\n\r\n ```bash\r\n mysql -u <db_user> -p ellas_alterations < migrations/20260407_add_conversion_job.sql\r\n ```\r\n\r\nNote: The app expects the `ellas_alterations` database (the DB name you mentioned). Ensure the DB user has `CREATE/ALTER` privileges.\r\n\r\n---\r\n\r\n## Server prerequisites\r\n\r\n- LibreOffice must be installed and available on PATH as `libreoffice` or `soffice`.\r\n - Debian/Ubuntu example: `sudo apt update && sudo apt install -y libreoffice`\r\n- PHP must have `exec()` available for the worker process (worker uses `exec()` to invoke LibreOffice).\r\n- PHP `ZipArchive` extension (`php-zip`) is recommended for better extraction of docx/odt/pptx/xlsx.\r\n- The web/worker user must have read access to the stored files and write access to:\r\n - `backend/documents_storage/convert_queue` (job files)\r\n- Prevent running conversions as `root` in production ? configure a dedicated unprivileged user.\r\n\r\nSecurity note: Converting user-uploaded files can be risky. Run the worker as a dedicated, restricted user, and consider sandboxing or containerization.\r\n\r\n---\r\n\r\n## Worker (how it runs)\r\n\r\n- Manual run (process queued jobs once):\r\n\r\n ```bash\r\n php backend/bin/convert_worker.php\r\n ```\r\n\r\n- Run continuously for development/testing:\r\n\r\n ```bash\r\n while true; do php backend/bin/convert_worker.php; sleep 3; done\r\n ```\r\n\r\n- Systemd service (example):\r\n - Copy `backend/systemd/convert-worker.service` to `/etc/systemd/system/convert-worker.service` and update `WorkingDirectory` and `ExecStart` to match your deployment path.\r\n - Enable and start:\r\n\r\n ```bash\r\n sudo systemctl daemon-reload\r\n sudo systemctl enable --now convert-worker.service\r\n sudo systemctl status convert-worker.service\r\n ```\r\n\r\nThe worker behavior:\r\n- Scans `backend/documents_storage/convert_queue/` for `job-*.json` files.\r\n- Marks DB job `status = processing` and updates the JSON job file with a timestamp.\r\n- Runs LibreOffice to convert the source file to HTML. If that fails, it attempts to convert to plain text.\r\n- On success, writes converted HTML (or text wrapped as HTML) into `backend_document.content_html` and updates job row `status = done` and `output_path`.\r\n- On failure, updates job row `status = error` and stores `error_text`.\r\n- Removes the job file when finished.\r\n\r\nNote: The worker updates DB rows using the same DB configuration as the app.\r\n\r\n---\r\n\r\n## API: managing conversion jobs\r\n\r\n- List jobs\r\n - `GET /backend/api/conversion_jobs.php`\r\n - Returns recent job rows (limited to 200)\r\n\r\n- Retry a job\r\n - `POST /backend/api/conversion_jobs.php` with JSON body `{ \"action\": \"retry\", \"conversion_job_id\": <id> }`\r\n - Re-enqueues a job and marks DB row `status = 'queued'`.\r\n\r\n- Cancel a job\r\n - `POST /backend/api/conversion_jobs.php` with JSON body `{ \"action\": \"cancel\", \"conversion_job_id\": <id> }`\r\n\r\nThe endpoint returns JSON success or error messages.\r\n\r\n---\r\n\r\n## Frontend behavior & UX\r\n\r\n- When selecting a document in the backend Documents UI:\r\n - If `document.can_edit_inline` is true, the **Edit** button loads the document into the textarea editor.\r\n - If not editable inline:\r\n - If `document.content_html` exists, the preview renders the converted HTML (Prism highlighting applied).\r\n - If `content_html` is missing, the preview shows a **Conversion pending...** panel with a placeholder progress bar and `convertStatus` text ? the page polls the documents API until `content_html` becomes available.\r\n\r\n- Save / highlights\r\n - Toolbar Save button submits the same form as the Save Document button.\r\n - Highlight swatches wrap selected text with `<span class=\"hl\" style=\"background:...\">` which renders in the preview. Highlights will be saved inside `content_markdown` (persisted as HTML markup in the markdown content).\r\n\r\n---\r\n\r\n## How to test end-to-end\r\n\r\n1. Ensure migration applied and DB reachable.\r\n2. Ensure LibreOffice is installed and the worker can run `exec()`.\r\n3. Upload a `.docx` / `.pptx` / `.odt` file using the Upload form in the backend.\r\n4. Check that a JSON job file appears in `backend/documents_storage/convert_queue/` and a row is created in `backend_conversion_job` with `status = queued`.\r\n5. Run the worker manually: `php backend/bin/convert_worker.php`.\r\n6. Worker should convert and update `backend_document.content_html` and set job `status = done`.\r\n7. In the Documents page, select the document: the preview should update to show converted HTML (or show Conversion pending until conversion completes).\r\n\r\n---\r\n\r\n## Troubleshooting\r\n\r\n- No jobs processed:\r\n - Ensure worker runs under an account that can read job files and run `libreoffice`.\r\n - Confirm `libreoffice` is on PATH and `php -r \"echo shell_exec('command -v libreoffice');\"` returns a path.\r\n - Check permissions of `backend/documents_storage/convert_queue/`.\r\n\r\n- Conversion fails / `status = error`:\r\n - Inspect `backend_conversion_job.error_text` for details.\r\n - Try running `php backend/bin/convert_worker.php` manually and capture output.\r\n - Confirm LibreOffice works on the file from the worker user: `libreoffice --headless --convert-to html --outdir /tmp /path/to/file.docx`.\r\n\r\n- HTML content looks malformed or contains unsafe markup:\r\n - Sanitize HTML before serving (recommended). Consider `HTMLPurifier` or a whitelist approach.\r\n\r\n---\r\n\r\n## Recommended next improvements (future work)\r\n\r\n- Migrate the queue to a DB-backed atomic queue (use `backend_conversion_job` rows as the primary queue source) so workers select and lock rows (avoid file races).\r\n- Sanitize `content_html` before storing/serving.\r\n- Add a small admin page to list conversion jobs and perform retry/cancel from the UI (calls `backend/api/conversion_jobs.php`).\r\n- Add metrics & logging for conversion durations and failure counts.\r\n- Implement a retention policy / cleanup job for old job records and temp files.\r\n\r\n---\r\n\r\n## Contact / follow-up\r\n\r\nTell me which of the following you want next and I will implement it:\r\n\r\n- Convert the file-based queue to a DB-atomic queue (worker picks rows with `status='queued'` using an atomic update), and remove JSON job files.\r\n- Add the admin UI to manage conversion jobs inside the backend.\r\n- Implement HTML sanitization of `content_html` prior to storage.\r\n- Update the systemd unit to match the exact server paths & user if you give me that information.\r\n\r\n\r\n\r\n---\r\n\r\n*Document generated by GitHub Copilot.*\r\n","is_text_editable":1,"can_edit_inline":1}
{"document_type":"markdown"}
{"backend_document_id":"2","document_type":"markdown","title":"Test Note","slug":"test-note","summary_text":"This is test note summary","content_markdown":"# test\r\n\r\n## second line\r\n\r\n---\r\n\r\n- list\r\n- sls\r\n- sdfas'\r\n- asdasdf","content_html":"<h1>test</h1>\n<h2>second line</h2>\n<p>---</p>\n<ul>\n<li>list</li>\n<li>sls</li>\n<li>sdfas'</li>\n<li>asdasdf</li>\n</ul>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 15:00:01","updated_at":"2026-04-03 08:04:41","editor_content":"# test\r\n\r\n## second line\r\n\r\n---\r\n\r\n- list\r\n- sls\r\n- sdfas'\r\n- asdasdf","is_text_editable":1}
{"backend_document_id":"2","document_type":"markdown","title":"Test Note","slug":"test-note","summary_text":"This is test note summary","content_markdown":"# test\r\n\r\n## second line\r\n\r\n---\r\n\r\n- list\r\n- sls\r\n- sdfas'\r\n- asdasdf","content_html":"<h1>test</h1>\n<h2>second line</h2>\n<p>---</p>\n<ul>\n<li>list</li>\n<li>sls</li>\n<li>sdfas'</li>\n<li>asdasdf</li>\n</ul>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 15:00:01","updated_at":"2026-04-07 10:16:12","editor_content":"# test\r\n\r\n## second line\r\n\r\n---\r\n\r\n- list\r\n- sls\r\n- sdfas'\r\n- asdasdf","is_text_editable":1}
{"file_name":"SESSION_LOG_2026-04-06.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"8","document_type":"upload","title":"Codex 04-06-2026","slug":"codex-04-06-2026","summary_text":"Codex 04-06-2026 Completed and Documented changes SESSION_LOG_2026-04-06.md","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-06.md","stored_name":"20260406-195518-95396ba2.md","mime_type":"application/octet-stream","file_size_bytes":"23912","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260406-195518-95396ba2.md","is_deleted":"0","created_at":"2026-04-06 15:55:18","updated_at":"2026-04-06 15:55:18"}
{"file_name":"Claude-04-06-2026-updated Documentation.odt","mime_type":"application/vnd.oasis.opendocument.text"}
[]
{"backend_document_id":"7","document_type":"upload","title":"Claude 04-06-2026","slug":"claude-04-06-2026","summary_text":"Claude 04-06-2026 Completed and Updated Documents","content_markdown":null,"content_html":null,"file_name":"Claude-04-06-2026-updated Documentation.odt","stored_name":"20260406-195418-e59c0ad8.odt","mime_type":"application/vnd.oasis.opendocument.text","file_size_bytes":"38724","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260406-195418-e59c0ad8.odt","is_deleted":"0","created_at":"2026-04-06 15:54:18","updated_at":"2026-04-06 15:54:18"}
{"document_type":"markdown"}
{"backend_document_id":"6","document_type":"upload","title":"Claude 04-05-2026 Changes","slug":"claude-04-05-2026-changes","summary_text":"Claude 04-05-2026 Changes","content_markdown":"ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data\r\n\r\nI’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.\r\n\r\nThought for 11s\r\n\r\nYes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.\r\n\r\nThe direct answer is this:\r\n\r\nThe safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:\r\n\r\nBinary logs turned on all the time\r\nA full physical backup every night with mariadb-backup\r\nIncremental backups several times a day\r\nA separate logical dump once or twice a day\r\nCopies of backups pushed to another drive or another machine\r\nRegular restore tests\r\n\r\nThat gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.\r\n\r\nYour real options\r\nOption 1. mariadb-dump\r\n\r\nThis makes SQL dump files.\r\n\r\nGood\r\n\r\nSimple\r\nEasy to restore a whole database or single tables\r\nNice extra safety copy\r\n\r\nBad\r\n\r\nSlower on big databases\r\nRestore takes longer\r\nNot ideal as your only “fresh all day” backup method\r\n\r\nMariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.\r\n\r\nOption 2. mariadb-backup\r\n\r\nThis is the one I would treat as your main production backup.\r\n\r\nGood\r\n\r\nFaster and more serious than plain SQL dumps\r\nDesigned for full and incremental backups\r\nBetter for larger active databases\r\nSupports restore workflows needed for point in time recovery\r\n\r\nBad\r\n\r\nRestore procedure is a little more involved\r\nYou need to prepare and test restores properly\r\n\r\nMariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.\r\n\r\nOption 3. Binary logs for point in time recovery\r\n\r\nThis is what closes the “we just lost the last few hours” gap.\r\n\r\nGood\r\n\r\nLets you recover right up to a specific time\r\nBest protection against losing recent inserts, edits, deletes\r\nEssential if you want recovery between scheduled backups\r\n\r\nBad\r\n\r\nNot a complete backup by itself\r\nMust be combined with a full or incremental backup\r\nNeeds storage management so logs do not grow forever\r\n\r\nMariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.\r\n\r\nOption 4. Replication to another MariaDB server\r\n\r\nThis is more about high availability than backup.\r\n\r\nGood\r\n\r\nExtra live copy\r\nFast failover possibilities\r\n\r\nBad\r\n\r\nIf bad data or accidental deletes replicate, you can lose data on both\r\nStill does not replace backups\r\n\r\nI would only add this later if uptime becomes a bigger deal.\r\n\r\nOption 5. Filesystem snapshots\r\n\r\nThis can be excellent if the server storage is on ZFS, LVM snapshots, or similar.\r\n\r\nGood\r\n\r\nFast\r\nGreat for frequent local rollback points\r\n\r\nBad\r\n\r\nDepends on how your storage is built\r\nStill should not be your only backup\r\nSnapshot alone is not the same as off machine backup\r\nWhat I recommend for your server\r\n\r\nFor ellas_alterations, I would use this exact strategy:\r\n\r\nBest setup\r\n\r\nLayer 1\r\nTurn on binary logging and keep enough logs for several days.\r\n\r\nLayer 2\r\nRun one nightly full backup with mariadb-backup.\r\n\r\nLayer 3\r\nRun incremental backups every 4 hours during the day.\r\n\r\nLayer 4\r\nRun a logical SQL dump twice a day as a second safety net.\r\n\r\nLayer 5\r\nImmediately copy backups to a second location, ideally:\r\n\r\nanother drive like /mnt/drive1 or /mnt/drive2\r\nand even better, another machine on your network\r\n\r\nLayer 6\r\nDo a test restore regularly, even if only to a temp database or test server.\r\n\r\nThat combo is the sweet spot between safe, practical, and not overly crazy.\r\n\r\nWhy this is the best balance\r\n\r\nHere is the logic:\r\n\r\nmariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points\r\nmariadb-backup gives you strong base and incremental backups\r\nbinary logs fill in the gaps between those backups\r\noff server copies protect you if the whole machine dies\r\nrestore tests make sure your backups are not fake confidence\r\n\r\nThat is the difference between “we have backup files” and “we can actually recover.”\r\n\r\nA practical schedule\r\n\r\nHere is a schedule I would use for you:\r\n\r\nEvery night at 1:00 AM\r\n\r\nfull mariadb-backup\r\n\r\nEvery 4 hours\r\n\r\nincremental mariadb-backup\r\n\r\nEvery 12 hours\r\n\r\nmariadb-dump of ellas_alterations\r\n\r\nEvery hour\r\n\r\nsync backup folder to another disk or server\r\n\r\nBinary logs\r\n\r\nalways on\r\nkeep at least 7 to 14 days, depending on space\r\n\r\nIf your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.\r\n\r\nWhat “not lose any data” really means\r\n\r\nI want to be blunt here.\r\n\r\nYou can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.\r\n\r\nFor your setup, the closest practical answer is:\r\n\r\nfull backup nightly\r\nincrementals multiple times a day\r\nbinary logs always on\r\noff machine copies\r\n\r\nThat can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.\r\n\r\nThe simplest strong version\r\n\r\nIf you want the least complicated version that is still solid, do this:\r\n\r\nEnable binary logs\r\nNightly mariadb-backup full backup\r\nMidday mariadb-dump\r\nRsync everything to another drive and another server\r\nTest restoring once\r\n\r\nThat is already way better than what most small businesses do.\r\n\r\nWhat I would do on your machine\r\n\r\nBecause you already use Linux servers and separate storage, I would set it up like this conceptually:\r\n\r\nMariaDB live data stays where it is now\r\nbackups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/\r\nstructure:\r\nfull/\r\nincremental/\r\nsql/\r\nbinlog_archive/\r\nlogs/\r\n\r\nThen automate with cron or systemd timers.\r\n\r\nStep by step reasoning\r\nYour risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”\r\nA single daily dump is too wide a gap.\r\nPhysical backups recover faster than pure SQL dumps.\r\nIncrementals reduce backup load while keeping more recent restore points.\r\nBinary logs are the only practical way to replay changes after the last backup and recover to a chosen time.\r\nOff machine copies protect you from total server loss.\r\nRestore testing proves the backup is real.\r\nOther angles you might not have thought about\r\nKeep backups on a different machine\r\n\r\nIf the server dies, local backups on the same disk can die with it.\r\n\r\nKeep one logical dump even if you use physical backups\r\n\r\nSQL dumps are slower, but they are convenient when you need just one database or one table.\r\n\r\nWatch backup size and retention\r\n\r\nYou do not want binlogs filling the drive and choking the server.\r\n\r\nBack up credentials and config too\r\n\r\nNot just the database files.\r\nAlso save:\r\n\r\nMariaDB config\r\nbackup scripts\r\ncron jobs\r\nbackup user credentials stored securely\r\nTest a restore to a second database name\r\n\r\nA backup you have never restored is just a theory.\r\n\r\nMy recommendation in one sentence\r\n\r\nUse mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.\r\n\r\nAction plan you can apply now\r\nConfirm binary logging is enabled on 192.168.7.202\r\nInstall and use mariadb-backup if it is not already installed\r\nCreate a backup folder on a separate mounted drive\r\nSet nightly full backup\r\nSet 2 hour or 4 hour incremental backups\r\nAdd 1 or 2 daily SQL dumps\r\nSync backup files to another machine or disk\r\nDo one full restore test this week\r\n\r\nI can turn this into an exact setup for your box next, including:\r\n\r\nthe MariaDB config changes\r\nbackup user creation\r\nfolder layout\r\ncron jobs\r\nfull backup script\r\nincremental backup script\r\nSQL dump script\r\nrestore instructions for ellas_alterations","content_html":"<p>ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data</p>\n<p>I’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.</p>\n<p>Thought for 11s</p>\n<p>Yes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.</p>\n<p>The direct answer is this:</p>\n<p>The safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:</p>\n<p>Binary logs turned on all the time</p>\n<p>A full physical backup every night with mariadb-backup</p>\n<p>Incremental backups several times a day</p>\n<p>A separate logical dump once or twice a day</p>\n<p>Copies of backups pushed to another drive or another machine</p>\n<p>Regular restore tests</p>\n<p>That gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.</p>\n<p>Your real options</p>\n<p>Option 1. mariadb-dump</p>\n<p>This makes SQL dump files.</p>\n<p>Good</p>\n<p>Simple</p>\n<p>Easy to restore a whole database or single tables</p>\n<p>Nice extra safety copy</p>\n<p>Bad</p>\n<p>Slower on big databases</p>\n<p>Restore takes longer</p>\n<p>Not ideal as your only “fresh all day” backup method</p>\n<p>MariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.</p>\n<p>Option 2. mariadb-backup</p>\n<p>This is the one I would treat as your main production backup.</p>\n<p>Good</p>\n<p>Faster and more serious than plain SQL dumps</p>\n<p>Designed for full and incremental backups</p>\n<p>Better for larger active databases</p>\n<p>Supports restore workflows needed for point in time recovery</p>\n<p>Bad</p>\n<p>Restore procedure is a little more involved</p>\n<p>You need to prepare and test restores properly</p>\n<p>MariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.</p>\n<p>Option 3. Binary logs for point in time recovery</p>\n<p>This is what closes the “we just lost the last few hours” gap.</p>\n<p>Good</p>\n<p>Lets you recover right up to a specific time</p>\n<p>Best protection against losing recent inserts, edits, deletes</p>\n<p>Essential if you want recovery between scheduled backups</p>\n<p>Bad</p>\n<p>Not a complete backup by itself</p>\n<p>Must be combined with a full or incremental backup</p>\n<p>Needs storage management so logs do not grow forever</p>\n<p>MariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.</p>\n<p>Option 4. Replication to another MariaDB server</p>\n<p>This is more about high availability than backup.</p>\n<p>Good</p>\n<p>Extra live copy</p>\n<p>Fast failover possibilities</p>\n<p>Bad</p>\n<p>If bad data or accidental deletes replicate, you can lose data on both</p>\n<p>Still does not replace backups</p>\n<p>I would only add this later if uptime becomes a bigger deal.</p>\n<p>Option 5. Filesystem snapshots</p>\n<p>This can be excellent if the server storage is on ZFS, LVM snapshots, or similar.</p>\n<p>Good</p>\n<p>Fast</p>\n<p>Great for frequent local rollback points</p>\n<p>Bad</p>\n<p>Depends on how your storage is built</p>\n<p>Still should not be your only backup</p>\n<p>Snapshot alone is not the same as off machine backup</p>\n<p>What I recommend for your server</p>\n<p>For ellas_alterations, I would use this exact strategy:</p>\n<p>Best setup</p>\n<p>Layer 1</p>\n<p>Turn on binary logging and keep enough logs for several days.</p>\n<p>Layer 2</p>\n<p>Run one nightly full backup with mariadb-backup.</p>\n<p>Layer 3</p>\n<p>Run incremental backups every 4 hours during the day.</p>\n<p>Layer 4</p>\n<p>Run a logical SQL dump twice a day as a second safety net.</p>\n<p>Layer 5</p>\n<p>Immediately copy backups to a second location, ideally:</p>\n<p>another drive like /mnt/drive1 or /mnt/drive2</p>\n<p>and even better, another machine on your network</p>\n<p>Layer 6</p>\n<p>Do a test restore regularly, even if only to a temp database or test server.</p>\n<p>That combo is the sweet spot between safe, practical, and not overly crazy.</p>\n<p>Why this is the best balance</p>\n<p>Here is the logic:</p>\n<p>mariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points</p>\n<p>mariadb-backup gives you strong base and incremental backups</p>\n<p>binary logs fill in the gaps between those backups</p>\n<p>off server copies protect you if the whole machine dies</p>\n<p>restore tests make sure your backups are not fake confidence</p>\n<p>That is the difference between “we have backup files” and “we can actually recover.”</p>\n<p>A practical schedule</p>\n<p>Here is a schedule I would use for you:</p>\n<p>Every night at 1:00 AM</p>\n<p>full mariadb-backup</p>\n<p>Every 4 hours</p>\n<p>incremental mariadb-backup</p>\n<p>Every 12 hours</p>\n<p>mariadb-dump of ellas_alterations</p>\n<p>Every hour</p>\n<p>sync backup folder to another disk or server</p>\n<p>Binary logs</p>\n<p>always on</p>\n<p>keep at least 7 to 14 days, depending on space</p>\n<p>If your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.</p>\n<p>What “not lose any data” really means</p>\n<p>I want to be blunt here.</p>\n<p>You can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.</p>\n<p>For your setup, the closest practical answer is:</p>\n<p>full backup nightly</p>\n<p>incrementals multiple times a day</p>\n<p>binary logs always on</p>\n<p>off machine copies</p>\n<p>That can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.</p>\n<p>The simplest strong version</p>\n<p>If you want the least complicated version that is still solid, do this:</p>\n<p>Enable binary logs</p>\n<p>Nightly mariadb-backup full backup</p>\n<p>Midday mariadb-dump</p>\n<p>Rsync everything to another drive and another server</p>\n<p>Test restoring once</p>\n<p>That is already way better than what most small businesses do.</p>\n<p>What I would do on your machine</p>\n<p>Because you already use Linux servers and separate storage, I would set it up like this conceptually:</p>\n<p>MariaDB live data stays where it is now</p>\n<p>backups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/</p>\n<p>structure:</p>\n<p>full/</p>\n<p>incremental/</p>\n<p>sql/</p>\n<p>binlog_archive/</p>\n<p>logs/</p>\n<p>Then automate with cron or systemd timers.</p>\n<p>Step by step reasoning</p>\n<p>Your risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”</p>\n<p>A single daily dump is too wide a gap.</p>\n<p>Physical backups recover faster than pure SQL dumps.</p>\n<p>Incrementals reduce backup load while keeping more recent restore points.</p>\n<p>Binary logs are the only practical way to replay changes after the last backup and recover to a chosen time.</p>\n<p>Off machine copies protect you from total server loss.</p>\n<p>Restore testing proves the backup is real.</p>\n<p>Other angles you might not have thought about</p>\n<p>Keep backups on a different machine</p>\n<p>If the server dies, local backups on the same disk can die with it.</p>\n<p>Keep one logical dump even if you use physical backups</p>\n<p>SQL dumps are slower, but they are convenient when you need just one database or one table.</p>\n<p>Watch backup size and retention</p>\n<p>You do not want binlogs filling the drive and choking the server.</p>\n<p>Back up credentials and config too</p>\n<p>Not just the database files.</p>\n<p>Also save:</p>\n<p>MariaDB config</p>\n<p>backup scripts</p>\n<p>cron jobs</p>\n<p>backup user credentials stored securely</p>\n<p>Test a restore to a second database name</p>\n<p>A backup you have never restored is just a theory.</p>\n<p>My recommendation in one sentence</p>\n<p>Use mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.</p>\n<p>Action plan you can apply now</p>\n<p>Confirm binary logging is enabled on 192.168.7.202</p>\n<p>Install and use mariadb-backup if it is not already installed</p>\n<p>Create a backup folder on a separate mounted drive</p>\n<p>Set nightly full backup</p>\n<p>Set 2 hour or 4 hour incremental backups</p>\n<p>Add 1 or 2 daily SQL dumps</p>\n<p>Sync backup files to another machine or disk</p>\n<p>Do one full restore test this week</p>\n<p>I can turn this into an exact setup for your box next, including:</p>\n<p>the MariaDB config changes</p>\n<p>backup user creation</p>\n<p>folder layout</p>\n<p>cron jobs</p>\n<p>full backup script</p>\n<p>incremental backup script</p>\n<p>SQL dump script</p>\n<p>restore instructions for ellas_alterations</p>","file_name":"SESSION_LOG_2026-04-05.md","stored_name":"20260405-133813-0749951d.md","mime_type":"application/octet-stream","file_size_bytes":"15661","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133813-0749951d.md","is_deleted":"0","created_at":"2026-04-05 09:38:13","updated_at":"2026-04-05 16:00:10"}
{"backend_document_id":"6","document_type":"markdown","title":"202 DB Save","slug":"202-db-save","summary_text":"Claude 04-05-2026 Changes","content_markdown":"ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data\r\n\r\nI’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.\r\n\r\nThought for 11s\r\n\r\nYes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.\r\n\r\nThe direct answer is this:\r\n\r\nThe safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:\r\n\r\nBinary logs turned on all the time\r\nA full physical backup every night with mariadb-backup\r\nIncremental backups several times a day\r\nA separate logical dump once or twice a day\r\nCopies of backups pushed to another drive or another machine\r\nRegular restore tests\r\n\r\nThat gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.\r\n\r\nYour real options\r\nOption 1. mariadb-dump\r\n\r\nThis makes SQL dump files.\r\n\r\nGood\r\n\r\nSimple\r\nEasy to restore a whole database or single tables\r\nNice extra safety copy\r\n\r\nBad\r\n\r\nSlower on big databases\r\nRestore takes longer\r\nNot ideal as your only “fresh all day” backup method\r\n\r\nMariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.\r\n\r\nOption 2. mariadb-backup\r\n\r\nThis is the one I would treat as your main production backup.\r\n\r\nGood\r\n\r\nFaster and more serious than plain SQL dumps\r\nDesigned for full and incremental backups\r\nBetter for larger active databases\r\nSupports restore workflows needed for point in time recovery\r\n\r\nBad\r\n\r\nRestore procedure is a little more involved\r\nYou need to prepare and test restores properly\r\n\r\nMariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.\r\n\r\nOption 3. Binary logs for point in time recovery\r\n\r\nThis is what closes the “we just lost the last few hours” gap.\r\n\r\nGood\r\n\r\nLets you recover right up to a specific time\r\nBest protection against losing recent inserts, edits, deletes\r\nEssential if you want recovery between scheduled backups\r\n\r\nBad\r\n\r\nNot a complete backup by itself\r\nMust be combined with a full or incremental backup\r\nNeeds storage management so logs do not grow forever\r\n\r\nMariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.\r\n\r\nOption 4. Replication to another MariaDB server\r\n\r\nThis is more about high availability than backup.\r\n\r\nGood\r\n\r\nExtra live copy\r\nFast failover possibilities\r\n\r\nBad\r\n\r\nIf bad data or accidental deletes replicate, you can lose data on both\r\nStill does not replace backups\r\n\r\nI would only add this later if uptime becomes a bigger deal.\r\n\r\nOption 5. Filesystem snapshots\r\n\r\nThis can be excellent if the server storage is on ZFS, LVM snapshots, or similar.\r\n\r\nGood\r\n\r\nFast\r\nGreat for frequent local rollback points\r\n\r\nBad\r\n\r\nDepends on how your storage is built\r\nStill should not be your only backup\r\nSnapshot alone is not the same as off machine backup\r\nWhat I recommend for your server\r\n\r\nFor ellas_alterations, I would use this exact strategy:\r\n\r\nBest setup\r\n\r\nLayer 1\r\nTurn on binary logging and keep enough logs for several days.\r\n\r\nLayer 2\r\nRun one nightly full backup with mariadb-backup.\r\n\r\nLayer 3\r\nRun incremental backups every 4 hours during the day.\r\n\r\nLayer 4\r\nRun a logical SQL dump twice a day as a second safety net.\r\n\r\nLayer 5\r\nImmediately copy backups to a second location, ideally:\r\n\r\nanother drive like /mnt/drive1 or /mnt/drive2\r\nand even better, another machine on your network\r\n\r\nLayer 6\r\nDo a test restore regularly, even if only to a temp database or test server.\r\n\r\nThat combo is the sweet spot between safe, practical, and not overly crazy.\r\n\r\nWhy this is the best balance\r\n\r\nHere is the logic:\r\n\r\nmariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points\r\nmariadb-backup gives you strong base and incremental backups\r\nbinary logs fill in the gaps between those backups\r\noff server copies protect you if the whole machine dies\r\nrestore tests make sure your backups are not fake confidence\r\n\r\nThat is the difference between “we have backup files” and “we can actually recover.”\r\n\r\nA practical schedule\r\n\r\nHere is a schedule I would use for you:\r\n\r\nEvery night at 1:00 AM\r\n\r\nfull mariadb-backup\r\n\r\nEvery 4 hours\r\n\r\nincremental mariadb-backup\r\n\r\nEvery 12 hours\r\n\r\nmariadb-dump of ellas_alterations\r\n\r\nEvery hour\r\n\r\nsync backup folder to another disk or server\r\n\r\nBinary logs\r\n\r\nalways on\r\nkeep at least 7 to 14 days, depending on space\r\n\r\nIf your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.\r\n\r\nWhat “not lose any data” really means\r\n\r\nI want to be blunt here.\r\n\r\nYou can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.\r\n\r\nFor your setup, the closest practical answer is:\r\n\r\nfull backup nightly\r\nincrementals multiple times a day\r\nbinary logs always on\r\noff machine copies\r\n\r\nThat can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.\r\n\r\nThe simplest strong version\r\n\r\nIf you want the least complicated version that is still solid, do this:\r\n\r\nEnable binary logs\r\nNightly mariadb-backup full backup\r\nMidday mariadb-dump\r\nRsync everything to another drive and another server\r\nTest restoring once\r\n\r\nThat is already way better than what most small businesses do.\r\n\r\nWhat I would do on your machine\r\n\r\nBecause you already use Linux servers and separate storage, I would set it up like this conceptually:\r\n\r\nMariaDB live data stays where it is now\r\nbackups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/\r\nstructure:\r\nfull/\r\nincremental/\r\nsql/\r\nbinlog_archive/\r\nlogs/\r\n\r\nThen automate with cron or systemd timers.\r\n\r\nStep by step reasoning\r\nYour risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”\r\nA single daily dump is too wide a gap.\r\nPhysical backups recover faster than pure SQL dumps.\r\nIncrementals reduce backup load while keeping more recent restore points.\r\nBinary logs are the only practical way to replay changes after the last backup and recover to a chosen time.\r\nOff machine copies protect you from total server loss.\r\nRestore testing proves the backup is real.\r\nOther angles you might not have thought about\r\nKeep backups on a different machine\r\n\r\nIf the server dies, local backups on the same disk can die with it.\r\n\r\nKeep one logical dump even if you use physical backups\r\n\r\nSQL dumps are slower, but they are convenient when you need just one database or one table.\r\n\r\nWatch backup size and retention\r\n\r\nYou do not want binlogs filling the drive and choking the server.\r\n\r\nBack up credentials and config too\r\n\r\nNot just the database files.\r\nAlso save:\r\n\r\nMariaDB config\r\nbackup scripts\r\ncron jobs\r\nbackup user credentials stored securely\r\nTest a restore to a second database name\r\n\r\nA backup you have never restored is just a theory.\r\n\r\nMy recommendation in one sentence\r\n\r\nUse mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.\r\n\r\nAction plan you can apply now\r\nConfirm binary logging is enabled on 192.168.7.202\r\nInstall and use mariadb-backup if it is not already installed\r\nCreate a backup folder on a separate mounted drive\r\nSet nightly full backup\r\nSet 2 hour or 4 hour incremental backups\r\nAdd 1 or 2 daily SQL dumps\r\nSync backup files to another machine or disk\r\nDo one full restore test this week\r\n\r\nI can turn this into an exact setup for your box next, including:\r\n\r\nthe MariaDB config changes\r\nbackup user creation\r\nfolder layout\r\ncron jobs\r\nfull backup script\r\nincremental backup script\r\nSQL dump script\r\nrestore instructions for ellas_alterations","content_html":"<p>ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data</p>\n<p>I’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.</p>\n<p>Thought for 11s</p>\n<p>Yes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.</p>\n<p>The direct answer is this:</p>\n<p>The safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:</p>\n<p>Binary logs turned on all the time</p>\n<p>A full physical backup every night with mariadb-backup</p>\n<p>Incremental backups several times a day</p>\n<p>A separate logical dump once or twice a day</p>\n<p>Copies of backups pushed to another drive or another machine</p>\n<p>Regular restore tests</p>\n<p>That gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.</p>\n<p>Your real options</p>\n<p>Option 1. mariadb-dump</p>\n<p>This makes SQL dump files.</p>\n<p>Good</p>\n<p>Simple</p>\n<p>Easy to restore a whole database or single tables</p>\n<p>Nice extra safety copy</p>\n<p>Bad</p>\n<p>Slower on big databases</p>\n<p>Restore takes longer</p>\n<p>Not ideal as your only “fresh all day” backup method</p>\n<p>MariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.</p>\n<p>Option 2. mariadb-backup</p>\n<p>This is the one I would treat as your main production backup.</p>\n<p>Good</p>\n<p>Faster and more serious than plain SQL dumps</p>\n<p>Designed for full and incremental backups</p>\n<p>Better for larger active databases</p>\n<p>Supports restore workflows needed for point in time recovery</p>\n<p>Bad</p>\n<p>Restore procedure is a little more involved</p>\n<p>You need to prepare and test restores properly</p>\n<p>MariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.</p>\n<p>Option 3. Binary logs for point in time recovery</p>\n<p>This is what closes the “we just lost the last few hours” gap.</p>\n<p>Good</p>\n<p>Lets you recover right up to a specific time</p>\n<p>Best protection against losing recent inserts, edits, deletes</p>\n<p>Essential if you want recovery between scheduled backups</p>\n<p>Bad</p>\n<p>Not a complete backup by itself</p>\n<p>Must be combined with a full or incremental backup</p>\n<p>Needs storage management so logs do not grow forever</p>\n<p>MariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.</p>\n<p>Option 4. Replication to another MariaDB server</p>\n<p>This is more about high availability than backup.</p>\n<p>Good</p>\n<p>Extra live copy</p>\n<p>Fast failover possibilities</p>\n<p>Bad</p>\n<p>If bad data or accidental deletes replicate, you can lose data on both</p>\n<p>Still does not replace backups</p>\n<p>I would only add this later if uptime becomes a bigger deal.</p>\n<p>Option 5. Filesystem snapshots</p>\n<p>This can be excellent if the server storage is on ZFS, LVM snapshots, or similar.</p>\n<p>Good</p>\n<p>Fast</p>\n<p>Great for frequent local rollback points</p>\n<p>Bad</p>\n<p>Depends on how your storage is built</p>\n<p>Still should not be your only backup</p>\n<p>Snapshot alone is not the same as off machine backup</p>\n<p>What I recommend for your server</p>\n<p>For ellas_alterations, I would use this exact strategy:</p>\n<p>Best setup</p>\n<p>Layer 1</p>\n<p>Turn on binary logging and keep enough logs for several days.</p>\n<p>Layer 2</p>\n<p>Run one nightly full backup with mariadb-backup.</p>\n<p>Layer 3</p>\n<p>Run incremental backups every 4 hours during the day.</p>\n<p>Layer 4</p>\n<p>Run a logical SQL dump twice a day as a second safety net.</p>\n<p>Layer 5</p>\n<p>Immediately copy backups to a second location, ideally:</p>\n<p>another drive like /mnt/drive1 or /mnt/drive2</p>\n<p>and even better, another machine on your network</p>\n<p>Layer 6</p>\n<p>Do a test restore regularly, even if only to a temp database or test server.</p>\n<p>That combo is the sweet spot between safe, practical, and not overly crazy.</p>\n<p>Why this is the best balance</p>\n<p>Here is the logic:</p>\n<p>mariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points</p>\n<p>mariadb-backup gives you strong base and incremental backups</p>\n<p>binary logs fill in the gaps between those backups</p>\n<p>off server copies protect you if the whole machine dies</p>\n<p>restore tests make sure your backups are not fake confidence</p>\n<p>That is the difference between “we have backup files” and “we can actually recover.”</p>\n<p>A practical schedule</p>\n<p>Here is a schedule I would use for you:</p>\n<p>Every night at 1:00 AM</p>\n<p>full mariadb-backup</p>\n<p>Every 4 hours</p>\n<p>incremental mariadb-backup</p>\n<p>Every 12 hours</p>\n<p>mariadb-dump of ellas_alterations</p>\n<p>Every hour</p>\n<p>sync backup folder to another disk or server</p>\n<p>Binary logs</p>\n<p>always on</p>\n<p>keep at least 7 to 14 days, depending on space</p>\n<p>If your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.</p>\n<p>What “not lose any data” really means</p>\n<p>I want to be blunt here.</p>\n<p>You can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.</p>\n<p>For your setup, the closest practical answer is:</p>\n<p>full backup nightly</p>\n<p>incrementals multiple times a day</p>\n<p>binary logs always on</p>\n<p>off machine copies</p>\n<p>That can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.</p>\n<p>The simplest strong version</p>\n<p>If you want the least complicated version that is still solid, do this:</p>\n<p>Enable binary logs</p>\n<p>Nightly mariadb-backup full backup</p>\n<p>Midday mariadb-dump</p>\n<p>Rsync everything to another drive and another server</p>\n<p>Test restoring once</p>\n<p>That is already way better than what most small businesses do.</p>\n<p>What I would do on your machine</p>\n<p>Because you already use Linux servers and separate storage, I would set it up like this conceptually:</p>\n<p>MariaDB live data stays where it is now</p>\n<p>backups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/</p>\n<p>structure:</p>\n<p>full/</p>\n<p>incremental/</p>\n<p>sql/</p>\n<p>binlog_archive/</p>\n<p>logs/</p>\n<p>Then automate with cron or systemd timers.</p>\n<p>Step by step reasoning</p>\n<p>Your risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”</p>\n<p>A single daily dump is too wide a gap.</p>\n<p>Physical backups recover faster than pure SQL dumps.</p>\n<p>Incrementals reduce backup load while keeping more recent restore points.</p>\n<p>Binary logs are the only practical way to replay changes after the last backup and recover to a chosen time.</p>\n<p>Off machine copies protect you from total server loss.</p>\n<p>Restore testing proves the backup is real.</p>\n<p>Other angles you might not have thought about</p>\n<p>Keep backups on a different machine</p>\n<p>If the server dies, local backups on the same disk can die with it.</p>\n<p>Keep one logical dump even if you use physical backups</p>\n<p>SQL dumps are slower, but they are convenient when you need just one database or one table.</p>\n<p>Watch backup size and retention</p>\n<p>You do not want binlogs filling the drive and choking the server.</p>\n<p>Back up credentials and config too</p>\n<p>Not just the database files.</p>\n<p>Also save:</p>\n<p>MariaDB config</p>\n<p>backup scripts</p>\n<p>cron jobs</p>\n<p>backup user credentials stored securely</p>\n<p>Test a restore to a second database name</p>\n<p>A backup you have never restored is just a theory.</p>\n<p>My recommendation in one sentence</p>\n<p>Use mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.</p>\n<p>Action plan you can apply now</p>\n<p>Confirm binary logging is enabled on 192.168.7.202</p>\n<p>Install and use mariadb-backup if it is not already installed</p>\n<p>Create a backup folder on a separate mounted drive</p>\n<p>Set nightly full backup</p>\n<p>Set 2 hour or 4 hour incremental backups</p>\n<p>Add 1 or 2 daily SQL dumps</p>\n<p>Sync backup files to another machine or disk</p>\n<p>Do one full restore test this week</p>\n<p>I can turn this into an exact setup for your box next, including:</p>\n<p>the MariaDB config changes</p>\n<p>backup user creation</p>\n<p>folder layout</p>\n<p>cron jobs</p>\n<p>full backup script</p>\n<p>incremental backup script</p>\n<p>SQL dump script</p>\n<p>restore instructions for ellas_alterations</p>","file_name":"SESSION_LOG_2026-04-05.md","stored_name":"20260405-133813-0749951d.md","mime_type":"application/octet-stream","file_size_bytes":"15661","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133813-0749951d.md","is_deleted":"0","created_at":"2026-04-05 09:38:13","updated_at":"2026-04-05 16:01:53"}
{"document_type":"upload"}
{"backend_document_id":"6","document_type":"upload","title":"Claude 04-05-2026 Changes","slug":"claude-04-05-2026-changes","summary_text":"Claude 04-05-2026 Changes","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-05.md","stored_name":"20260405-133813-0749951d.md","mime_type":"application/octet-stream","file_size_bytes":"15661","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133813-0749951d.md","is_deleted":"0","created_at":"2026-04-05 09:38:13","updated_at":"2026-04-05 09:38:13"}
{"backend_document_id":"6","document_type":"upload","title":"Claude 04-05-2026 Changes","slug":"claude-04-05-2026-changes","summary_text":"Claude 04-05-2026 Changes","content_markdown":"ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data\r\n\r\nI’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.\r\n\r\nThought for 11s\r\n\r\nYes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.\r\n\r\nThe direct answer is this:\r\n\r\nThe safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:\r\n\r\nBinary logs turned on all the time\r\nA full physical backup every night with mariadb-backup\r\nIncremental backups several times a day\r\nA separate logical dump once or twice a day\r\nCopies of backups pushed to another drive or another machine\r\nRegular restore tests\r\n\r\nThat gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.\r\n\r\nYour real options\r\nOption 1. mariadb-dump\r\n\r\nThis makes SQL dump files.\r\n\r\nGood\r\n\r\nSimple\r\nEasy to restore a whole database or single tables\r\nNice extra safety copy\r\n\r\nBad\r\n\r\nSlower on big databases\r\nRestore takes longer\r\nNot ideal as your only “fresh all day” backup method\r\n\r\nMariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.\r\n\r\nOption 2. mariadb-backup\r\n\r\nThis is the one I would treat as your main production backup.\r\n\r\nGood\r\n\r\nFaster and more serious than plain SQL dumps\r\nDesigned for full and incremental backups\r\nBetter for larger active databases\r\nSupports restore workflows needed for point in time recovery\r\n\r\nBad\r\n\r\nRestore procedure is a little more involved\r\nYou need to prepare and test restores properly\r\n\r\nMariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.\r\n\r\nOption 3. Binary logs for point in time recovery\r\n\r\nThis is what closes the “we just lost the last few hours” gap.\r\n\r\nGood\r\n\r\nLets you recover right up to a specific time\r\nBest protection against losing recent inserts, edits, deletes\r\nEssential if you want recovery between scheduled backups\r\n\r\nBad\r\n\r\nNot a complete backup by itself\r\nMust be combined with a full or incremental backup\r\nNeeds storage management so logs do not grow forever\r\n\r\nMariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.\r\n\r\nOption 4. Replication to another MariaDB server\r\n\r\nThis is more about high availability than backup.\r\n\r\nGood\r\n\r\nExtra live copy\r\nFast failover possibilities\r\n\r\nBad\r\n\r\nIf bad data or accidental deletes replicate, you can lose data on both\r\nStill does not replace backups\r\n\r\nI would only add this later if uptime becomes a bigger deal.\r\n\r\nOption 5. Filesystem snapshots\r\n\r\nThis can be excellent if the server storage is on ZFS, LVM snapshots, or similar.\r\n\r\nGood\r\n\r\nFast\r\nGreat for frequent local rollback points\r\n\r\nBad\r\n\r\nDepends on how your storage is built\r\nStill should not be your only backup\r\nSnapshot alone is not the same as off machine backup\r\nWhat I recommend for your server\r\n\r\nFor ellas_alterations, I would use this exact strategy:\r\n\r\nBest setup\r\n\r\nLayer 1\r\nTurn on binary logging and keep enough logs for several days.\r\n\r\nLayer 2\r\nRun one nightly full backup with mariadb-backup.\r\n\r\nLayer 3\r\nRun incremental backups every 4 hours during the day.\r\n\r\nLayer 4\r\nRun a logical SQL dump twice a day as a second safety net.\r\n\r\nLayer 5\r\nImmediately copy backups to a second location, ideally:\r\n\r\nanother drive like /mnt/drive1 or /mnt/drive2\r\nand even better, another machine on your network\r\n\r\nLayer 6\r\nDo a test restore regularly, even if only to a temp database or test server.\r\n\r\nThat combo is the sweet spot between safe, practical, and not overly crazy.\r\n\r\nWhy this is the best balance\r\n\r\nHere is the logic:\r\n\r\nmariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points\r\nmariadb-backup gives you strong base and incremental backups\r\nbinary logs fill in the gaps between those backups\r\noff server copies protect you if the whole machine dies\r\nrestore tests make sure your backups are not fake confidence\r\n\r\nThat is the difference between “we have backup files” and “we can actually recover.”\r\n\r\nA practical schedule\r\n\r\nHere is a schedule I would use for you:\r\n\r\nEvery night at 1:00 AM\r\n\r\nfull mariadb-backup\r\n\r\nEvery 4 hours\r\n\r\nincremental mariadb-backup\r\n\r\nEvery 12 hours\r\n\r\nmariadb-dump of ellas_alterations\r\n\r\nEvery hour\r\n\r\nsync backup folder to another disk or server\r\n\r\nBinary logs\r\n\r\nalways on\r\nkeep at least 7 to 14 days, depending on space\r\n\r\nIf your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.\r\n\r\nWhat “not lose any data” really means\r\n\r\nI want to be blunt here.\r\n\r\nYou can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.\r\n\r\nFor your setup, the closest practical answer is:\r\n\r\nfull backup nightly\r\nincrementals multiple times a day\r\nbinary logs always on\r\noff machine copies\r\n\r\nThat can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.\r\n\r\nThe simplest strong version\r\n\r\nIf you want the least complicated version that is still solid, do this:\r\n\r\nEnable binary logs\r\nNightly mariadb-backup full backup\r\nMidday mariadb-dump\r\nRsync everything to another drive and another server\r\nTest restoring once\r\n\r\nThat is already way better than what most small businesses do.\r\n\r\nWhat I would do on your machine\r\n\r\nBecause you already use Linux servers and separate storage, I would set it up like this conceptually:\r\n\r\nMariaDB live data stays where it is now\r\nbackups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/\r\nstructure:\r\nfull/\r\nincremental/\r\nsql/\r\nbinlog_archive/\r\nlogs/\r\n\r\nThen automate with cron or systemd timers.\r\n\r\nStep by step reasoning\r\nYour risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”\r\nA single daily dump is too wide a gap.\r\nPhysical backups recover faster than pure SQL dumps.\r\nIncrementals reduce backup load while keeping more recent restore points.\r\nBinary logs are the only practical way to replay changes after the last backup and recover to a chosen time.\r\nOff machine copies protect you from total server loss.\r\nRestore testing proves the backup is real.\r\nOther angles you might not have thought about\r\nKeep backups on a different machine\r\n\r\nIf the server dies, local backups on the same disk can die with it.\r\n\r\nKeep one logical dump even if you use physical backups\r\n\r\nSQL dumps are slower, but they are convenient when you need just one database or one table.\r\n\r\nWatch backup size and retention\r\n\r\nYou do not want binlogs filling the drive and choking the server.\r\n\r\nBack up credentials and config too\r\n\r\nNot just the database files.\r\nAlso save:\r\n\r\nMariaDB config\r\nbackup scripts\r\ncron jobs\r\nbackup user credentials stored securely\r\nTest a restore to a second database name\r\n\r\nA backup you have never restored is just a theory.\r\n\r\nMy recommendation in one sentence\r\n\r\nUse mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.\r\n\r\nAction plan you can apply now\r\nConfirm binary logging is enabled on 192.168.7.202\r\nInstall and use mariadb-backup if it is not already installed\r\nCreate a backup folder on a separate mounted drive\r\nSet nightly full backup\r\nSet 2 hour or 4 hour incremental backups\r\nAdd 1 or 2 daily SQL dumps\r\nSync backup files to another machine or disk\r\nDo one full restore test this week\r\n\r\nI can turn this into an exact setup for your box next, including:\r\n\r\nthe MariaDB config changes\r\nbackup user creation\r\nfolder layout\r\ncron jobs\r\nfull backup script\r\nincremental backup script\r\nSQL dump script\r\nrestore instructions for ellas_alterations","content_html":"<p>ok ssh kefa@192.168.7.202 my MariaDB ellas_alterations i need a way to back this data up multiple times a day what are my options so we always have a fresh backup of data and not lose any data</p>\n<p>I’m checking the current MariaDB backup options so I can give you something solid, not hand waving. Then I’ll turn that into a setup that fits your 192.168.7.202 box and your “multiple backups a day, fresh data, don’t lose work” goal.</p>\n<p>Thought for 11s</p>\n<p>Yes. You absolutely can set this up so you are not relying on one lonely nightly dump and hoping for the best.</p>\n<p>The direct answer is this:</p>\n<p>The safest practical setup for your MariaDB on 192.168.7.202 is a layered backup plan:</p>\n<p>Binary logs turned on all the time</p>\n<p>A full physical backup every night with mariadb-backup</p>\n<p>Incremental backups several times a day</p>\n<p>A separate logical dump once or twice a day</p>\n<p>Copies of backups pushed to another drive or another machine</p>\n<p>Regular restore tests</p>\n<p>That gives you fresh backups, fast recovery, and a way to restore to a specific point in time instead of just “whatever the last dump was.” MariaDB’s own docs distinguish logical backups from physical backups, recommend mariadb-backup for production style physical backups, and explain that point in time recovery uses a restored backup plus binary logs. The binary log records database changes, which is exactly what you want if your goal is to avoid losing recent data.</p>\n<p>Your real options</p>\n<p>Option 1. mariadb-dump</p>\n<p>This makes SQL dump files.</p>\n<p>Good</p>\n<p>Simple</p>\n<p>Easy to restore a whole database or single tables</p>\n<p>Nice extra safety copy</p>\n<p>Bad</p>\n<p>Slower on big databases</p>\n<p>Restore takes longer</p>\n<p>Not ideal as your only “fresh all day” backup method</p>\n<p>MariaDB documents mariadb-dump as the logical backup tool, and logical backups recreate data using SQL statements like CREATE TABLE and INSERT.</p>\n<p>Option 2. mariadb-backup</p>\n<p>This is the one I would treat as your main production backup.</p>\n<p>Good</p>\n<p>Faster and more serious than plain SQL dumps</p>\n<p>Designed for full and incremental backups</p>\n<p>Better for larger active databases</p>\n<p>Supports restore workflows needed for point in time recovery</p>\n<p>Bad</p>\n<p>Restore procedure is a little more involved</p>\n<p>You need to prepare and test restores properly</p>\n<p>MariaDB’s docs say mariadb-backup supports full and incremental backups, and that backups must be prepared before restore.</p>\n<p>Option 3. Binary logs for point in time recovery</p>\n<p>This is what closes the “we just lost the last few hours” gap.</p>\n<p>Good</p>\n<p>Lets you recover right up to a specific time</p>\n<p>Best protection against losing recent inserts, edits, deletes</p>\n<p>Essential if you want recovery between scheduled backups</p>\n<p>Bad</p>\n<p>Not a complete backup by itself</p>\n<p>Must be combined with a full or incremental backup</p>\n<p>Needs storage management so logs do not grow forever</p>\n<p>MariaDB states that point in time recovery restores a base backup first, then replays binary logs with mysqlbinlog, and that the binary log contains records of data changes.</p>\n<p>Option 4. Replication to another MariaDB server</p>\n<p>This is more about high availability than backup.</p>\n<p>Good</p>\n<p>Extra live copy</p>\n<p>Fast failover possibilities</p>\n<p>Bad</p>\n<p>If bad data or accidental deletes replicate, you can lose data on both</p>\n<p>Still does not replace backups</p>\n<p>I would only add this later if uptime becomes a bigger deal.</p>\n<p>Option 5. Filesystem snapshots</p>\n<p>This can be excellent if the server storage is on ZFS, LVM snapshots, or similar.</p>\n<p>Good</p>\n<p>Fast</p>\n<p>Great for frequent local rollback points</p>\n<p>Bad</p>\n<p>Depends on how your storage is built</p>\n<p>Still should not be your only backup</p>\n<p>Snapshot alone is not the same as off machine backup</p>\n<p>What I recommend for your server</p>\n<p>For ellas_alterations, I would use this exact strategy:</p>\n<p>Best setup</p>\n<p>Layer 1</p>\n<p>Turn on binary logging and keep enough logs for several days.</p>\n<p>Layer 2</p>\n<p>Run one nightly full backup with mariadb-backup.</p>\n<p>Layer 3</p>\n<p>Run incremental backups every 4 hours during the day.</p>\n<p>Layer 4</p>\n<p>Run a logical SQL dump twice a day as a second safety net.</p>\n<p>Layer 5</p>\n<p>Immediately copy backups to a second location, ideally:</p>\n<p>another drive like /mnt/drive1 or /mnt/drive2</p>\n<p>and even better, another machine on your network</p>\n<p>Layer 6</p>\n<p>Do a test restore regularly, even if only to a temp database or test server.</p>\n<p>That combo is the sweet spot between safe, practical, and not overly crazy.</p>\n<p>Why this is the best balance</p>\n<p>Here is the logic:</p>\n<p>mariadb-dump alone is easy but too slow and too coarse if you want frequent fresh recovery points</p>\n<p>mariadb-backup gives you strong base and incremental backups</p>\n<p>binary logs fill in the gaps between those backups</p>\n<p>off server copies protect you if the whole machine dies</p>\n<p>restore tests make sure your backups are not fake confidence</p>\n<p>That is the difference between “we have backup files” and “we can actually recover.”</p>\n<p>A practical schedule</p>\n<p>Here is a schedule I would use for you:</p>\n<p>Every night at 1:00 AM</p>\n<p>full mariadb-backup</p>\n<p>Every 4 hours</p>\n<p>incremental mariadb-backup</p>\n<p>Every 12 hours</p>\n<p>mariadb-dump of ellas_alterations</p>\n<p>Every hour</p>\n<p>sync backup folder to another disk or server</p>\n<p>Binary logs</p>\n<p>always on</p>\n<p>keep at least 7 to 14 days, depending on space</p>\n<p>If your business data changes constantly and you are nervous about same day loss, make the incrementals every 2 hours instead of every 4.</p>\n<p>What “not lose any data” really means</p>\n<p>I want to be blunt here.</p>\n<p>You can get very close to no data loss, but true zero loss is hard unless you move into high availability, synchronous replication, battery backed storage, and more advanced infrastructure.</p>\n<p>For your setup, the closest practical answer is:</p>\n<p>full backup nightly</p>\n<p>incrementals multiple times a day</p>\n<p>binary logs always on</p>\n<p>off machine copies</p>\n<p>That can reduce your recovery point objective from many hours down to minutes, depending on how current your binlogs and copied backup files are. MariaDB’s point in time recovery model is specifically built for this kind of restoration.</p>\n<p>The simplest strong version</p>\n<p>If you want the least complicated version that is still solid, do this:</p>\n<p>Enable binary logs</p>\n<p>Nightly mariadb-backup full backup</p>\n<p>Midday mariadb-dump</p>\n<p>Rsync everything to another drive and another server</p>\n<p>Test restoring once</p>\n<p>That is already way better than what most small businesses do.</p>\n<p>What I would do on your machine</p>\n<p>Because you already use Linux servers and separate storage, I would set it up like this conceptually:</p>\n<p>MariaDB live data stays where it is now</p>\n<p>backups go to something like /mnt/drive1/mariadb_backups/ellas_alterations/</p>\n<p>structure:</p>\n<p>full/</p>\n<p>incremental/</p>\n<p>sql/</p>\n<p>binlog_archive/</p>\n<p>logs/</p>\n<p>Then automate with cron or systemd timers.</p>\n<p>Step by step reasoning</p>\n<p>Your risk is not just server failure. It is also accidental delete, bad update, corruption, and “oops we changed the wrong record.”</p>\n<p>A single daily dump is too wide a gap.</p>\n<p>Physical backups recover faster than pure SQL dumps.</p>\n<p>Incrementals reduce backup load while keeping more recent restore points.</p>\n<p>Binary logs are the only practical way to replay changes after the last backup and recover to a chosen time.</p>\n<p>Off machine copies protect you from total server loss.</p>\n<p>Restore testing proves the backup is real.</p>\n<p>Other angles you might not have thought about</p>\n<p>Keep backups on a different machine</p>\n<p>If the server dies, local backups on the same disk can die with it.</p>\n<p>Keep one logical dump even if you use physical backups</p>\n<p>SQL dumps are slower, but they are convenient when you need just one database or one table.</p>\n<p>Watch backup size and retention</p>\n<p>You do not want binlogs filling the drive and choking the server.</p>\n<p>Back up credentials and config too</p>\n<p>Not just the database files.</p>\n<p>Also save:</p>\n<p>MariaDB config</p>\n<p>backup scripts</p>\n<p>cron jobs</p>\n<p>backup user credentials stored securely</p>\n<p>Test a restore to a second database name</p>\n<p>A backup you have never restored is just a theory.</p>\n<p>My recommendation in one sentence</p>\n<p>Use mariadb-backup full plus incrementals as your main backup system, keep binary logs enabled for point in time recovery, add a daily mariadb-dump as a second format, and sync all backups off the server automatically.</p>\n<p>Action plan you can apply now</p>\n<p>Confirm binary logging is enabled on 192.168.7.202</p>\n<p>Install and use mariadb-backup if it is not already installed</p>\n<p>Create a backup folder on a separate mounted drive</p>\n<p>Set nightly full backup</p>\n<p>Set 2 hour or 4 hour incremental backups</p>\n<p>Add 1 or 2 daily SQL dumps</p>\n<p>Sync backup files to another machine or disk</p>\n<p>Do one full restore test this week</p>\n<p>I can turn this into an exact setup for your box next, including:</p>\n<p>the MariaDB config changes</p>\n<p>backup user creation</p>\n<p>folder layout</p>\n<p>cron jobs</p>\n<p>full backup script</p>\n<p>incremental backup script</p>\n<p>SQL dump script</p>\n<p>restore instructions for ellas_alterations</p>","file_name":"SESSION_LOG_2026-04-05.md","stored_name":"20260405-133813-0749951d.md","mime_type":"application/octet-stream","file_size_bytes":"15661","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133813-0749951d.md","is_deleted":"0","created_at":"2026-04-05 09:38:13","updated_at":"2026-04-05 16:00:10"}
{"file_name":"SESSION_LOG_2026-04-05.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"6","document_type":"upload","title":"Claude 04-05-2026 Changes","slug":"claude-04-05-2026-changes","summary_text":"Claude 04-05-2026 Changes","content_markdown":null,"content_html":null,"file_name":"SESSION_LOG_2026-04-05.md","stored_name":"20260405-133813-0749951d.md","mime_type":"application/octet-stream","file_size_bytes":"15661","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133813-0749951d.md","is_deleted":"0","created_at":"2026-04-05 09:38:13","updated_at":"2026-04-05 09:38:13"}
{"file_name":"MORNING_CHANGES_2026-04-05.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"5","document_type":"upload","title":"Codex 04-05-2026 Changes","slug":"codex-04-05-2026-changes","summary_text":"Codex 04-05-2026 Changes","content_markdown":null,"content_html":null,"file_name":"MORNING_CHANGES_2026-04-05.md","stored_name":"20260405-133729-718b7452.md","mime_type":"application/octet-stream","file_size_bytes":"7821","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-133729-718b7452.md","is_deleted":"0","created_at":"2026-04-05 09:37:29","updated_at":"2026-04-05 09:37:29"}
{"file_name":"PROJECT_STATUS.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"4","document_type":"upload","title":"04-04-2026 All Claude Documentation","slug":"04-04-2026-all-claude-documentation","summary_text":"04-04-2026 All Claude Documentation","content_markdown":null,"content_html":null,"file_name":"PROJECT_STATUS.md","stored_name":"20260405-122933-c684516a.md","mime_type":"application/octet-stream","file_size_bytes":"61168","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-122933-c684516a.md","is_deleted":"0","created_at":"2026-04-05 08:29:33","updated_at":"2026-04-05 08:29:33"}
{"file_name":"IMPLEMENTATION_STATUS_AND_TEST_PLAN_2026-04-04.md","mime_type":"application/octet-stream"}
[]
{"backend_document_id":"3","document_type":"upload","title":"04-04-2026 All Codex Documentation","slug":"04-04-2026-all-codex-documentation","summary_text":"04-04-2026 All Codex Documentation","content_markdown":null,"content_html":null,"file_name":"IMPLEMENTATION_STATUS_AND_TEST_PLAN_2026-04-04.md","stored_name":"20260405-122838-08b37e3f.md","mime_type":"application/octet-stream","file_size_bytes":"28700","storage_path":"/mnt/drive1/customerdb/backend/documents_storage/20260405-122838-08b37e3f.md","is_deleted":"0","created_at":"2026-04-05 08:28:38","updated_at":"2026-04-05 08:28:38"}
{"document_type":"markdown"}
{"backend_document_id":"2","document_type":"markdown","title":"Test Note","slug":"test-note","summary_text":"This is test note summary","content_markdown":"On the settings side, settings.html now has a slideshow image manager for customerdb/webui/slideshow/ImageFiles, and I added slideshow_list.php, slideshow_upload.php, and slideshow_delete.php. I also fixed the webui settings endpoints so theme_font_size, theme_font_weight, theme_heading_weight, and nightly_report_to_email are saved and applied in settings_save.php, settings_get.php, and settings_css.php.\r\n\r\nWhat to test now:\r\n\r\nOpen https://ella.floridaalterations.com/backend/ and confirm the new Open Documents and View Logs links are there.\r\nOpen https://ella.floridaalterations.com/backend/documents.php, create a markdown doc, edit it, upload a file, and delete one document.","content_html":"<p>On the settings side, settings.html now has a slideshow image manager for customerdb/webui/slideshow/ImageFiles, and I added slideshow_list.php, slideshow_upload.php, and slideshow_delete.php. I also fixed the webui settings endpoints so theme_font_size, theme_font_weight, theme_heading_weight, and nightly_report_to_email are saved and applied in settings_save.php, settings_get.php, and settings_css.php.</p>\n<p>What to test now:</p>\n<p>Open https://ella.floridaalterations.com/backend/ and confirm the new Open Documents and View Logs links are there.</p>\n<p>Open https://ella.floridaalterations.com/backend/documents.php, create a markdown doc, edit it, upload a file, and delete one document.</p>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 15:00:01","updated_at":"2026-04-02 15:00:01"}
{"backend_document_id":"2","document_type":"markdown","title":"Test Note","slug":"test-note","summary_text":"This is test note summary","content_markdown":"# test\r\n\r\n## second line\r\n\r\n---\r\n\r\n- list\r\n- sls\r\n- sdfas'\r\n- asdasdf","content_html":"<h1>test</h1>\n<h2>second line</h2>\n<p>---</p>\n<ul>\n<li>list</li>\n<li>sls</li>\n<li>sdfas'</li>\n<li>asdasdf</li>\n</ul>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 15:00:01","updated_at":"2026-04-03 08:04:41"}
{"document_type":"markdown"}
[]
{"backend_document_id":"2","document_type":"markdown","title":"Test Note","slug":"test-note","summary_text":"This is test note summary","content_markdown":"On the settings side, settings.html now has a slideshow image manager for customerdb/webui/slideshow/ImageFiles, and I added slideshow_list.php, slideshow_upload.php, and slideshow_delete.php. I also fixed the webui settings endpoints so theme_font_size, theme_font_weight, theme_heading_weight, and nightly_report_to_email are saved and applied in settings_save.php, settings_get.php, and settings_css.php.\r\n\r\nWhat to test now:\r\n\r\nOpen https://ella.floridaalterations.com/backend/ and confirm the new Open Documents and View Logs links are there.\r\nOpen https://ella.floridaalterations.com/backend/documents.php, create a markdown doc, edit it, upload a file, and delete one document.","content_html":"<p>On the settings side, settings.html now has a slideshow image manager for customerdb/webui/slideshow/ImageFiles, and I added slideshow_list.php, slideshow_upload.php, and slideshow_delete.php. I also fixed the webui settings endpoints so theme_font_size, theme_font_weight, theme_heading_weight, and nightly_report_to_email are saved and applied in settings_save.php, settings_get.php, and settings_css.php.</p>\n<p>What to test now:</p>\n<p>Open https://ella.floridaalterations.com/backend/ and confirm the new Open Documents and View Logs links are there.</p>\n<p>Open https://ella.floridaalterations.com/backend/documents.php, create a markdown doc, edit it, upload a file, and delete one document.</p>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 15:00:01","updated_at":"2026-04-02 15:00:01"}
[]
{"backend_document_id":"1","document_type":"markdown","title":"Backend Smoke Test","slug":"backend-smoke-test","summary_text":null,"content_markdown":"# Test\n\n- one\n- two","content_html":"<h1>Test</h1>\n<ul>\n<li>one</li>\n<li>two</li>\n</ul>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 12:40:21","updated_at":"2026-04-02 12:40:21"}
{"deleted":true}
{"document_type":"markdown"}
[]
{"backend_document_id":"1","document_type":"markdown","title":"Backend Smoke Test","slug":"backend-smoke-test","summary_text":null,"content_markdown":"# Test\n\n- one\n- two","content_html":"<h1>Test</h1>\n<ul>\n<li>one</li>\n<li>two</li>\n</ul>","file_name":null,"stored_name":null,"mime_type":null,"file_size_bytes":null,"storage_path":null,"is_deleted":"0","created_at":"2026-04-02 12:40:21","updated_at":"2026-04-02 12:40:21"}