- Add CommitteeMeetingAgenda entity + repository for relational agenda storage
- Liquibase migration 0353: create m_election_committee_meeting_agenda table,
drop old agenda TEXT column from m_election_committee_meeting
- Update CommitteeMeeting: replace String agenda with List<CommitteeMeetingAgenda>
(EAGER, no cascade — writes handled explicitly via repository)
- Update CommitteeMeetingRepository: explicit JPQL with SELECT DISTINCT + LEFT JOIN
FETCH to load agendaItems and prevent NonUniqueResultException on Optional returns
- Fix email sending stale agenda: inject EntityManager, call refresh() after
agendaRepository.flush() so EclipseLink reloads the collection from DB
- Redesign buildMeetingInviteHtml: table-based email layout, icon+label+value rows,
amber agenda section with numbered badges, compact two-column footer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ElectionConfig: add isCandidateModificationLocked() — blocks remove/update/symbol after voting starts
- ElectionWritePlatformServiceImpl: full audit logging on all 16 operations; failures logged in new transaction (REQUIRES_NEW) so they survive main tx rollback
- DirectorAppointmentData: add memberId field for client photo lookup
- DirectorAppointmentServiceImpl: batch-fetch candidate memberId via ElectionCandidateRepository; pass to response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add durationYears to ElectionConfig with Liquibase migration (0348)
- Block new election creation when previous election duration is still active (PostgreSQL-compatible native query)
- Auto-override director tenureEndDate from election durationEndDate on appoint and update
- Add ElectionSchedulerConfiguration to enable @Scheduled support
- Fix JPQL EclipseLink enum compatibility in expireByTenureDate query
- Add full audit logging to DirectorAppointmentServiceImpl (appoint, update, remove, auto-expire)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DirectorAppointment entity + Liquibase migration (0347)
- CRUD API: POST/GET/PUT/DELETE /v1/election/directors with @Valid Bean Validation
- GET /v1/election/directors/pending — WON candidates not yet appointed
- Duplicate prevention: existsByElectionIdAndCandidateId check on appoint
- Business validation: tenure end must be after start; tenure start cannot be before appointment date
- @Scheduled daily midnight job to auto-expire tenure-ended directors via bulk JPQL UPDATE
- Full audit logging for all director actions (success + failure) via ElectionAuditService
- PendingDirectorData DTO; DirectorAppointmentStatus enum (ACTIVE/RESIGNED/REMOVED/TENURE_ENDED)
- ElectionAuditAction: added APPOINT_DIRECTOR, UPDATE_DIRECTOR_APPOINTMENT, REMOVE_DIRECTOR_APPOINTMENT + FAILED variants
- ElectionResultRepository: findByResult(String) for pending director lookup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ElectionAnalyticsServiceImpl: fix SQL_FAMILY to count candidate's own family members who are active shareholders (candidate-specific, not system-wide)
- ElectionAnalyticsServiceImpl: SQL_SUB_CASTE now groups by m_client.lastname instead of sub_caste_cv_id
- ElectionAnalyticsServiceImpl: SQL_CANDIDATE_INFO fetches candidate client_id, lastname, religion for match highlighting
- ElectionAnalyticsServiceImpl: add getOverallShareholderAnalysis() — returns all demographic breakdowns with no candidate matching
- ElectionAnalyticsService: add getOverallShareholderAnalysis() interface method
- ElectionCandidateApiResource: add GET /{electionId}/shareholders/analysis endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New ElectionAnalyticsServiceImpl with 14 SQL breakdown queries:
gender, age, area, social category (caste), sub-caste, religion,
occupation, share-holding, tenure, loan, education, marital status,
monthly income, savings account depth
- Cross-dimension match: caste + area simultaneously (core voter base)
- Zero-count row injection for caste/gender/sub-caste when candidate's
category has no shareholder matches in GROUP BY result
- Liquibase 0343: sub_caste_cv_id column on m_client with 33 pre-seeded
sub-caste values across General / OBC / SC / ST categories
- Liquibase 0342: election post eligibility columns
- CandidateVoterAnalysisData DTO with education, marital, income,
savings, activeSavingsHolders, crossMatchCount fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete all fineract-provider/logs/*.log from version control
- Add fineract-provider/logs/ to .gitignore so runtime logs are
never tracked again
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ElectionCandidateData: replace String symbol with Boolean hasSymbol + add memberId
- ElectionCandidateApiResource: symbol upload/download via ContentStoreService
(mimeType|storagePath format, backward-compatible with legacy base64 records)
- ElectionVotingApiResource: add GET /vote-summary endpoint (totalVotes, totalVoters,
per-candidate breakdown) using ElectionVoteRepository queries
- ElectionVoteRepository: add countByElectionConfigId and voteSummaryByCandidate queries
- ElectionReadPlatformServiceImpl: map hasSymbol and memberId in candidate list response
- ElectionWritePlatformServiceImpl: set photoUrl from memberId on approveCandidate,
delete old symbol file from ContentStore on removeCandidate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PagedResponse<T>: generic paginated wrapper with statusCounts and summary fields
- ElectionConfigRepository: pageable query + countGroupByStatus
- ElectionNominationRepository: pageable queries + count-by-status helpers
- ElectionCandidateRepository: pageable queries by election and post
- ElectionReadPlatformService: all list methods return PagedResponse with page/size params
- ElectionReadPlatformServiceImpl: add missing Pageable import; paginated impls with summary data
- API resources: @QueryParam page/size with @DefaultValue on all 3 list endpoints
- ElectionCeoPrecheck: add familyLoanCheckResult + familyLoanCheckRemarks (7th auto-check)
- ElectionCeoPreCheckServiceImpl: fix directorLoan check (overdue only); add family loan check covering manual members (unverifiable→FAIL) and linked CBS clients (overdue→FAIL)
- Liquibase 0338/0339: extended CEO pre-check columns + family loan check columns
- changelog-tenant.xml: include both new changesets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add borrower name, savings account, guaranteed/released/remaining amounts, loan timeline, and overdue info to GuarantorAccountSummaryData
- Fix duplicate rows in GuarantorLoanAccountSummaryDataMapper by joining savings account via guarantor funding details instead of client ID
- Fix NPE in GuarantorDomainServiceImpl when loan product guarantee details are null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 5 missing permissions were added to the already-executed changeset
causing checksum mismatch on restart. Move them to a new changeset
0337_seed_election_permissions_v2 with a preCondition guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 5 permissions that the write service enforces but were absent from
the DB seed: UPDATE_ELECTION, ELIGIBILITY_CHECK_ELECTION,
CEO_PRECHECK_ELECTION, APPROVE_ELECTION_NOMINATION,
APPROVE_ELECTION_CANDIDATE — without these, role assignments for those
operations would fail at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ShareAccountWritePlatformServiceJpaRepositoryImpl:
- Remove m_share_cashier_collection validation from activateShareAccount() —
cashier now enters GL directly via transaction entry workstation
- Mark all share account charges as paid on activation instead of posting
duplicate GL (cashier already posted DR Cash -> CR Charges GL directly)
- Remove unused imports: java.sql.Date, EmptyResultDataAccessException, Collectors
- Update createShareAccount() comment to reflect new cashier GL flow
ClerkDisbursementRequestApiResource:
- Add null guard for clerk_id before cast in approve() — returns 500 with
clear message if DB record is missing the field
- Add null/blank guard for credit_entries in approve() — returns 400 with
clear message instead of NPE on gson.fromJson
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace Fineract cash deposit API with direct INSERT for both savings and current accounts
→ Zero CashVault entries created anywhere in DB for disbursements
- Savings: directCreditSavingsAccount now sets running_balance_derived and updates
total_deposits_derived alongside account_balance_derived (parity with current account)
- Current: directCreditCurrentAccount — direct INSERT into m_current_account_transaction
with running_balance_derived, total_deposits_derived update, and remarks audit field
- getCurrentControlGlId: looks up SAVINGS_CONTROL GL for current account products
via acc_product_mapping (product_type=2, financial_account_type=2)
- directReverseCurrentCredit: compensation restores both account_balance_derived
and total_deposits_derived on rollback
- GL: combined DR sourceGL / CR SavingsControl entry posted for both savings and
current account credits; linkGlEntriesToSavingsAccounts sets entity_type_enum=2
so ACCOUNT NO column shows in journal entries view
- Hide reversed=true GL entries from all GL views in JournalEntryReadPlatformServiceImpl
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use parseTxnIdToLong() for voucherService.linkSavingsTransaction() to
handle hex GL txnId (e.g. "a2070e542bab") without NumberFormatException
- Add GL deposit maker-checker: submit stores gl_account_id, contra_gl_id,
office_id, currency_code; approve routes to executeGlJournalEntry()
- Save denomination at submit time (cash physically received); update
txn_id to real Fineract ID on approve, delete on reject
- Add Liquibase migration 0331 to add GL columns to m_cashier_deposit_request
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CashierDepositRequestApiResource:
- approve(): use isCurrentAccountById() to detect current accounts, route to
executeCurrentAccountDeposit() instead of executeSavingsDeposit()
- Add executeCurrentAccountDeposit() using CommandWrapperBuilder.currentAccountDeposit()
- Add isCurrentAccountById() helper querying m_current_account table
- Add POST /{id}/reprocess endpoint to fix already-approved CA records that
were wrongly posted to savings account
ClerkWithdrawalRequestApiResource:
- approve(): use isCurrentAccountById() to detect current accounts, route to
executeCurrentAccountWithdrawal() instead of executeSavingsWithdrawal()
- Add executeCurrentAccountWithdrawal() using CommandWrapperBuilder.currentAccountWithdrawal()
- Add isCurrentAccountById() helper querying m_current_account table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- VoucherService: central voucher register with globally unique voucher numbers
- CashierDepositRequestApiResource: full submit/approve/reject/list flow
- Cashier submits deposit request; balance credited only on manager approval
- Self-approval blocked (maker cannot be checker)
- Role guard: only manager/head-cashier/ALL_FUNCTIONS can approve/reject
- Race condition guard: WHERE status='PENDING' on approve/reject UPDATE
- Clean JSON error responses (no 500 stack traces) via apiError() helper
- Denomination total validation at submit and approve
- ClerkWithdrawalRequestApiResource: refactored to use m_voucher
- Voucher created at submit, linked to savings txn at approve
- Same role guard and self-approval block as deposit flow
- Disburse: denomination validation, null denoms for non-cash payments
- Migrations 0327-0330:
- 0327: add voucher_no to m_clerk_withdrawal_request
- 0328: add voucher_no to m_savings_account_transaction
- 0329: create m_voucher central table (globally unique voucher_no)
- 0330: create m_cashier_deposit_request table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add offset/limit query params with defaults (limit=20, offset=0)
- Return totalFilteredRecords + pageItems response structure
- Compute totalDr/totalCr from all records (not just current page)
- Fix ordering: ORDER BY MAX(created_at) for correct chronological sort
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CashierGlDenominationApiResource: new POST /v1/cashier/gl-denomination endpoint
to record denomination breakdown for GL journal entries in m_savings_txn_denomination
- Fix GL account real-time balance in GLAccountReadPlatformServiceImpl using live SUM query
- Add Liquibase migrations 0318/0319/0320: make savings_account_id and txn_id nullable,
add payment_type column to m_savings_txn_denomination
- Add validCheckSum to 0317b changeset to resolve checksum mismatch on existing installs
- Enhance GL entries closing report SQL with date, time, teller, payment type, txn source
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix searchParam SQL syntax: parameterized LIKE with %value% instead of literal %?%
- Fix organizationRunningBalance: correlated subquery for latest journal entry (ORDER BY id DESC LIMIT 1)
- Migration 0317: grant READ_GLACCOUNT, READ_LOAN, READ_SHAREACCOUNT to CASHIER role
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CurrentAccountTransactionsApiResource:
- Fixed: search endpoint now queries m_current_account_transaction
instead of m_savings_account_transaction (wrong table)
- Added remarks column to search query (column exists in CA table)
- Charge Deduction (type=7) mapped in transactionType value
- Removed sat.remarks that caused 500 (column doesn't exist in savings table)
CurrentAccountTransactionWritePlatformServiceImpl:
- Added min required balance validation in withdrawal()
- Throws GeneralPlatformDomainRuleException when balance after withdrawal
would fall below minRequiredBalance (if enforceMinRequiredBalance=true)
StandingInstructionReadPlatformServiceImpl:
- Fixed SQL WHERE clause: fromAccountType + fromAccount condition
now correctly adds WHERE when fromAccountId is set
Liquibase 0315:
- Fix standing instruction permission code (trim trailing space on id=449)
- Grant READ permissions to CASHIER role for transaction entry screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CashierRoleHelper service: detects CASHIER role via DB permission
check, resolves cashier ID from staff/user linkage
- Role-based filtering on GET list endpoints (cashtransfer, cashsettlement,
cashintercashier, cashexchange, cashreports): CASHIER sees only own data,
ADMIN/HEAD sees all
- Pagination (offset/limit) added to all cashier list APIs returning
standard Fineract Page format {totalFilteredRecords, pageItems}
- ShareCollectionApiResource: collect cash before account activation,
post GL entries (Dr Cash Vault / Cr Share Sundry / Cr Income from Fees),
mark charges paid to prevent duplicate GL on activation,
GET status endpoint for collection check
- Migration 0315: grant READ_CLIENTKYC, READ_CLIENTNOMINEE, READ_ADDRESS,
READ_FAMILYMEMBERS, READ_SAVINGSACCOUNT to CASHIER role
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New POST /cashier/sharecollection endpoint: collects cash for share purchase,
posts Dr Cash Vault / Cr Share Sundry / Cr Income GL entries, marks charges
as paid and sets cashier_collected flag on the account
- GET /cashier/sharecollection/:id endpoint for collection status check
- Block share account activation if cashier has not collected cash first
(validates m_share_cashier_collection record within last 30 days)
- Skip duplicate charge GL on activation when charge already collected upfront
- Suppress automatic GL posting at account submission time to prevent
double entries; GL is now posted only via cashier collection endpoint
- Fix JdbcTemplate injection in ShareAccountWritePlatformServiceJpaRepositoryImpl
via ShareAccountsConfiguration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SavingsAccount.modifyApplication() previously blocked modification unless
the account was in SUBMITTED_AND_PENDING_APPROVAL state. Extended to also
allow REJECTED state so bank users can amend and re-submit rejected applications.
When a rejected account is modified, status resets to
SUBMITTED_AND_PENDING_APPROVAL (rejectedOnDate and rejectedBy cleared)
so the account re-enters the approval workflow.
Applies to Savings, FD, and RD accounts (all extend SavingsAccount).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ALLOCATE (vault→cashier) and SETTLE (cashier→vault) no longer post
DR CASH_AT_TELLER / CR CASH_AT_MAINVAULT journal entries.
Rationale: Cash transfer between vault and teller drawer is an internal
physical movement — the denomination/amount is already tracked operationally
via m_cash_transfer and m_cash_settlement tables. A single Cash in Hand GL
(CASH_AT_MAINVAULT) covers all bank cash. GL entries now only occur on
actual customer deposit/withdrawal transactions (SAVINGS_REFERENCE ↔
SAVINGS_CONTROL), keeping the GL clean and RBI-compliant.
Removed unused imports and field injections:
JournalEntryRepository, FinancialActivityAccountRepositoryWrapper,
FinancialActivity, FinancialActivityAccount, GLAccount,
JournalEntry, JournalEntryType
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DenominationBreakdownWriter: add ORDER BY mc.id DESC LIMIT 1 to all cashier
lookup queries so multiple cashier records per staff do not crash queryForObject
- CashReportApiResource: same LIMIT 1 fix for cashier-day-status and position endpoints
- 0311: fix m_client and m_savings_account NOT NULL columns (created_by, last_modified_by,
created_on_utc, last_modified_on_utc, cif_number, is_lien_allowed, etc.)
- 0312: create CASHIER role if missing + grant all 34 cashier permissions (incl FD/RD)
- 0313: add missing m_cash_exchange columns (cashier_id, is_reversed, reversed_by,
reversed_at, reverse_reason, customer_id_proof)
- 0314: unique constraint on m_cashiers.staff_id to prevent duplicate allocations
- application.properties: restore local postgres password after merge
- ExecuteOverdueAndCurrentStandingInstructionsTest: fix constructor call (5 args)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- validateCashierHasMorningTransfer(): blocks all txns when no today's cash transfer in m_cash_transfer
- validatePreviousDayClosed(): blocks when previous business day unsettled
- Both validations called on deposit AND withdrawal in Savings, Current, FD, RD services
- cashier-day-status endpoint now returns morningTransferReceived field
- getCashierTotalCash SQL fixed: dm.denomination_value -> dm.denomination
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(fundtransfer+imps+mfa): fund transfer module, IMPS switch client, MFA log API
- Fund transfer: NEFT/RTGS/IMPS/internal strategies, limit resolution, bulk
transfer, verification tokens, suspense GL, recall/approval flow, jobs
- IMPS CBS switch client with crypto (JWT/envelope) and transaction domain
- MFA log read API (audit of MFA events)
- Liquibase migrations 0300-0311 for fund transfer / IMPS tables, permissions,
roles, and suspense account
- JobName, SecurityConfig, dependencies.gradle wiring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
- Savings/Current/FD/RD withdrawal services: call validateWithdrawalStock() before
validateBreakdown() to block withdrawal when cashier lacks the requested denomination
- CashExchangeApiResource: validate cashier denomination stock before giving to customer;
fix saveAndFlush (EclipseLink does not flush on save() — getId() returned null causing
exchange_id NOT NULL violation in m_cash_exchange_detail)
- CashReportApiResource excess(): include cash exchanges >= threshold in result,
add type field (SAVINGS_TXN/EXCHANGE) and idProof for RBI/AML compliance;
combined list sorted by amount DESC
- CashReportApiResource closing(): add denomination-wise exchange rows (already present)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /cashreports/cashier-morning: returns denomination-wise cash received by cashier from vault
for a given date (only vault→cashier transfers, non-reversed)
- CashSettlementApiResource: include fromCashierId in list and detail responses
- CashTransferApiResource: include toCashierId in list and detail responses for traceability
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>