diff --git a/CHANGELOG.md b/CHANGELOG.md index 92d9a4861..977ab7565 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.6) 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.7) in the same pass. Nothing else is required. -This is a big release, with seventeen database steps in one go, so take a backup before you update. +This is a big release, with eighteen 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. Invoices made from a quote before the update are linked to that quote, so each one shows where it came from. Products that ever had stock added keep tracking their stock, and the rest are set to ordered as needed (see Breaking Changes, Products). Their past stock changes are sorted into received, sold and returned from their notes, and linked to the invoice they name. No product becomes a client asset when it is sold until you give it an asset type (see New Features, Products). +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. Products that ever had stock added keep tracking their stock, and the rest are set to ordered as needed (see Breaking Changes, Products). Their past stock changes are sorted into received, sold and returned from their notes, and linked to the invoice they name. No product becomes a client asset when it is sold until you give it an asset type (see New Features, Products). Each product's price history starts with the update, so lines on recurring invoices are only flagged as behind once their product's price changes. 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. @@ -124,7 +124,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - **Dashboard** (rebuilt) - Needs attention along the top: - tickets past their SLA, unassigned tickets and overdue projects - - overdue and draft invoices, resolved tickets not invoiced yet, stock to reorder, and hardware sold in the last 90 days that isn't on the client's assets yet + - overdue and draft invoices, resolved tickets not invoiced yet, stock to reorder, lines on recurring invoices, drafts and quotes at an old price, and hardware sold in the last 90 days that isn't on the client's assets yet - domains, certificates, licenses and warranties expiring in the next 30 days - services needing attention and service reviews due @@ -272,6 +272,12 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Products has a Hardware tab, and each row says the asset it becomes. - The packing slip lists the serial numbers under each line. - The starter catalog's hardware comes with its asset type and warranty, and so does the demo data's. + - Price changes reach recurring invoices: + - Every change to a product's price or cost is kept, with who made it and whether it was edited, imported or came from received stock, on a Prices tab on its page. Pricing says when its price last changed and from what. + - Lines on recurring invoices, draft invoices and draft quotes still at one of its old prices are flagged on the product's page, the Products page, each of those documents and Needs attention on the dashboard, with what updating them adds a month. + - Update Prices lists them by product - all of them from the Products menu, or one product's, one client's or one document's. Lines at an old price are picked, ready for today's price. A price the product never had looks agreed with the client, so it is marked Own price and left alone unless picked, with the last change applied to it as the suggestion. Each new price can be changed, Catalog price or the same percent fills in a product's lines at once, and the footer says what recurring invoices bring in a month after. Invoices already sent, and quotes out with clients, keep their prices. + - Each line changed goes in its document's history, and the document's tax and total follow. + - Saving a new price, or importing a price list, says how many lines are at an old price. - Import takes every field, including Reorder At, Order, Asset Type, Make, Model and Warranty. Categories, taxes and vendors are matched by name. A new price list can update products by their code, and a file with only Code and Price will do. Download the template for the layout. - Export adds cost, margin, how it is billed, vendor, part number, stock and its value, the reorder level, and the asset it becomes with its make, model and warranty. It can export just what is to reorder, or just hardware. - Invoices, quotes and recurring invoices show agents their margin on lines from products with a cost, at today's cost. A quote shows it up front and each month. @@ -521,6 +527,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - 2.9.4: `products.product_cost`, `product_vendor_id`, `product_vendor_part`, `product_notes`, `product_frequency` (NULL once, or `month`, `quarter`, `year`), `product_seat_source` (NULL, `contacts` or `assets`) and `product_track_stock`, set for products with any `product_stock` rows. Keys on `item_product_id` in `invoice_items`, `quote_items` and `recurring_invoice_items`, and on `product_stock.stock_product_id`. - 2.9.5: `product_stock.stock_qty` becomes `decimal(15,2)`, with `stock_type` (`received`, `sold`, `returned`, `adjusted` or `written_off`, default `adjusted`), `stock_unit_cost`, `stock_invoice_id` (keyed) and `stock_user_id`. Existing rows take their invoice from a note ending "Invoice n" and their type from the sign and the note. `products.product_reorder_level` and `product_reorder_qty`. - 2.9.6: `products.product_asset_type` (one of the asset types, NULL for none), `product_make`, `product_model` and `product_warranty_months`. `assets.asset_product_id`, `asset_invoice_id` and `asset_invoice_item_id`, keyed on the product and the invoice. + - 2.9.7: `product_prices` - a product's price and cost history: `price_old`, `price_new`, `price_cost_old`, `price_cost_new`, `price_source` (`edit`, `import` or `receive`), `price_user_id` and `price_product_id`, keyed. - 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`. @@ -644,6 +651,9 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Sold hardware, in `functions/products.php`: `productAssetTypes()` (the `$asset_types_array` list), `productAssetType()`, `productAssetIcon()`, `productHardwareSql()`, `productHardwareUnits()` (a line's quantity rounded up, none for a credit), `productWarrantyUntil()`, `productHardwareInvoiceSql()` (not drafts or cancelled, not made by a recurring invoice), `productHardwareShortSql()`, `productHardwarePending($days)`, `productInvoiceHardware($invoice_id)`, `productAssets($product_id)` and `productAssetCandidates()`. `productSaleAssets($invoice_id, $lines, $units, $shared, &$error)` checks every row first, then adds or links them in one transaction, each new one with its primary interface, and each with a history line and an audit log entry. - Handler `invoice_assets_save` (POST `invoice_id`, `status`, `location`, `lines[item_id][make|model|warranty]` and `units[item_id][n][name|serial|contact|existing]`; needs Sales, and Support at edit) from `agent/modals/invoice/invoice_assets.php?invoice_id=`. `billingItemDelete()` clears `asset_invoice_item_id` on the line's assets and `delete_invoice` clears both invoice links - the assets stay. - `productSave()` and `productCopy()` take `asset_type`, `make`, `model` and `warranty_months`, with import aliases and export columns for them. The products list and export take `type=hardware`, and `invoices.php` takes `hardware=1`. `dashboardAttention()` adds Hardware not on assets for agents with Support access, `billingPdfRender()` reads the serials for the packing slip, and the asset page joins the invoice and the product. Seeds: columns 12 to 15 on `demoDataProducts()` and an eighth and ninth on `starterContentProducts()`. CSS: `.itflow-hw-*`. + - Price history: `productSave($in, $product_id, &$error, $source)` records a change to an existing product's price or cost through `productPriceRecord()` - `edit_product` passes `edit`, `import_products_csv` passes `import` and `product_stock_save` passes `receive` when a delivery sets the cost. Also `productPriceSources()`, `productPrices()` and `productPriceLastChange()`. `productDelete()` and the demo data removal delete the history too. + - Repricing: `productRepriceKinds()`, `productRepriceUnionSql()` (every line from a product on recurring invoices not archived, draft invoices and draft quotes, as one union), `productRepriceOldSql()` (at a price in the product's history), `productRepriceFilterSql()`, `productRepriceCount($filters)` (lines, by kind, products, and the change a month on recurring invoices) and `productRepriceLines($filters)` (by product, with `old` and `suggested`). `productRepriceApply($changes, &$error)` checks every line and price, then changes them in one transaction through `billingItemSetPrice($type, $doc_id, $item_id, $price)` (new in `functions/billing.php` - the line's amounts at its tax rate, then `billingRecalc()`), with a history line on each document. + - New page `agent/products_reprice.php` (`?product_id=`, `client_id`, `kind` and `doc_id`) posting `products_reprice_save` (`pick[]` of `kind:item_id`, and `price[kind:item_id]`). `dashboardAttention()` adds Lines at an old price, and the invoice, quote and recurring invoice pages link their own. CSS: `.itflow-reprice-*`. - The billing helpers (`billingTimeInvoice()`, `quoteConvert()`, `recurringInvoiceGenerate()` and the rest) take the agent's name as plain text. Their handlers now pass `stripslashes($session_name)`, so a name with an apostrophe no longer shows a backslash in history and the audit log. - 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.7.php b/admin/database_updates/2.9.7.php new file mode 100644 index 000000000..092d21fa6 --- /dev/null +++ b/admin/database_updates/2.9.7.php @@ -0,0 +1,26 @@ +
| When | Price | Change | Cost | How | By |
|---|---|---|---|---|---|
| = escapeHtml(date('M j, Y', strtotime($price_row['price_created_at']))) ?> | ++ + = $money($price_old) ?> → = $money($price_new) ?> + + = $money($price_new) ?> + + | ++ = $price_moved == 0 ? '-' : ($price_moved > 0 ? '+' : '') . ($price_old > 0 ? round($price_moved / $price_old * 100, 1) . '%' : $money($price_moved)) ?> + | ++ + = $money($price_row['price_cost_old']) ?> → = $money($price_row['price_cost_new']) ?> + + = floatval($price_row['price_cost_new']) > 0 ? $money($price_row['price_cost_new']) : '-' ?> + + | += escapeHtml($price_sources[$price_row['price_source']] ?? $price_row['price_source']) ?> | += $price_row['user_name'] !== null ? escapeHtml($price_row['user_name']) : '-' ?> | +