Commit Graph

7511 Commits

Author SHA1 Message Date
johnnyq
20cc3a9cf3 Speed up the dashboard's Hardware not on assets count and the invoices list's hardware filter: productHardwarePending and the hardware filter in agent/invoices.php had a COUNT(*) subquery a line on assets by the line's invoice and line, and where nearly every asset has asset_invoice_id 0 the index statistics say a lookup finds every asset, so the database read the whole assets table once a hardware line, 1.89 seconds and 3.1 million rows on a forum install and 22.9 seconds and 185 million rows on a test install with 25,000 assets none from an invoice; both now count each line's assets once and join them through the new productHardwareMadeJoinSql, a LEFT JOIN onto invoice_items of assets with asset_invoice_id > 0 grouped by invoice and line, a range the database measures on the index itself, with productHardwareShortSql comparing against hardware_made.made; run with none, 3,000 and 21,000 of 25,000 assets made from an invoice, the same counts as before each time and 18 to 32 ms; the lookups by one invoice give the database a constant and are unchanged; add both to the 26.11.8 changelog section 2026-11-13 12:18:21 -05:00
johnnyq
d14788a4f1 SLAs page wording: name the report SLA Performance, as the reports list does, in the menu beside New SLA and in the help card where it said SLA Summary; correct the archive confirmation, which said tickets under a contract that gave the SLA get no SLA from then on, when they fall back to the default for their priority and only a priority the SLA was the default for gets none; join the priorities with and in that sentence 2026-11-13 03:04:21 -05:00
johnnyq
bf7f6d476b Word contract SLA targets as the SLA clock counts them: slaMinutesText now gives formatSlaMinutes in business time, so the contract page, the contract and template windows, the contract document and its PDF read 4 business hours and 6 business days where they read 4 hrs and 2 days; add an exact option to formatSlaMinutes that drops no remainder, used by contracts and Admin > SLAs so both word a target the same, with the emails unchanged; keep the old wording only for comparing fingerprints through the new contractDocumentIs, so a contract signed or out for signature before this is not marked as changed or withdrawn by the rewording alone and keeps the text it was sent with; a document now depends on the business hours wherever a target is a business day or more; changelog entries for 26.11.8 including an Upgrading note to check signed copies; no database change 2026-11-13 02:41:53 -05:00
johnnyq
be50f92fe8 Rebuild Admin > SLAs on the standard list pattern: the SLAs are a searchable, sortable, paged list with Active and Archived views and the settings forms beside it, and each SLA shows its targets in business time with the hours typed under it, the priorities it is the default for and what uses it (open tickets, contracts and contract templates); closure days become views of the same list, coming up and past, searched by name, date, month or weekday, and Add US Holidays is a window that says how many of each year are already in; the SLA window takes targets as hours and minutes and refuses a missing name, a missing response target or a resolution target shorter than the response target; archiving says what stops getting the SLA and an archived default is shown as archived; Default SLA by priority uses the select picker, Business hours and alerts has day buttons and warns when cron is not running, and an On tickets now strip links to the tickets close to or past a target; fix business hours that end before they start being accepted and running the SLA clocks around the clock, keep the warning percent within 1 to 99, word the save messages as tickets not yet closed, and fix the backslash shown after saving an SLA with an apostrophe in its name; add slaBusinessDayMinutes, formatSlaClockMinutes and slaTargetMinutes to functions/sla.php; no database change 2026-11-13 02:25:09 -05:00
johnnyq
dfb0298245 Rebuild Maintenance > Cron on the Update and Backup pages' pattern: a status card at the top says cron is running, when it last ran, how many jobs are on and which runs next, or instead that cron has never run or has stopped, with the crontab line and a copy button, that it is switched off, with Turn cron on, that a managed instance's jobs are paused by its manager, or that a job failed, by name; the jobs are listed with an icon, what each does, its schedule and next run, its last run and how long it took, and a status of Queued, Running, Completed, Failed, Skipped, Paused, Not run yet or Didn't finish for one still running six hours on, with the reason a run was skipped under the job and a kept error shown until dismissed, marked as having run since once it has; the list sorts in PHP on the standard sort and order by job, schedule, last run or status, starting by schedule, with no pager or search as it is a fixed list of at most fifteen; a job's menu has Run now, Change the schedule, Copy the command to run it and Turn off, which asks first; the page reloads itself every 10 seconds while a job is queued or running and every 15 while cron has not been heard from; the side column has At a glance, the cron line with a copy button and Turn cron off, which asks first; includes/cron_jobs.php gives each job an icon and drops cronJobTimeAgo, which nothing uses now; the schedule window opens with what the job does, uses the select picker and names the time zone; admin/post/cron.php words its messages as the buttons are worded, with the audit log unchanged; js/auto_reload.js holds a reload while a window, menu or confirmation is open, a field has focus or text is selected, which also stops the Backup page reloading away its Change the time window or what is typed into its retention boxes; CONTRIBUTING says to give a new job its icon; no database change and no new CSS; changelog entries added to 26.11.8 2026-11-13 01:51:34 -05:00
johnnyq
dd160ef1d7 Rebuild Maintenance > Backup on the Update page's pattern: a status card at the top says when the last backup was made, what kind and when the next scheduled one is due, or instead that one is being made, the last one failed and why, the last good one is over 7 days old, none has been made yet, a queued one is stuck because cron isn't running, or there is no encryption key, with Back up now and Database only under it, and the page reloads itself every 10 seconds while a backup is being made except while an exported master key is showing; the backups list uses the standard list plumbing with sortable headers on date, type, size and status and the standard pager, no longer stops at the newest 100, shows who made each backup or that the schedule did, when it was last downloaded and why one failed, and a row menu copies its path on the server and its SHA-256 checksum, deletes a finished backup, takes a queued one off the queue or removes a failed or missing one, with the card left out when there are no backups; the schedule and retention card shows whether the Backup cron job is on and at what time, with Turn on and Change the time using the Cron page's enable_cron_job handler and cron_edit window; an At a glance column has the last Full Backup and Database Only, the schedule, retention, how many are kept and their size, free space, cron status and the backup folder; the encryption key is masked behind the eye with a copy button; the master key export moves to a new ajax modal, admin/modals/backup/backup_master_key.php, which asks for the password and says an encrypted copy is filed with the backups and every agent is notified, where the old window said the key was not written anywhere; delete_backup in admin/post/backup.php words its audit entry and message by the backup's status; no new CSS or JS; changelog entries added to 26.11.8 2026-11-13 01:14:10 -05:00
johnnyq
755e488d6c Add and change lines on invoices, quotes and recurring invoices on the page itself, without a reload: the row of boxes under the lines is now one field - type a product and Enter takes the one picked out, adding it as a line with its description, price and tax, billed as the product usually is on a quote and counted per user or per device on a recurring invoice, or type anything and pick the Add row for a line of its own - and the new line opens with its quantity ready to type over, Enter moving on to a price it still needs and then back to the field; a line name, description, quantity, price and tax are changed where they sit, by a click or by Tab and Enter, saved on Enter or on leaving, put back by Escape, with the tax picked from the select picker and a counted quantity left to the Edit Line window; a quote line menu gains Billed, one time to yearly; agent/ajax.php gains billing_item_add and billing_item_set with the checks post.php billing_item_save makes, answering JSON, and functions/billing.php gains billingItemSetField, which replaces one value and hands the line to billingItemSave so stock, tax and totals follow as before, saves nothing when the value is the same, and gives the audit log what changed; js/billing_items.js is rewritten to send saves one at a time in order and then fetch the page again, taking from it the lines by id and every element marked data-billing-live - the totals and amount due in includes/billing_paper.php, the header card, tax warning and side column on the three pages - so nothing is drawn twice, a field in use is left alone, and a document no longer open for changes reloads; the paper carries the products as JSON and the tax options, and itflowBillingItems no longer takes products; js/autocomplete.js gains pickFirst and an extra row kept in view under the list, and opens above the field when the list does not fit below; getProductsForAutocomplete encodes with JSON_HEX_TAG and JSON_HEX_AMP; the guest and client portal pages are unchanged; add a 26.11.8 changelog section 2026-11-12 16:14:30 -05:00
johnnyq
351a3f9b9c update itflow version and changelog 2026-11-11 21:47:01 -05:00
johnnyq
2c9bd0a7f2 Let the database update get past 3.2.8 on installs whose tables have different collations, and on ones already stuck there: 3.2.8 matched license names against template names across software and software_templates, the second being one of the tables 2.1.6 to 2.4.1 created without a COLLATE, so where the server default is not utf8mb4_general_ci, MariaDB 11.5 and later, the comparison failed with Illegal mix of collations after the two ALTERs above it had gone in, and every run after that stopped at the first of them with Duplicate column name software_template_id, short of 3.6.5 which puts those tables right; admin/database_updates.php now runs databaseCollationRepair before each update it applies as well as after the last, so no update meets two collations whatever version it starts from, loads functions/database_collation.php itself when the process started on code from before it, and does not try a table again in the same run once it has failed, databaseCollationRepair taking a list of tables to skip; 3.2.8.php is changed in what it can get through and not in what it makes, each ALTER checked first so it can run again from wherever it stopped, the comparison naming its collation and the UPDATE leaving licenses that already have a template; every update from 2.0.7 to 3.6.5 was run on MariaDB with those 22 tables in utf8mb4_uca1400_ai_ci and 3.2.8 was the only one that failed, then the whole history run through the new runner with the tables put wrong before every update, on an install left as the forum report had it and from a process started on older code; CONTRIBUTING.md records the exception to never editing a shipped migration and the 26.11.7 changelog section has the fix 2026-11-11 21:13:02 -05:00
johnnyq
69fd43051f Keep collations at utf8mb4_general_ci going forward: functions.php now ends by calling databaseConnectionCollation, which runs SET collation_connection = 'utf8mb4_general_ci' on the connection config.php opened, so a string the server makes itself, CAST(x AS CHAR) for one, matches the tables where on MySQL 8 it came out utf8mb4_0900_ai_ci and failed beside a column with Illegal mix of collations, on a new install as much as an upgraded one; only the collation is set and not the character set, no SET NAMES and no mysqli_set_charset, because on a server that defaults to latin1 the connection has always been latin1 and changing it shows every accented letter already stored as two wrong ones, where setting collation_connection alone leaves what is stored and what is stored after the same; admin/database_updates.php calls databaseCollationRepair after a run that applied at least one database update and finished, so a table a migration creates without a COLLATE is given the collation db.sql has in the same run, with what it changed and what it could not going to App Logs and to the output of scripts/update_cli.php, a table it cannot change left as it was and never failing the update, and a run that stops at a failed migration leaving this for the run that finishes; run on MySQL 8.0 and MariaDB 10.11, on a latin1 server default and through update_cli.php --update_db with a migration written as 2.4.1 was; CONTRIBUTING.md and the 26.11.7 changelog section say so 2026-11-11 20:52:46 -05:00
johnnyq
bce68aac6a Add a collation check and repair to Admin > Debug: functions/database_collation.php reads from db.sql each table's default collation and each text column's, compares them with information_schema and can give every table that differs the collation db.sql has for it, one ALTER a table with a MODIFY a column built from its SHOW CREATE TABLE line with only the collation replaced, as database 3.6.5 does, so a binary column that was flattened goes back to utf8mb4_bin too and no column type changes; the ITFlow section gains a Database collation check that warns when any table differs, and the Database card lists them under Structure a row a table, in Copy for support as well, with a Repair collations button behind a confirm; the button posts to the new admin/post/debug.php, CSRF-checked and not tied to the request so a large table is not cut off with the browser, which writes an audit log entry naming the tables changed and a warning in App Logs for each one it had to leave, a table being changed whole or not at all; tables and columns db.sql does not have are not looked at; run through the page and against the whole schema on MySQL 8.0 and MariaDB 10.11; add both to the 26.11.7 changelog section 2026-11-11 20:33:19 -05:00
johnnyq
81ea84b71e Bring every table to one collation on upgraded installs: database 3.6.5 changes any ITFlow table whose default collation or any text column is not utf8mb4_general_ci, in practice the 22 tables that 2.1.6 to 2.4.1 created without a COLLATE and that took the server default, utf8mb4_0900_ai_ci on MySQL 8 and utf8mb4_uca1400_ai_ci on MariaDB 11.5 and later, which is what made the reprice UNION fail and the dashboard come up blank; it reads information_schema so a table already right is not touched and a second run changes nothing, and db.sql is unchanged; each table is one ALTER of MODIFYs built from its own SHOW CREATE TABLE lines with only the collation replaced, in place of CONVERT TO CHARACTER SET as 2.0.6 used, so calendars.calendar_feed_key and user_calendar_feeds.feed_key stay utf8mb4_bin and no column type changes; columns with a binary collation and tables that are not ITFlow's are left alone; NO_ZERO_DATE and NO_ZERO_IN_DATE are lifted for the connection while it runs because records.record_updated_at defaults to a zero date and MySQL 8 otherwise refuses any ALTER of that table; a table that cannot be changed is left as it was with a warning in App Logs and the PHP error log and the update carries on; run on MySQL 8.0 and MariaDB 10.11 against the whole schema with a row in every table; CONTRIBUTING.md now says a CREATE TABLE in a migration ends with the COLLATE; update the 26.11.7 changelog section 2026-11-11 20:21:03 -05:00
johnnyq
a58c10fba6 Fix the blank dashboard on upgraded installs whose item tables have different collations: productRepriceUnionSql gives every text column of its three branches one collation with CONVERT USING utf8mb4 and COLLATE utf8mb4_general_ci, where database 2.4.1 made quote_items and recurring_invoice_items with no COLLATE so on a server whose default is not utf8mb4_general_ci, such as MySQL 8 with utf8mb4_0900_ai_ci, item_name differed from invoice_items and the UNION ALL threw Illegal mix of collations from productRepriceCount, ending the dashboard at its attention list and leaving the user menu dead, and doing the same on the products list, a product page and draft invoices, quotes and recurring invoices; the tables themselves are left as they are; add the fix to the 26.11.7 changelog section 2026-11-11 20:05:38 -05:00
johnnyq
5ba8ffc0cf Speed up the dashboard and the menu counts on installs with years of history: database 3.6.4 adds seven indexes, each only where it is missing and also in db.sql for new installs - tickets on ticket_resolved_at, ticket_closed_at and ticket_created_at, a five-column one led by ticket_billable that answers the unbilled count from the index alone, ticket_replies on ticket_reply_by with ticket_reply_created_at, invoices on invoice_status and expenses on expense_date - so open tickets, unpaid invoices, an agent time this week and unbilled tickets are found directly, where each was a read of the whole table, a dozen times over tickets on the dashboard and twice in the menu on every page; dashboardPeriodOptions finds the years with data with one index step a year per table in place of a UNION of YEAR over every row of five tables, and dashboardLatestIncome joins from payments with STRAIGHT_JOIN so the last five are read off payment_date, where every payment was sorted; measured on 280,000 tickets, 380,000 replies and 70,000 invoices the dashboard went from 5.1s to 0.7s and other pages lost about half a second, with every figure unchanged; also fix lists past 10,000 pages, where includes/filter_footer.php left the pager step unset and a division by zero ended the page at its page numbers; add a 26.11.7 changelog section 2026-11-11 14:13:11 -05:00
johnnyq
914d1c549a Make the line item picker on invoices, quotes and recurring invoices look and work like the other pickers: js/autocomplete.js now draws its list with the select picker panel, list and option classes, as the quick pick and tag pickers do, in place of a Bootstrap dropdown menu, so each product row has an icon for service or product, its name, a quieter line with its code, billing, tax and description, and its price at the end with stock under it, the typed text is marked, the row under the mouse or arrow keys is picked out in grey and dark mode is followed; the render option that returned an HTML string is replaced by row, which returns the parts of a row and is written as text, and js/billing_items.js passes it for products; the panel is added to the field modal or the body only while open, a field listeners are removed once it has left the page, and Escape closes the list without closing the window the field is in; adds .itflow-ac-panel and .itflow-ac-aside to css/itflow_custom.css; the asset operating system suggestions get the same look; add the changelog entries 2026-11-11 13:24:44 -05:00
johnnyq
24a50824b6 Add a Client column to the contacts export: getExportColumns for contacts in functions/export.php now offers client_name as Client, unticked by default as in the other exports, so Export Contacts can put each contact client in the CSV or PDF when exporting across all clients; the export_contacts query already joined clients, so nothing else changes; add the changelog entries. Requested by Bigbug on the forum 2026-11-11 13:14:47 -05:00
johnnyq
6d7ec51723 Bump Version and Changelog 2026-11-11 12:59:23 -05:00
johnnyq
feb068f16c Keep a picture that was copied by itself as a copy when it is pasted into an editor, not as a link to where it came from: Copy image in a browser puts both the picture and an img tag pointing at the other site on the clipboard, and TinyMCE takes the HTML whenever there is any, so the reply or document saved the link and the picture showed only for the person who pasted it; js/tinymce_defaults.js now puts a paste handler on every editor, ahead of TinyMCE own, that hands the paste back with only the picture on it when the clipboard holds one image file and its HTML is a single img with no text or other content, so it is stored like a pasted screenshot in every editor in ITFlow and the client portal; HTML with text in it is left to TinyMCE as before and nothing is fetched; add the changelog entries. Reported by jcobu on the forum 2026-11-11 12:41:31 -05:00
johnnyq
9b64195630 Only charge a saved card that belongs to the invoice client: add_payment_stripe in agent/post/payment.php looked the card up by saved_payment_id alone after checking access to the invoice client only, so a request naming another client saved card charged that client and recorded the payment on the invoice it was sent for; the lookup now requires the card to be on file for the invoice client with a payment provider that is turned on, as the Charge Saved Card window lists them, joins the provider customer on the card provider as well as its client, and stops with a message when there is no such card; add the changelog entries 2026-11-11 12:16:14 -05:00
johnnyq
f7cc3fde54 Make the audit log, notification and history writers safe whatever text they are given, and fix the API client delete that relied on them: logAudit, appNotify, logTicketHistory and logHistory now pass their text through the new logSqlText, which reads it as MySQL reads a quoted string, cuts it to the column by character and escapes it once, so text a caller escaped is stored exactly as before and text passed as read from a row can no longer end the quote; api/v1/clients/delete.php escapes the client name as archive.php does, where a name written for the purpose ran a query when the client was deleted and a name with an apostrophe ended in an error after the delete with nothing logged; logAudit also cuts the session IP and User-Agent to their columns, since a User-Agent over 250 characters failed the insert under strict mode, leaving the action unlogged and failed sign-ins and wrong two-step codes uncounted by the lockout and the code limit, and the User-Agent is cut to 250 where it is written to logs directly or compared with it in login.php, client/login_reset.php, api/v1/validate_api_key.php, guest/includes/inc_all_guest.php and portalSignupRequest; add_invoice_copy, add_invoice_recurring and add_recurring_invoice escape the invoice and recurring invoice prefixes before their inserts, which failed with an apostrophe in the prefix; add the changelog entries 2026-11-11 11:57:09 -05:00
johnnyq
980f394ee2 Send mail again on installs first set up with Microsoft 365 or Google OAuth on 26.11: cast the SMTP host to a string at both sendQueueEmail call sites, since it is NULL until a host is saved and the typed argument threw a TypeError that ended the mail queue run before anything was sent; catch any Throwable around each send so one failing email is marked failed and the rest still go out, where only PHPMailer's own Exception was caught and an Error left the email as Sending; add the changelog entries. Fix reported by toto707 on the forum 2026-11-10 16:25:07 -05:00
johnnyq
1cafa86aaa Point the Client portal tab's notice to Modules, where the client portal is switched on, instead of Settings > Client Portal, and give the sign-in field in the Turn on portal access window a sign-in icon so it doesn't show two keys 2026-11-10 15:47:40 -05:00
johnnyq
a85bcc1169 Add a Client portal view to Admin > Users listing every contact with a portal login across all clients, with their client, how they sign in, two-step status and last sign-in, searchable by name, email or client, sortable, paged, and filterable by client and by sign-in (password, Microsoft 365, no two-step, never signed in, access off); add row and bulk actions to email a password link or the invite again, turn off two-step sign-in, forget remembered browsers, and turn portal access off or back on, each logged against the contact; add the portal user helpers in functions/portal.php, have the portal's People page turn access off through the same function, which now also forgets remembered browsers, and keep the Users search to the side being looked at; add the changelog entries 2026-11-10 15:30:10 -05:00
johnnyq
a7a052729e Show only the picked client's contacts in the contract form and use the standard pickers across the contract modals: the form put every client's contacts in the page and hid the others with the hidden attribute, which Safari ignores on an option, so New Contract from the Contracts page listed them all there; it now renders only the contract's client's contacts and fetches them from get_client_contacts when a client is picked, with the primary picked; make every select in the contract and contract template modals a picker, with avatars and titles for contacts and owners, except frequency and tax in the recurring invoice modal, which match the recurring invoice form; add the changelog entries 2026-11-10 13:54:07 -05:00
johnnyq
4000e6bcc6 Drop contract_templates.company_id in database 3.6.3 where it exists: the 2.3.7 migration shipped in v25.11 creating it as INT NOT NULL with no default and was later changed to leave it out, so installs that updated on v25.11 kept a column nothing used, which the debug page listed as extra and which made adding a contract template fail in strict SQL mode; add the changelog entries and say in CONTRIBUTING why a shipped migration must not be edited 2026-11-10 12:51:38 -05:00
johnnyq
a737c04a7d Remove Hosting Coming Soon as its live now 2026-11-09 17:35:53 -05:00
johnnyq
a6d3b465df Update Changelog and App Version 2026-11-09 15:43:02 -05:00
johnnyq
6ecc27e7ca Version ITFlow's scripts and stylesheets so browsers fetch the new copies after an update instead of needing a cache clear: add staticFileUrl() in functions/app.php, which adds ?v= and the file's modification time to the address, and send every script and stylesheet tag through it, libs included, with page-relative addresses changed to start from the site root; add js/tinymce_defaults.js to set TinyMCE's cache_suffix so the plugins, theme and skins it loads itself are versioned too; load ticket collision detection only when a ticket is shown, so a ticket that isn't found no longer throws a script error; add changelog entries and a CONTRIBUTING rule that new tags must use staticFileUrl() 2026-11-09 01:22:35 -05:00
johnnyq
076f4295be Bug fixes 2026-11-07 12:26:58 -05:00
johnnyq
1c741b808b Bump Version 2026-11-07 12:06:20 -05:00
johnnyq
29a0b9b303 Update Chnagelog 2026-11-07 01:27:56 -05:00
johnnyq
53e6564f76 Stop row actions menus being cut off on short lists - a list's table scrolls sideways inside .table-responsive, which also clipped a row's menu at the bottom of the list when it had only a row or two, hiding its last options; new js/bootstrap_defaults.js, loaded right after Bootstrap on agent pages and the client portal, sets Bootstrap's dropdown default to Popper's fixed strategy so menus open over the page and still turn upward when there's no room below, covering every list, the board, modals and the portal, with navbar menus unaffected; changelog updated 2026-11-06 18:51:50 -05:00
johnnyq
2a36534583 Add demo mode for a public demo like demo.itflow.org - $config_demo_mode = true; in config.php puts one-click sign-in buttons for Admin, Technician, Accountant and a client portal contact on the sign-in page and a demo notice on every page, and the new Demo Reset cron job rebuilds the demo nightly at 2:00 AM from db.sql with all starter content, the demo clients and the demo accounts, empties uploads except the logo and signs everyone out, keeping the company details, timezone and cron schedules; visitors can try everything except users and roles, their own sign-in details, mail, payment, AI and texting settings, API keys, backups, updates, cron, log forwarding, company, module and portal settings, custom links, starter content and share links, Debug is closed, guest links open only for someone signed in, queued email is cancelled with the reason, no sign-in alert emails are sent, certificate checks reach only public addresses on port 443, and the domain and certificate refreshers are left out; new functions/demo.php and cron/demo_reset.php, no database change, changelog, CONTRIBUTING and README updated 2026-11-06 17:46:40 -05:00
johnnyq
79e616614a Set up a hosted instance and connect it in one command - Add Instance takes the address it will have, and the instance page gives a ready setup_cli command with the connection code and the client's company name, address, phone, website, tax ID, currency and primary contact as the first admin; setup_cli gains --manager-code and --manager-required (connects after setup, writes the managed line to config.php, exits 3 if it set up but could not connect), --ask-missing, --generate-password, --json and ITFLOW_SETUP_ environment variables for any option, and shows the cron job as a crontab line and for a hosting panel with the full PHP path; Managed Instances shows a connected instance whose cron has never run as Waiting for its cron job with the command to add, and the Managed Instances job says so in its notification; new managedSetupCommand(), the cron command in the instance report and the waiting state in managedInstanceHealth(), changelog updated 2026-11-06 14:29:27 -05:00
johnnyq
172cd4933e Turn cron on by default for new installs so adding the cron job is the only step - database 3.6.2 changes the default of settings.config_enable_cron to 1 and nothing else, so an existing install keeps its setting on or off, and setup/seed_data.php sets it for both setup paths; Get started's cron step is now Add the cron job with a line for hosting control panels such as Jabali, HestiaCP and cPanel, and shows Turn cron back on only when someone has switched it off; the setup page and setup_cli no longer say cron ships off, CONTRIBUTING's master switch section explains the new default, changelog updated 2026-11-06 14:26:50 -05:00
johnnyq
b391e2ce6c Add Skyetel text messaging with two-way ticket texting, text to ticket for contacts, Text reply on tickets, a Texts inbox for unmatched numbers, verification codes and spam (codes masked, limited to credential access, every reveal audited, wiped after a day), STOP/START/HELP handling, a text log with retry and cancel in Maintenance > Text Messages, the Text Messages cron job, and texts to clients about their tickets, visits, visit reminders and invoices with per-contact opt-in from the contact, by text or in the client portal, quiet hours, and new settings in Tickets and Notifications (database 3.6.0 and 3.6.1) 2026-11-06 00:08:10 -05:00
johnnyq
87cdf79b28 Make managed mode off by default and turned on per side in config.php - $config_enable_managed_instances = true; adds Admin > Managed Instances, the api/managed endpoints and the Managed Instances cron job, and $config_enable_managed_by = true; (or $config_manager_required = true;) adds Admin > Managed By, the Manager Check-in cron job and support sign-in; with a side off its menu entry, pages, modal, post handler and cron job are gone, the manager endpoints answer 503 so instances keep their connection and retry, a connected instance stops checking in and its locks, read-only and suspended lift while its connection is kept, any open support session is ended, and the support user still takes no seat; new managerEnabled(), managedEnabled() and managedConnected(), the cron registry and health check leave out the jobs of a side that is off, changelog updated 2026-11-05 20:17:42 -05:00
johnnyq
b5ceeb4eaf Bill sign-ups through the API and follow up when an invoice is paid - a lead that pays an invoice in full becomes a client however it is paid (online, autopay, a recorded payment or a batch), with the new A lead that pays an invoice becomes a client switch in Admin > Settings > Invoices and billing, on by default; a non-billable open ticket tied to an invoice (one raised through the API with ticket_invoice_id, or a quote's tickets once it becomes the invoice) gets an internal note when the invoice is paid in full, goes from On Hold back to Open, and its assigned agent is notified in the app and by email, while billable tickets whose time is on an invoice are left alone; new API endpoints recurring_invoices/create (lines from products or spelled out, the client's currency, frequency, start date, optional first invoice made at once and emailed to the primary and billing contacts, or marked Sent so its link can be paid, the answer including the invoice reference, amount, due date and pay link), recurring_invoices/read and portal_invites/create (sent from the company rather than the key's agent); tickets/create takes ticket_status (an active status other than Resolved and Closed), ticket_invoice_id (one of the client's own invoices) and ticket_notify agents to tell only the team; database 3.5.9 adds settings.config_invoice_paid_lead_to_client; invoicePaidFollowUp() runs from triggerCustomAction() on invoice_pay, with recurringInvoiceCreate(), billingInvoiceIssue() and the paid kind of ticketAgentEmailContent() in support; the Clients list reads each contract's term, so Renews soon shows again and its PHP warning is gone; changelog updated 2026-11-05 19:00:16 -05:00
johnnyq
294352fd8b Let the API raise a ticket from a ticket template - tickets/create takes ticket_template_id, giving the ticket the template's subject and details when none are sent, its category, priority, billing and agent unless sent, and its tags, due time and tasks (the ones the answers call for), with the template's fill-ins answered from ticket_fill_ins and its placeholders filled in, and a missing or archived template, fill-ins that aren't an object, a required fill-in left out or a choice not on a list refused with 400 and the reason; ticket_notify sends the contact and watchers the Ticket Created email when Email clients about their tickets is on, tells the new-ticket address and notifies the assigned agent in the app and by email; the answer now includes ticket_reference, ticket_url (the contact's link to the ticket) and, from a template, ticket_tasks; canned responses now fill in a ticket's template fill-in answers such as [Hostname] alongside [Contact] and [Ticket], never over a built-in placeholder of the same name, and the canned response form says so; contacts/create and locations/create no longer write a PHP warning for each field left out; the new-ticket agent email reads raised through the API; changelog updated 2026-11-05 17:39:22 -05:00
johnnyq
1649db51a5 Add secure link sharing for the client IT documentation PDF: new Export shared-link type started from Export Data (Share a link, carrying over the ticked sections) or Shared Links (Share the IT documentation), PDF built at share time with the sharing agent's permissions and stored in uploads/shares encrypted with the link-derived key, deleted when the link is ended, expires or is used up and swept by the nightly prune, PDF password required and the sharing protection policy applied when passwords and 2FA secrets are included, guest page with Download the PDF and a password hint, download notification and client audit entry, section picker takes an id prefix and preselected sections, changelog updated 2026-11-05 16:03:43 -05:00
johnnyq
3b5c11c552 Add managed mode, so one ITFlow can manage others - Admin > Managed Instances adds an instance and gives a one-time connection code (a week) that the other ITFlow pastes into the new Admin > Managed By, after which it checks in every minute from the new Manager Check-in cron job with a health report (versions, update waiting and whether it has security fixes, cron, last backup, errors today, agents against the seat limit, clients, storage) and takes back what the manager sets; the manager lists each instance's version, health, seats and last check-in with a page per instance showing what needs a look, its requests and everything it reported, can update it by hand or automatically after a set number of days (security releases straight away, not retried for six hours after a failure), set its seat limit and white-label key (locked on the instance while set), make it read-only (look, export and download only, across agents, portal, guest links and the API) or suspended (a notice with the manager's message and contact, 503 for the API) with other cron jobs paused until it is active again, sign in to it as support through a signed one-time two-minute link into a two-hour session that takes no seat, can't open the vault and can't set passwords or make API keys, and remove it, while the new Managed Instances job notifies when one stops checking in and closes requests never answered; instances only call the manager, can disconnect unless config.php sets $config_manager_required, and the Update page and queue_update defer to the manager; database 3.5.8 adds managed_instances, managed_instance_commands, settings.config_manager_keys and settings.config_managed, the logic is in the new functions/managed.php with api/managed/enroll.php, api/managed/checkin.php, login_support.php and includes/managed_blocked.php, cronJobClaim() takes a paused reason that leaves a due job due, getAgentUserLimit() and getActiveAgentUserCount() honour the manager's limit and leave out the support user, the vault helpers read a missing session key as empty, the audit log knows the new types and actions, and the email parser no longer logs an App Logs error every minute while parsing is switched off; changelog updated 2026-11-05 15:22:41 -05:00
johnnyq
d7bc1bccf7 Make Maintenance > Update easier to follow - it opens with where the install stands (up to date, an update available, updating, the database still to finish, checking) and the one button for it, Update ITFlow in place of Queue Update, with what's new below it (the release notes between this install and the latest, read from the changelog by the update check, developer notes and commits folded away), what to do first including the ITFlow files changed on this install that updating would put back, the update shown running with the page refreshing itself and then when it finished or what stopped it, and this install's version, branch, database, commit, last check, last update, cron and the command to update from a shell alongside; database 3.5.7 adds settings.config_update_details, written by cron/update_check.php through the new updateCheckDetails(), updateChangelogSections() and updateChangelogHtml() render the release notes escaped, cron/app_update.php asks for a fresh check after an update, changelog updated 2026-11-05 14:25:19 -05:00
johnnyq
c11de9a599 Drop the phone formats build script from scripts/ - it is a maintainer tool, not one for installs - and say in functions/phone_formats.php where its rules came from (libphonenumber v9.0.43's PhoneNumberMetadata.xml), how they were taken and to make it again from a newer release rather than edit it by hand; changelog updated 2026-11-05 13:24:59 -05:00
johnnyq
3143cdb115 Fix phone numbers keeping the 0 dialled at home after the country code (+49 0172 1234567) and click-to-call dialling it - formatPhoneNumber() now writes every country's numbers with Google libphonenumber's rules from the new functions/phone_formats.php, built by scripts/phone_formats_build.php from libphonenumber v9.0.43, taking off the national prefix and a typed-in country code where what is left is still a number of that country, with the international format after the code and the national format without it, German landlines split after their area code, Italy keeping its 0 and North America unchanged; the new phoneDialNumber() gives tel: links +code and the number without its prefix, used by contactPhoneLink(), vendorDialNumber() and every hand-built tel: link on the agent side, the client portal and the guest ticket page, and the ticket quick look now dials the extension after a pause instead of running it into the number; changelog updated 2026-11-05 13:16:06 -05:00
johnnyq
cf2c9c2d9b Let clients pay the card fee - turn on Clients pay the card fee on Stripe in Admin > Payment Providers with Stripe's percent and fixed rate, and each card payment in the client portal, through an invoice's link, by autopay or from Charge Saved Card is grossed up so Stripe's fee comes out of it, with one fee per payment shared over its invoices; the fee is shown before paying (Pay invoices, checkout, the guest pay page, invoice pages, autopay and the Charge Saved Card window) and goes on each invoice as a Card Fee line with no tax when the payment is booked, carried in the PaymentIntent's itflow_fees metadata and checked against the amount received to the cent; a client can be let off it with No card fee on their Billing tab, refunding a card payment takes its Card Fee line back off, autopay and Charge Saved Card now compare cents instead of whole dollars, database 3.5.6 adds the provider and client columns, changelog updated 2026-11-05 12:47:06 -05:00
johnnyq
5918cc59d4 Bump app version 2026-11-04 14:46:01 -05:00
johnnyq
e0f2b229ff Update changelog 2026-11-04 14:41:50 -05:00
johnnyq
e472c95a11 Record Stripe fees every 15 minutes instead of once a night - a new Payment Provider Fees cron job runs the fee recorder that Nightly Tasks used to, Nightly Tasks no longer does, the recorder holds a lock so the job and Record fees now can't both record the same fee, and the payment provider screens no longer say fees are recorded each night 2026-11-04 14:38:32 -05:00
johnnyq
66d08c00be Fix Transactions Money in and Money out on All accounts counting every transfer twice - a transfer is money moved between your own accounts, so across all accounts both of its lines are now left out of the totals, the Money in and Money out pills and their export, while the Transfers pill still lists them and a single account's view still counts them as money in and out of that account 2026-11-04 14:29:01 -05:00
johnnyq
aff6812036 Security: hold an agent who must set up MFA to the set up page on every request, not just at sign in, and fix the client portal two-step gate, which a path added to the URL or a second action sent with the set up one got past - both now go through requestIsMfaSetup(), which checks the script actually running and every key in the request; View as contact is refused while an agent still owes MFA set up, and agents are emailed when MFA is turned on 2026-11-04 14:01:40 -05:00