Same submit()/add_comment-in-except shape as the PO->SCO mapper: on Postgres a failed submit aborts the transaction so the follow-on Comment insert raises InFailedSqlTransaction; MariaDB continues. Savepoint + rollback before add_comment. No-op on MariaDB.
target_doc.submit() is wrapped in except Exception whose handler calls add_comment (a Comment insert). On Postgres a failed submit poisons the transaction so the add_comment insert raises InFailedSqlTransaction; MariaDB logs the comment. Savepoint + rollback before add_comment. No-op on MariaDB.
Purchase Receipt stock and asset GL lines used the item row's cost center
but always fell back to the document-level project, unlike Purchase Invoice
which uses the item-level project. add_gl_entry accepted a project argument
but never wrote it to the GL dict, so the inward, Stock Received But Not
Billed, landed cost, divisional loss, sub-contracting and exchange rate
lines dropped the row's project.
Write project into the GL dict and pass project=item.project on the entries
that were missing it, so project behaves like cost center and matches
Purchase Invoice.
Ticket: 72523
get_last_sales_amt ordered by the sales date DESC only; same-date documents made the reported Last Order Amount engine-dependent. Add name DESC tiebreaker.
get_last_purchase_rate ordered by posting_date DESC only; same-date Purchase Invoices yielded an undefined last_purchase_rate that diverged between MariaDB and Postgres. Add purchase_invoice.name DESC tiebreaker.
get_latest_location_and_custodian ordered by transaction_date DESC only; equal-dated movements left the current location/custodian engine-dependent. Add asm.name DESC tiebreaker so both engines pick the same movement.
calculate_exchange_rate_using_last_gle ordered the latest-GLE lookups by posting_date DESC only. With multiple GL Entries on the latest posting_date the picked row was undefined, so MariaDB and Postgres could choose different vouchers and return a different last_exchange_rate (and revaluation gain/loss). Add gl.name DESC as a tiebreaker so both engines pick the same row; MariaDB row count unchanged.
The lead/address cleanup pre-escaped each address name before passing the
list into a query-builder .isin() filter, which escapes again. The
double-escaping produced `name IN ('''Addr''')`, matching nothing, so
Lead-linked Addresses were never deleted on either MariaDB or Postgres.
Pass the raw list straight into .isin() so the builder escapes once.
The "Is Fully Depreciated" field was hidden on the Asset form (hidden: 1),
so it could never be set for manually entered existing assets.
Make it visible based on context:
- Existing Asset with Calculate Depreciation off -> visible and editable
- Calculate Depreciation on -> visible but read-only and forced unchecked
(it is only meaningful for manually entered assets)
The unchecked value is enforced in the form script (immediate feedback on
toggle and on load) and in server-side validate() so it can never be saved
as checked while depreciation is being calculated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>