fix(crm): thread trx through sequence-claim sites to unblock SQLite

Reviewer feedback on #555: nextQuoteNumber inside createQuote's
db.transaction was called without passing the outer trx, so
claimNextSequence opened its own connection — Postgres tolerated this
via the pool, SQLite (1-connection default) deadlocked on every quote
creation.
Audited the same pattern across invoiceService + contractService and
found five more matching call sites:
  - createInvoice (single-row path after installment auto-route)
  - spawnInstallmentInvoices (per-sibling claim inside the loop)
  - createStorno
  - createContract
  - createFromQuote
All now thread trx through to nextXxxNumber → claimNextSequence so
the claim joins the caller's transaction on both engines.
convertToInvoiceOnly's Path B (standalone-contract) is the lone
remaining nextInvoiceNumber() call without trx — that path isn't
wrapped in a transaction at all (separate concern: sequence-number
leak on insert failure, tracked separately).
This commit is contained in:
Luca
2026-05-27 22:08:38 +02:00
parent 5ce0b6edc3
commit d1aecaa180
3 changed files with 18 additions and 6 deletions
+6 -2
View File
@@ -765,7 +765,9 @@ async function createContract(payload, adminId) {
const hasEventCols = await hasColumnCached('contracts', 'event_name');
return await db.transaction(async (trx) => {
const contractNumber = await nextContractNumber();
// Pass trx so the sequence claim joins our outer transaction —
// SQLite deadlocks otherwise (1-connection default).
const contractNumber = await nextContractNumber(trx);
const row = {
contract_number: contractNumber,
customer_account_id: payload.customerAccountId,
@@ -1594,7 +1596,9 @@ async function createFromQuote(quoteId, adminId) {
const hasContractEventCols = await hasColumnCached('contracts', 'event_name');
return await db.transaction(async (trx) => {
const contractNumber = await nextContractNumber();
// Pass trx so the sequence claim joins our outer transaction —
// SQLite deadlocks otherwise (1-connection default).
const contractNumber = await nextContractNumber(trx);
const contractRow = {
contract_number: contractNumber,
customer_account_id: quote.customer_account_id,