Files
cbsnexora-backend/cleanup_duplicate_vouchers.sql
T
keshavandClaude Opus 5 e4cc2e558e
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
cash-only registers, day book sections and DD amendments
Cashier reports now reconcile against the drawer:
- Received/Payment exclude what cannot be cash rather than admitting
  only proven cash, so registers that pay out without recording a
  denomination breakdown are no longer silently dropped
- Closing report scoped to the cashier, with opening/closing balance,
  reversed counter transactions excluded, and cash DD receipts counted
- Day book split into Cashier and Clerk sections, filed by whose cash
  moved rather than who raised the request

Fixed one deposit or withdrawal raising two vouchers: the cashier
deposit and clerk withdrawal registers did not suppress the inner
savings command's voucher, doubling the day's cash on both reports.

Sponsor DD:
- Amendment request flow (beneficiary, payable-at branch and city)
  under maker-checker; face value and funding stay unamendable
- Cash DD denominations were collected but never saved, and the amount
  check demanded a figure notes could not add to; both fixed and the
  breakdown is now required

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 18:45:36 +05:30

69 lines
3.1 KiB
PL/PgSQL

-- Removes the duplicate vouchers left behind by the double-count bug.
--
-- Until 2026-09-03 the cashier deposit and clerk withdrawal registers did not wrap their inner
-- savings command in VoucherService.withOwnVoucher(...), so one counter transaction raised two
-- vouchers over the same money: the register's own (CASHIER_DEPOSIT_REQUEST / CLERK_WITHDRAWAL_
-- REQUEST) and a second one from the savings command (SAVINGS_TRANSACTION). The cashier's Received
-- and Payment reports counted both, doubling the day's cash.
--
-- The code is fixed, so no new duplicates are created. This clears the ones already recorded.
--
-- WHICH ONE IS DELETED: the SAVINGS_TRANSACTION voucher. The register's voucher is the keeper --
-- it is the one the register owns, attaches its DR/CR lines to, and stamps onto
-- m_savings_account_transaction.voucher_no. Deleting the register's voucher instead would break
-- that link and orphan the request row.
--
-- Run: psql -h localhost -U postgres -d fineract_default -v ON_ERROR_STOP=1 -f cleanup_duplicate_vouchers.sql
BEGIN;
-- The duplicates: a SAVINGS_TRANSACTION voucher whose transaction is already claimed by a
-- register voucher. Matching through savings_txn_id rather than by date or amount so that two
-- genuinely separate transactions of the same value on the same day are never confused.
CREATE TEMP TABLE dup_vouchers ON COMMIT DROP AS
SELECT sav.voucher_no
FROM m_voucher sav
JOIN m_voucher reg
ON reg.savings_txn_id = sav.reference_id
AND reg.reference_type IN ('CLERK_WITHDRAWAL_REQUEST', 'CASHIER_DEPOSIT_REQUEST')
WHERE sav.reference_type = 'SAVINGS_TRANSACTION'
AND sav.voucher_type IN ('DEPOSIT', 'WITHDRAWAL');
-- Refuse to run if anything else has attached itself to a voucher about to be deleted: a
-- verification or an attachment means someone acted on that slip, and it must be looked at by hand
-- rather than removed silently.
DO $$
DECLARE
blocked INT;
BEGIN
SELECT COUNT(*) INTO blocked
FROM (SELECT voucher_no FROM m_voucher_verification
WHERE voucher_no IN (SELECT voucher_no FROM dup_vouchers)
UNION ALL
SELECT voucher_no FROM m_voucher_attachment
WHERE voucher_no IN (SELECT voucher_no FROM dup_vouchers)) x;
IF blocked > 0 THEN
RAISE EXCEPTION 'Aborting: % duplicate voucher(s) carry a verification or attachment. Review these by hand.', blocked;
END IF;
END $$;
-- Likewise refuse if a savings transaction is stamped with a voucher about to be deleted. It should
-- carry the register's number, not the duplicate's; if it does not, the pair needs looking at.
DO $$
DECLARE
stamped INT;
BEGIN
SELECT COUNT(*) INTO stamped FROM m_savings_account_transaction
WHERE voucher_no IN (SELECT voucher_no FROM dup_vouchers);
IF stamped > 0 THEN
RAISE EXCEPTION 'Aborting: % savings transaction(s) are stamped with a duplicate voucher number.', stamped;
END IF;
END $$;
SELECT voucher_no AS "about to delete" FROM dup_vouchers ORDER BY voucher_no;
DELETE FROM m_voucher_line WHERE voucher_no IN (SELECT voucher_no FROM dup_vouchers);
DELETE FROM m_voucher WHERE voucher_no IN (SELECT voucher_no FROM dup_vouchers);
COMMIT;