diff --git a/CHANGELOG.md b/CHANGELOG.md index fb093d694..f8762946d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,9 @@ This file documents all notable changes made to ITFlow. ### Upgrading to 26.11 -Update from Maintenance > Update — Queue Update hands the job to cron, which updates the files and applies this release's database changes (2.7.9 to 2.9.2) in the same pass. Nothing else is required. +Update from Maintenance > Update — Queue Update hands the job to cron, which updates the files and applies this release's database changes (2.7.9 to 2.9.3) in the same pass. Nothing else is required. -This is a big release, with thirteen database steps in one go, so take a backup before you update. +This is a big release, with fourteen database steps in one go, so take a backup before you update. The update also clears out data that deletes in older versions left behind, all of it pointing at something that no longer exists: @@ -17,7 +17,7 @@ The update also clears out data that deletes in older versions left behind, all - the tasks, approvals and history of deleted tickets, the projects of deleted clients, and tickets still pointing at a deleted project - project template entries whose ticket template is gone, and the same service link saved twice -It also gives every existing network a type (LAN, WAN, Guest and so on) guessed from its name or description, which you can change afterwards, and fills in the issue date, names covered and fingerprint of every certificate already on file. +It also gives every existing network a type (LAN, WAN, Guest and so on) guessed from its name or description, which you can change afterwards, and fills in the issue date, names covered and fingerprint of every certificate already on file. Invoices made from a quote before the update are linked to that quote, so each one shows where it came from. If you are coming from 26.08 or earlier, the database update still has to be run from the command line one time, exactly as described under Upgrading to 26.09 below. @@ -50,6 +50,12 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Every invoice made from a recurring invoice takes its taxes at today's rates, and the recurring invoice's lines are updated to match. The nightly run used to copy the amounts saved when each line was added, while Invoice Now used today's rates. - A schedule on the 29th, 30th or 31st keeps that day. After a shorter month it goes back to the 31st, where it used to stay on the 28th from then on. - The email goes to the client's primary and billing contacts that are not archived, once per address. When outgoing mail is not set up, nothing is queued and the invoice stays a draft. It used to be marked Sent anyway. +- Quotes: + - A quote's amount is now what it bills up front. Lines billed each month, quarter or year are counted apart, as so much a month, and the quotes list, search and the client portal show that next to it. Quotes from before the update have only one-time lines, so their amounts don't change. + - The quote's discount comes off its one-time lines. On a quote with only recurring lines it comes off the first recurring part (each month, then each quarter, then each year), and stays on the recurring invoice made from it. + - Make Invoice only works on an accepted quote, and only once. The quote is then Invoiced. It used to invoice a quote in any status, as many times as it was pressed. When the client says yes by phone or email, mark the quote Accepted first. + - The quote email says "Cost: $1,540.00 up front and $2,034.76 a month" when the quote has recurring lines. A quote with only one-time lines still says "Total Cost". + - Quotes Pipeline values a quote at what it bills up front plus a year of its recurring lines, so a quote with monthly lines counts for more than its up-front amount in quoted, won, lost and open. The open quotes table shows that first-year value with the breakdown under it, and its CSV has Up front, Monthly and First year columns. - Notifications now follow access too. An agent only sees notifications for clients and areas they can open, so someone without sales access no longer sees invoice and quote notifications. - Drop-downs: Tom Select is gone, replaced by ITFlow's own picker. It works the way you are used to: type to search, tick several in a multi-select, and type a new value in fields that allow it, such as ticket watchers. See Developer Updates if you have custom pages that used Tom Select. - Several small windows are gone because the change is now made where the value is shown: @@ -191,7 +197,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Recurring invoices: your monthly recurring revenue (quarterly ones count as a third, yearly ones as a twelfth), what bills in the next 30 days, how many are running, and any that need a look. Filter by Active, Next 7 days, Late, Paused or All. Each row shows the amount, what it brings in a month, when it next bills, whether it is on autopay, and whether it bills per user or per device. - Pages: the invoice, quote or recurring invoice is shown the way the client sees it, with the next thing to do at the top: - Invoice: Send (or Mark Sent) a draft, Add Payment on one that is owed, with Charge Saved Card when the client has a card on file. On the side: what has been paid and what is left with a progress bar, the payments (edit, refund through Stripe or delete them there), the tickets billed on it, and its history as a timeline. - - Quote: Send, then Accepted or Declined when the client answers by phone or email, then Make Invoice. A quote that ran out without an answer says so, with an Extend button. On the side: the files the client sent back, and its history. + - Quote: Send, then Accepted or Declined when the client answers by phone or email, then Make Invoice (Start Billing when it has recurring lines). A quote that ran out without an answer says so, with an Extend button. On the side: the files the client sent back, and its history. - Recurring invoice: Invoice Now, Pause and Resume. On the side: the schedule with the next few dates, the period the next invoice covers and whether invoices go out by themselves, autopay, the invoices it has made and whether they were paid, and its history. A run missed in the last 7 days is billed by the next nightly run. One further behind says so, and offers Invoice Now for each missed run or moving on to the next date without billing them. - Lines are edited on the page: - Type a product's name or code to fill in its description, price and tax. What the line comes to shows as you type, and Enter adds it. The page comes back ready for the next line. @@ -221,6 +227,12 @@ If you are coming from 26.08 or earlier, the database update still has to be run - The ticket's Invoice window is redone. It shows the time logged by each agent and the hours that bills, with the client's rate (or the default rate) filled in, and it can add to one of the client's drafts. An open ticket can still be billed from here, with a warning that later time won't be. - Add Tickets on an invoice bills their time: tick the tickets, change their hours, and they go on as lines at the rate. - The client overview's Not yet invoiced shows what the unbilled time comes to and opens Unbilled Time. The dashboard and the Unbilled Work report link to it too. + - **Quotes with monthly lines**, so one quote can carry the onboarding and the agreement that follows: + - Each line is billed one time, or each month, quarter or year. Pick it under Billed on the add-a-line row, or in Edit Line. + - The quote groups its lines under One-time, Each month, Each quarter and Each year, with a total for each, on the page, the PDF and the client's view. The top shows what it comes to up front and each month, quarter or year. A line drags within its own group. + - Start Billing on an accepted quote: the one-time lines go on a draft invoice, dated as you choose and due on the client's terms. Each month, quarter or year part goes on a new recurring invoice from the date you pick (the first of next month to start), with its invoices emailed as they are made or left as drafts, or is added to one of the client's recurring invoices billed as often. Switch on Bill the first month on the invoice now to put the first period on the invoice too. A quote with only one-time lines keeps Make Invoice. + - The quote shows what it was invoiced as, with a link to each invoice and recurring invoice, including any it added lines to. The invoice and the recurring invoices link back to the quote, and each history says what happened. + - The quotes list adds the monthly part to each row and to its totals. Search, the client portal's quotes, the Send and Copy windows, the quote email and the client's Accept all show the amounts by part. The quotes export has a Monthly column, and the client export a monthly amount on quotes and how each quote line is billed. - **Tickets** - The ticket list has views for Open, Mine, Unassigned, Needs reply, Waiting, Overdue, Scheduled, Closed and All. - Each row shows whose move it is (yours or the client's) along with SLA and due badges, and status, priority and agent change right in the row. @@ -394,6 +406,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Billing: an invoice made from a quote was due counting from today instead of the invoice date, and with no terms when the client had none set. - Billing: the client's view of a quote offered Accept on a viewed quote that had run out, and the invoice and quote views put the browser's HTML into the history. - Billing: copying an invoice logged its old number with the prefix twice. +- Billing: Make Invoice on a quote could make a second invoice when pressed twice or from two tabs, and could invoice a quote that was never accepted or had been declined. Its audit log entry named the quote with the current quote prefix instead of the quote's own. - Recurring invoices: if cron missed a night, that recurring invoice stopped billing until someone moved its date. The missed run is now billed the next night, up to 7 days late. - Invoicing a ticket: on an invoice that already had lines, its line went in near the top instead of at the end. A ticket already on an invoice could be billed again from a second window, and a ticket with 100 hours or more logged had no hours filled in. - Invoicing a ticket for a client with no hourly rate filled in nothing. It now takes the default hourly rate, as the Unbilled Work report already did. @@ -429,7 +442,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run ### Developer Updates -- Database 2.8.0 to 2.9.2, one file per step under `admin/database_updates/`, with `db.sql` matching 2.9.2: +- Database 2.8.0 to 2.9.3, one file per step under `admin/database_updates/`, with `db.sql` matching 2.9.3: - 2.8.0: `racks.rack_units_descending`, `rack_power_capacity` and `rack_photo_rear`. On `rack_units`: `unit_face`, `unit_full_depth`, `unit_type`, `unit_power_watts`, `unit_notes` and a `unit_asset_id` key. Unlinked items move from asset id 0 to NULL. - 2.8.1: `location_documents` and `location_files`. - 2.8.2: `vendor_tags` (tag type 6), and `vendor_contact_phone_country_code`, `vendor_contact_phone` and `vendor_contact_email`. @@ -455,6 +468,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - `settings.config_share_max_hours` (720) and `config_share_require_protection` (0) - 2.9.1: `recurring_invoice_items.item_seat_source`, `item_seat_filter` (JSON) and `item_seat_minimum`, and `recurring_invoices.recurring_invoice_anchor_day`, backfilled from the day of each next date. - 2.9.2: `settings.config_time_increment` (15), `config_time_rounding` ('nearest'), `config_time_minimum` (0) and `config_time_item_name` ('Support [Hourly]'). + - 2.9.3: `quote_items.item_frequency` (NULL for one time, or `month`, `quarter` or `year`), `quotes.quote_monthly_amount`, `invoices.invoice_quote_id` and `recurring_invoices.recurring_invoice_quote_id`. Invoices are linked to their quote from the "Invoice created from quote QUO-n" history the old Make Invoice wrote, matched on the client too. - New shared function files, loaded by `functions.php`: `functions/asset.php`, `asset_interface.php`, `certificate.php`, `client_overview.php`, `contact.php`, `credential.php`, `dashboard.php`, `links.php`, `location.php`, `notifications.php`, `project.php`, `rack.php`, `search.php`, `service.php`, `software.php`, `tags.php`, `ticket.php`, `ticket_fields.php`, `user.php` and `vendor.php`. They hold the queries and rules, so the pages, the API and cron share one definition. - Removed post models: `agent/post/{asset_interface,certificate,credential,domain,event,location,network_ip,network,recurring_ticket,vendor}_model.php`, `admin/post/user_model.php` and `api/v1/certificates/certificate_model.php`. Their handlers call the shared functions above. Interface handlers moved to `agent/post/asset_interface.php`. - New scripts: `js/itflow_select.js`, `quick_pick.js`, `tag_picker.js`, `global_search.js`, `notifications.js`, `dashboard.js`, `client_overview.js`, `client_switcher.js`, `credentials.js` and `autosubmit.js`, and `agent/js/calendar.js`, `calendar_event_modal.js`, `files.js`, `interface_form.js`, `network_form.js`, `project_modal.js` and `ticket_schedule_modal.js`. @@ -556,6 +570,10 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Billing time (`functions/billing.php`): `billingTimeRules()` reads the settings once a request (falling back to the old rules before 2.9.2 is applied, so they are not in `load_global_settings.php`), `billingTimeIncrements()`, `billingTimeRulesText()`, `billingTimeHours()`, `billingTimeRate()`, `billingTimeDescription()`. `billingUnbilledSql()` is the one definition of unbilled (billable, status Resolved or Closed, not archived, `ticket_invoice_id = 0`); `billingUnbilledTickets()`, `billingUnbilledByClient()` and `billingUnbilledCount()` read it, scoped to the agent's clients. The dashboard, client overview, sidebars and the Unbilled Work report use them. - `billingTimeInvoice($client_id, $hours, $opts, &$error)` is the one path that bills time - Unbilled Time, `add_invoice_from_ticket`, `add_ticket_to_invoice`. It validates first, makes the draft with `billingInvoiceCreate()` when needed, claims the tickets with a conditional `UPDATE` (deleting a new draft that ends up with none), adds the lines through `billingItemSave()`, and logs ticket history, invoice history and the audit log. `billingTimeWriteOff()` sets `ticket_billable = 0`. `invoiceUnbilledTickets()` is replaced by `billingUnbilledTickets()`, and `roundToNearest15Min()` is no longer used. - New page `agent/unbilled_time.php` with `js/unbilled_time.js`, and handlers `unbilled_invoice`, `unbilled_invoice_clients` and `unbilled_write_off` in `agent/post/billing.php`. `add_ticket_to_invoice` takes `ticket_ids[]`, `hours[id]`, `bill_time`, `item_name`, `price`, `tax_id` and `lines` (`each` or `one`). `add_invoice_from_ticket` keeps its field names. `remove_ticket_from_invoice` writes to the ticket's history. + - Quotes (`functions/billing.php`): `quoteFrequencies()`, `quoteFrequency()` (anything else is one time), `quoteLineGroups($items, $discount)` (lines grouped by frequency with subtotal, tax, taxes by name, discount and total each; the discount sits on the one-time group, or the first recurring one when there is none), `quoteGroups($quote)`, `quoteHasRecurring()`, `quoteMonthly()` and `quoteAmountText()`. `quote_amount` is the one-time group's total and `quote_monthly_amount` the recurring ones as a month, both set by `billingRecalc()`. `billingTotals()` for a quote adds `groups`, `recurring` and `monthly`. `billing_item_save` takes `frequency` on quote lines, and a form without it leaves the line's frequency alone. + - `quoteConvert($quote_id, $opts, &$error)` is the one path that invoices a quote, used by `add_quote_to_invoice`. `$opts`: `date`, `first_period`, `recurring[frequency]` with `to` (a recurring invoice id, or 0 for a new one), `start` and `auto_send`, and `by`. It checks every target first, claims the quote with a conditional `UPDATE` from Accepted to Invoiced, and makes everything in one transaction, rolled back and logged with `logApp()` on failure. Lines are copied with `billingCopyLines()`. It returns `invoice_id`, `recurring` (id => number) and `numbers`, or null with `$error`. `quoteMadeFrom()` lists what a quote became. A recurring invoice that only got lines is found through its history entry, which carries `history_quote_id` as well as `history_recurring_invoice_id`, so `billingHistory()` leaves such rows off the quote's history. `delete_quote` keeps those rows and clears `invoice_quote_id` and `recurring_invoice_quote_id`. + - `agent/modals/quote/quote_to_invoice.php` posts those fields. Quote lines carry `data-group`, with `tr.itflow-paper-group` headings in `includes/billing_paper.php`, and the Sortable in `js/billing_items.js` drags only `tr[data-item-id]`, within its group when the table is grouped. + - `reportCsv()` writes a totals row as given, without the column's `csv` function, which read fields a totals row doesn't have. The quotes export takes `quote_monthly_amount`. - Removed: `add_invoice_item`, `edit_invoice_item`, `delete_invoice_item`, `add_quote_item`, `edit_quote_item`, `delete_quote_item`, `add_recurring_invoice_item`, `edit_recurring_invoice_item`, `delete_recurring_invoice_item` and `link_invoice_to_ticket` (it wrote a column that does not exist), `update_invoice_items_order`, `update_quote_items_order` and `update_recurring_invoice_items_order` in `agent/ajax.php`, `agent/modals/invoice/invoice_item_edit.php`, `agent/modals/quote/quote_item_edit.php`, `agent/modals/recurring_invoice/recurring_invoice_item_edit.php`, `guest/guest_quote_upload_file_modal.php` and `js/product_autocomplete.js`. - Sidebar brand: `agent/css/sidebar_brand_fix.css` and `agent/js/sidebar_brand_fix.js` from 26.10 are replaced by CSS in `css/itflow_custom.css`. `includes/load_company_settings.php` now sets `$session_company_logo`. - `alertMessageHtml()` no longer double-encodes, and save messages built from `escapeSql()` values use `stripslashes()` at the flash. diff --git a/admin/database_updates/2.9.3.php b/admin/database_updates/2.9.3.php new file mode 100644 index 000000000..8b7189675 --- /dev/null +++ b/admin/database_updates/2.9.3.php @@ -0,0 +1,27 @@ + 0 && $totals['total'] > 0 ? min(100, round($state['paid'] / $totals['total'] * 100)) : 0; @@ -112,6 +116,9 @@ $paper = [ From + + From + Created diff --git a/agent/modals/billing/billing_item.php b/agent/modals/billing/billing_item.php index 25cdc10a1..1a824a25e 100644 --- a/agent/modals/billing/billing_item.php +++ b/agent/modals/billing/billing_item.php @@ -81,6 +81,21 @@ ob_start(); + +
+ +
+ + +
+
A line billed each month, quarter or year goes on a recurring invoice when the quote is invoiced, and the quote shows it apart from what is billed once.
+
+ ">