## Summary
Fixes **TRU-73 / DRIFT 12** — invoice format must clearly show the
discount given on all services.
For customers with a global e-conomic discount (e.g. `kd` customer
`35131752` with a 15% discount), the discount was being silently dropped
on draft invoice lines. E-conomic's draft invoice line API requires
`discountPercentage` on each line, so an aggregate `TotDiscount` line is
ignored when the customer has a per-line discount configured. The fix
applies the customer discount at the line level.
## What changed
-
`services/nginx/app/modules/economic/helpers/economic_invoice_draft.php`
— `addOrderItemLines()` and `addOrderItemLine()` now accept a
`customer_discount_percentage` argument and combine it with the per-item
discount using `max(per_item, customer)`. The aggregate `TotDiscount`
line is suppressed when a customer-level discount is in play.
- `services/nginx/app/modules/economic/customers/economicCustomers.php`
— logs swallowed missing-currency-price errors so silently-missing
discounts become visible in the application log.
-
`services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php`
— forwards the customer discount percentage to the draft builder.
- `services/nginx/app/objects/collected_order_invoices_o.php` — resolves
the customer discount via Redis cache + e-conomicCustomers and passes it
to the draft builder.
-
`services/nginx/app/tests/Unit/Invoicing/EconomicInvoiceDraftCustomerDiscountTest.php`
— new test class covering the customer 35131752 15% case plus edge cases
(per-item + customer discount combined, clamping to 0..100,
zero-discount baseline).
-
`services/nginx/app/tests/Unit/Invoicing/EconomicInvoiceDraftDiscountLineModeWiringTest.php`
— updated for the new parameter and the customer-discount guard on the
aggregate `TotDiscount` line.
-
`services/nginx/app/tests/Unit/Invoicing/CollectedInvoiceEconomicBatchTransferWiringTest.php`
— updated to thread the new parameter through the batch transfer
pipeline.
- `documentation/economic/invoice-discount-format-drift12.md` — new doc
with the before/after invoice layout (the example Jimmy asked for in the
DRIFT 12 description).
## Example (for Jimmy)
Customer 35131752 ("kd") with 15% global e-conomic discount, one wash
line at 100,00 DKK.
### Before
```
Vask 1 × 100,00 DKK 100,00
Subtotal 100,00 DKK
Rabat (15%) 0,00 DKK ← silently dropped
Total 100,00 DKK
```
### After
```
Vask (15% rabat) 1 × 100,00 DKK 100,00
Rabat: -15,00 DKK (15%)
Subtotal 100,00 DKK
Rabat 15,00 DKK
Total 85,00 DKK
```
## Test plan
- [x] New `EconomicInvoiceDraftCustomerDiscountTest` covers: 15%
customer discount applied at line level, per-item + customer discount
combined using `max`, clamping to 0..100, zero-discount baseline.
- [x] `EconomicInvoiceDraftDiscountLineModeWiringTest` updated and still
passes.
- [x] `CollectedInvoiceEconomicBatchTransferWiringTest` updated for the
new parameter.
- [ ] Run full `php-ci-test.sh unit` locally to confirm nothing else
regressed.
## Linear
Closes TRU-73 (DRIFT 12).
🤖 Generated via the TRU-73 pickup cron run.
---------
Co-authored-by: MiniMax M3 Subagent <fix@truckwash.local>
## Summary
- gate object-tree v2 on exact, schema-backed supersession audit columns
- serialize checked additive DDL and fail closed without disrupting
legacy invoicing
- preserve pre-schema supersession markers when structured columns are
still null
- add a superuser-only, self-scoped canary endpoint with locked
legacy-to-canonical allowlist migration
- verify effective activation, roll back failed readiness, and audit
enable/disable/failure
## Verification
- focused invoicing safety: 16 tests passed (99 assertions)
- full unit suite: 1,237 passed (9,003 assertions), 2 skipped, existing
warnings only
- PHP syntax and `git diff --check` clean
- independent architecture, security, and reviewer gates: GO
## Activation
Deploy with global database/environment enablement off. POST the
self-canary endpoint for one authenticated superuser, require
`configured_enabled=true` and `effective_enabled=true`, then verify the
exact period and tree GET routes. Roll back with the same endpoint using
`enabled=false`.
## Summary
- Makes Stripe Terminal card payment intents always use 25% moms in the
API, independent of any client-supplied `tax_percentage`.
- Updates amount calculation, metadata persistence, stored-intent reuse
matching, the authoritative OpenAPI contracts, and operation-specific
Writerside outputs.
- Prevents double charging and false order closure across stale,
concurrently succeeded, partially recorded, or mismatched intents.
- Serializes payment create/capture/closure with order-item changes and
every order-to-invoice-collection reassignment through shared database
locks.
- Converts expected lock contention and reconciliation cases into
deliberate 409 responses.
## Exact-head evidence
Current head: `3a0f70d315a94d2efe586a2188d2c54f8ff11cd4`
- PHP syntax passed for all changed runtime files.
- Focused Orders suite: **42 tests / 293 assertions passed**.
- `git diff --check` passed.
- Fresh exact-head Tests and Qodana are running.
- Every Codex finding has a concrete reply; a fresh exact-head review is
requested below.
## Safety behavior
- Caller-controlled VAT is absent from request contracts; fixed 25% moms
is server-owned.
- A succeeded payment is preserved, requires the full expected
`amount_received`, and cannot close a changed/mismatched or
already-claimed collection.
- A compatible partially recorded Stripe closure is completed
idempotently; conflicting partial state fails closed for manual
reconciliation.
- Every cancellation/delete caller honors a concurrent-success result
and never falsely reports a completed payment as cleared.
- Price changes and invoice-collection reassignment share the payment
lock through validation, capture, post-capture reload, and closure.
- Reader changes are persisted only for reusable matching intents, so
stale intent cancellation targets the original terminal.
- Accepted legacy succeeded intents normalize stored tax to 25% before
response construction.
---------
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
Resolve recommended-profile Critical and High findings, retain narrow analyzer exceptions, and update the edge-broker WebSocket dependency to a non-vulnerable release.
- Implemented `InvoicingPeriodDraftOverlayTest` with coverage for blocking and permitting invoicing actions based on draft states, transactions, and metadata.
- Created `ReferenceSuggestionsApiTest` to validate ranked and filtered suggestions across bookings, orders, and vehicles with varied match relevance, context, and frequency.
- Added `order_reference_suggestions_service` class, including query methods, normalization utilities, and aggregation logic for reference suggestions.
- Enhanced query handling in `InvoicingPeriodDraftOverlayFakeDb` to validate SQL constraints and column cache resets in overlapping invoicing contexts.
- Deleted `complete_booking_f` and `generate_booking_wash_certificate_f` classes.
- Updated tests to ensure legacy booking completion routes are disabled.
- Introduced tests for POST `/order-bookings/complete` to enforce POS-based booking completion management.
- Added `/collected-invoices/split-by-month` route with API and unit tests for splitting collections into monthly periods.
- Refactored impacted files to exclude legacy references and ensure continued compatibility with POS processes.
Transitioned from obsolete gateway object classes (`edge_gateway_shell_action_jobs_o`, `edge_gateway_shell_events_o`, `edge_gateway_shell_sessions_o`, `edge_gateway_update_jobs_o`) to the new agent implementation (`edge-gateway-agent/agent.php`).
- Enhance Redis methods (`exists`, `setEx`, `delete`, `get`, `set`) to ensure connection before execution.
- Introduce short-lived caching for collected order invoices to minimize redundant processing and improve performance.
- Add `pagination_helper` for dynamic WHERE clause construction in queries.
- Refactor net amount calculation in `collected_order_invoices_o` for efficiency with batch processing.
- Extend `listObjectsWithPaginationIfSet` to support additional WHERE clauses.
- Refactor date calculation to use `closed_at` for determining monthly ranges.
- Fix SQL query to use `closed_at` instead of `created_at` for filtering orders.
- Add `isEmpty` method to check if an invoice collection has any orders.
- Update cache expiration times for `economicCustomerName` and `isBooked` objects
- Introduce `getCachedForMultipleObjects` for batch cache retrieval
- Optimize `isBooked` with optional caching and update to store results
- Implement `getCustomerNames` in `users_o` with caching for bulk name retrieval
- Refactor customer transaction handling in `InvoicingPeriodRoute` for efficiency
- Filter orders excluded from invoicing in `collected_order_invoices_o`
- Introduced `isIncludedInInvoicing` in `orders_o` to check department-based invoicing exclusions.
- Updated routes and methods to skip orders excluded from invoicing.
- Refined transaction and order handling to respect invoicing settings, ensuring correct filtering.
- Added `virtual_cart` property in `bookings_o` and enhanced its structure method.
- Introduced `display_in_booking_form` and `order_priority` properties in `products_o` with updates to object structure, parsing, and routes.
- Enabled handling of product attributes `display_in_booking_form` and `order_priority` in `productsRoute`.
- Added `booked_invoice_id` reset logic in `collected_order_invoices_o`.
- Introduced `machineButtonPressRoute` to log button press incidents and send Slack notifications.
- Introduced `add_environmental_and_oil_fees` in `economic_invoices_draft_endpoint` to compute and add fees to drafts.
- Added `addEnvironmentalAndOilFeesToDraft` in `collected_order_invoices_o` to conditionally apply fees for customers with the `onlyTankCleaning` attribute.
- Enhanced `orderInvoicesRoute` with `reset`, `unlink`, and `remove-special-arrangements` handlers for invoice actions, improving E-Conomic integration.
- Added `resetPricesOfItemsNotIncludedInInvoice`, `setAllItemsToBeIncludedInInvoice`, and `unlinkFromEconomic` methods in `collected_order_invoices_o` for robust invoice control.
- Introduced `getCustomerProductPrice` in `orders_o` to calculate customer-specific product pricing with discounts.
- Added `getCachedResultsBySubstring` in `motorapi_lookups_o` for efficient cached lookup based on result substrings.
Introduced a new `po_number` property in `collected_order_invoices_o` and updated related methods to handle it. Added `userInvoicesRoute` to manage user invoice APIs with GET and PUT endpoints for listing and updating invoices. Updated `microsoft/microsoft-graph` dependency to version `^2.8`.
Introduced functionality to handle fixed prices for collected order invoices, allowing customer-specific pricing overrides. Added a new route and methods to integrate fixed pricing logic, modify invoice items, and ensure proper data management in E-Conomic. Adjusted discount calculations to accommodate cases where final price is zero.
Introduce the closed_at field to track closure dates for order invoices. Updated validation, processing logic, and database integration to ensure correct handling of the new field. Adjusted related routes to pass and store the closed_at value where applicable.
Introduced a method to handle the removal of vehicle subscription transactions from invoice collections. Refactored references to a hardcoded cashier ID by using a class property for better maintainability. This ensures a cleaner and more dynamic transaction-handling process.
Updated default values for department IDs to prevent null issues and ensure proper handling of optional fields. Adjusted logic to skip non-invoiceable items and corrected department validation to avoid invalid entries. Improved overall robustness and consistency in invoice-related operations.
Introduced support for user email and phone management, including phone country code and email validation. Added API endpoints for email change and password validation to enhance account security features. Updated Stripe processor logic for better external ID handling.
Introduced logic to handle free subscription addons by setting their price to zero based on a predefined list. Updated affected areas to apply this change consistently when adding or modifying order items. This ensures correct pricing and aligns with business rules for these products.
Introduce a new POST endpoint to handle adding collected order invoices to Stripe, with necessary validations and logging. Extend `collected_order_invoices_o` to include Stripe integration, fetching details of Stripe payment intents when applicable.
Introduced methods to handle Stripe payment capture and updated related classes and endpoints. Refactored constants for payment processors and integrated order collections to reflect Stripe payments. Ensured robust error handling and session validation for payment operations.
Revised a string constant for transaction purposes to improve clarity. Added fallback values for address, zip code, and city in customer data methods to ensure defaults are returned when data is missing. These changes enhance robustness and readability.
Added functionality to handle vehicle subscriptions in invoices, including validation and item visibility. Introduced department notification SMS capability with endpoints for creating, retrieving, and deleting SMS records. Enhanced invoice drafting logic to exclude items not flagged for inclusion in invoices.
Simplified the loop by removing the special-case handling for the first order. Adjusted variable usage to improve clarity and ensure proper assignment. This streamlines the creation of invoice collections for orders.
Implemented functionality to split collected order invoices, ensuring proper validation and handling processor constraints. Added a notification system to inform departments about new bookings via Slack or SMS. Also, included minor enhancements such as a corrected Gateway API URL and improved form handling for wash certificate emails.
Refactored invoice draft handling to improve error checks, added support for optional fetch skipping, and enhanced currency management. Expanded filtering capabilities with date range and attribute-based filters. Adjusted Nginx config to increase FastCGI read timeout for long-running processes.
Introduce new endpoints for viewing Economic invoice summaries and running draft checks. Implement `economic_tasks` helper for managing draft validations, and enhance `collected_order_invoices_o` and SQL clauses to improve query flexibility, including handling "NOT NULL" filters.
Modified functionalities for creating invoice drafts by adding support for custom dates and enhanced validations. Updated methods now check and validate date formats, handle booked invoice collections, and ensure proper invoice creation workflows.
Introduced new superuser-only routes for handling collected invoices, including viewing invoices per order/month, invoice totals, and transactions. Enhanced validation for invoice closure with processors and added support for database views in multiple classes and objects. These updates improve backend functionality and data granularity while maintaining strict permission control.
Introduced dynamic currency handling in invoice generation by adding methods to set currency and retrieve conversion rates. Adjusted logic to ensure accurate currency conversions when processing orders, draft invoices, and customer data. Default behavior falls back to DKK if no currency is specified.
Implemented a new booking confirmation email template and its integration with email sending functionality. Updated form handling to trigger confirmation email after submission and enhanced error handling on department validation.