- Use FD/RD transaction ID prefixes for Fixed/Recurring Deposit GL entries
via ThreadLocal context set in Cash/AccrualBasedAccountingProcessorForSavings
- Add depositAccountType to SavingsDTO and pass from SavingsAccount bridge data
- Add savingsAccountNo field to JournalEntryData; populate via LEFT JOIN on
m_savings_account (SB/FD/RD) and m_current_account (CA prefix) in read query
- Fix JournalEntryMapper MapStruct: ignore savingsAccountNo (SQL-populated)
- Fix JournalEntryRunningBalanceUpdateServiceImpl constructor call (27 params)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pre-save validator (ClientWritePlatformServiceJpaRepositoryImpl.validateClientUniqueness)
rejects duplicate mobile / email / PAN / Aadhaar (masked) / GSTIN before saveAndFlush
and before the LincPay KYC API call. Avoids burning vendor credits on identity-duplicate
records and gives the UI a clean field-level 403 instead of waiting for the DB constraint.
Splits the new repository finders into "create" and "excluding-self" variants so a
nullable Long parameter never reaches PostgreSQL's parser (same bigint <> varchar trap
that broke the family-member POST earlier).
Also seeds the full set of 28 Indian States + 8 UTs as STATE code_values (migration
0297). Previously only MAHARASHTRA / MADHYA_PRADESH / DELHI existed and the names were
UPPER_SNAKE_CASE; the postal PIN-lookup service returns "Madhya Pradesh" so any state
other than Maharashtra/Delhi silently failed to resolve to m_address.state_province_id.
Renames the 3 existing rows in place (preserves their ids/FKs) and inserts the missing
33 with idempotent preConditions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The relationship-uniqueness query used (:excludeId IS NULL OR fm.id <> :excludeId).
On create, excludeId is null and PostgreSQL parses the parameter as varchar before
the IS NULL branch can short-circuit, so adding a family member returned 403 with
"operator does not exist: bigint <> character varying". Split into two queries so
neither has a nullable Long parameter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings TOTP / Google Authenticator MFA factor (device mapping at
/v1/mfa/totp/setup + /verify, login via existing twofactor pipeline),
DSC-2FA login UI fix, Individual-only filter on guardian/nominee/family
link pickers, Title Case normalization on client names, and migration
0296 unique nominee per account type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts:
# .claude/settings.local.json
# fineract-provider/logs/fineract.log
Backend:
- New dependency dev.samstevens.totp:totp:1.7.1 (bundles ZXing for QR PNG).
- TotpService: secret/QR/otpauth-URL/verify primitives. SHA1, 6 digits, 30s —
the standard combo every consumer authenticator app accepts unconfigured.
Issuer label is configurable via FINERACT_MFA_TOTP_ISSUER, defaults to
"CBSnexora".
- Two-step device mapping driven by TotpEnrollmentService:
* POST /v1/mfa/totp/setup — generate secret, upsert m_user_mfa_factor row
as factor_type=TOTP/verified=false, return {secret, qrDataUri, otpAuthUrl,
issuer, factorId}. Refuses to overwrite an already-verified-and-active
factor so an accidental click can't unbind the user's working device.
* POST /v1/mfa/totp/verify — body {code}; verify against the stored secret
and flip verified=true on success.
- Login wiring in MfaFactorBackedTwoFactorServiceImpl:
* getDeliveryMethodsForUser surfaces verified TOTP factors as
{name:"totp", target:"Google Authenticator"} so the picker offers it
alongside SMS/EMAIL.
* createNewOTPToken("totp") creates a no-op OTPRequest (no LincPay/SMTP
round-trip) carrying the factor id in the token field so validate can
look up the secret.
* createAccessTokenFromOTP branches on channel — TOTP path verifies code
vs factor.secret with totp library (sliding 1-step window) and mints
a TFAccessToken with the same lifetime + extended-token logic as OTP.
* Defence-in-depth: re-checks at validate time that the factor still
belongs to the user, is verified, and not disabled.
- TwoFactorConstants.TOTP_DELIVERY_METHOD_NAME = "totp".
- SecurityConfig: /api/*/mfa/totp/** is fullyAuthenticated() (exempt from
the TFA gate so users can enrol before they can satisfy 2FA).
UI changes pending (see chat): add a "Google Authenticator" tile on the
MFA Factors settings page that calls /setup → shows QR + secret → calls
/verify; extend two-factor-authentication.component.ts iconFor() and copy
for the new "totp" delivery method. Existing requestOTP / validateOTP
pipeline handles the login flow transparently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- DSC-as-2FA login: UI's claimAndValidate() now calls a new
AuthenticationService.applyTwoFactorToken(token, liveSeconds) instead of
validateOTP(). The /v1/login/dsc/{rn}/claim response is already a minted
TFAccessToken — POSTing it to /twofactor/validate fails because that path
expects an in-memory OTPRequest. applyTwoFactorToken runs only the
post-success bookkeeping (interceptor.setTwoFactorAccessToken, setCredentials,
Authentication Success alert that drives router.navigate(['/']), and
sessionStorage persist of {token, validFrom, validTo}). Fixes the
OTPTokenInvalidException users hit after a successful DSC verify+claim.
- Individual-only filter for link-client pickers: ClientTextSearch gains a
legalFormType field; SearchingClientRepository accepts an Integer legalForm
value and adds cb.equal(r.get("legalForm"), ...) to the spec.
ClientSearchService.parseLegalForm tolerates INDIVIDUAL/PERSON/1 and
NON_INDIVIDUAL/ENTITY/2; unknown = no filter so a typo doesn't blank results.
UI: clients.service.ts searchByText gains a 7th optional arg; six link
pickers (guardian, nominee, family-member across create flow and post-create
tabs) now pass 'INDIVIDUAL'. General client list and signatory picker are
intentionally unchanged.
- Title-case client names on persistence: ClientWritePlatformServiceJpa
RepositoryImpl now runs first/middle/last/full name fields through
toTitleCase() on both create and update paths. "aditya kumar vyas",
"ADITYA KUMAR VYAS", "Aditya Kumar Vyas" all persist as "Aditya Kumar Vyas".
Whitespace-split title case; preserves hyphens/apostrophes inside words
without re-capitalising after them so clerks verifying against ID documents
see predictable output.
- Migration 0296: unique constraint to prevent duplicate nominee per account
type (the underlying repository + service changes also persisted in this
commit complete the enforcement path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- m_user_mfa_factor is now the single 2FA gate. DSC enroll callback writes a
factor row (factor_type=DSC), revoke flips disabled_on when the last cert
goes. AuthenticationApiResource and TwoFactorAuthenticationFilter drop the
direct m_bank_user_dsc check so disabling from the UI actually stops the
challenge at login.
- SecurityConfig exempts /api/*/dsc/** and /api/*/mfa/factors from the
TWOFACTOR_AUTHENTICATED gate. Without this a user with a verified factor
can't manage their own factors after they break the device.
- MfaFactorType gains a DSC enum value; SelfUserMfaFactorServiceImpl error
message lists it.
- dsc-helper: CBSnexora-branded header (logo, title, subtitle, accent palette),
8-column cert table (Holder Name, Alias, Organisation, DSC Type, Serial,
Issuer, Valid From, Expires), LdapName-based DN parsing, sign+verify proof-
of-possession that picks SHA256withRSA / SHA256withECDSA from the key's own
algorithm (works on RSA, RSA Signing-only, EC), pattern-matched JNLP age
check, dscType JNLP-arg override with KeyUsage fallback.
- generate-test-dsc.ps1 produces a realistic self-signed DSC with strong key
protection so dev testing sees real CN/O values and a PIN prompt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- m_bank_user_dsc + m_dsc_challenge tables (Liquibase 0292/0293/0294)
- DSC enrollment flow: start → JNLP → JWS helper → callback → upsert
- DSC login factor: challenge → sign → verify → claim minted TFA token
- 2FA gate now also accepts an active DSC mapping (AuthenticationApiResource
+ TwoFactorAuthenticationFilter), defensively wrapped so a missing DSC table
doesn't 500 the auth path
- TenantAwareBasicAuthenticationFilter exempts /api/v*/dsc/helper.jar and
strips Authorization header for that path so JWS's raw GET works without
the user seeing a browser Basic-auth prompt
- DSC helper JAR served at /api/v*/dsc/helper.jar from a configurable
filesystem path (default ~/.cbsnexora/dsc-helper.jar)
Java helper JAR (dsc-helper/):
- Standalone Gradle subproject, Java 8 target, fat jar with Gson shaded in
- Cross-platform token picker (Windows Cert Store / ePass2003 / eToken /
ProxKey / Other) — matches the JNLP arg contract emitted by the backend
- Sets Fineract-Platform-TenantId on its callback POST from the JNLP args
- JNLP age check now reads cbsnexora-dsc.jnlp (current name) AND
SignTender.jnlp (legacy) — picks the youngest, never blocks if none exist
Inline KYC upsert in updateClient (mirror of the create-flow path):
- ClientKycWritePlatformService.upsertKycInline() — creates or merges KYC
fields, bumps status to IN_PROGRESS on real changes; resets PAN verification
on PAN edit of a verified record
- ClientWritePlatformServiceJpaRepositoryImpl.updateClient() handles the
kyc:{} sub-object on PUT /clients/{id}, matching createClient
User uniqueness:
- m_appuser.email + m_appuser.mobile_no UNIQUE (Liquibase 0295)
- Pre-flight assertEmailAndMobileUnique() in create/update with self-exclusion
- handleDataIntegrityIssues maps the two new constraints to clean
field-scoped error messages (race-condition safety net)
Email OTP subject line now: "Your OTP for Login - <AppName> (dd-MM-yy-HH:mm)"
where AppName comes from fineract.email.from-name (default "CBSnexora").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Login 2FA via @Primary MfaFactorBackedTwoFactorServiceImpl (replaces legacy)
- LincPay SMS OTP and SMTP email OTP with auto-promote to m_user_mfa_factor
- Reusable EmailSenderService + EmailTemplateService (CBSnexora branding)
- Admin OTP audit viewer (GET /v1/otp-logs) with paging, search, masking
- Self-service MFA factor API at /v1/mfa/factors
- READ_OTP_LOG permission + OTP Log Auditor role + bankadmin direct grant
- JsonAuthenticationEntryPoint suppresses Basic-auth popup for SPA paths
- TwoFactorAuthenticationFilter grants TWOFACTOR_AUTHENTICATED when no challenge
- Soft-delete + audit columns + deletion audit + version on client tables
- CIF number generator (TTYYYYNNNNNN format)
- DocumentNotFoundExceptionMapper to silence noisy WARN logs on missing images
- Liquibase migrations 0278-0291 (MFA, OTP log, audit/soft-delete, role wiring)
Note: application.properties intentionally not committed — contains local DB
and Gmail App Password fallbacks. Set SPRING_MAIL_PASSWORD,
FINERACT_DEFAULT_TENANTDB_PWD, FINERACT_HIKARI_PASSWORD as env vars in prod.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- GET /ckyc/reports/summary — KPI cards (total, gender split, missing PAN/Aadhaar, dup mobile)
- GET /ckyc/reports/state-distribution — donut chart by state
- GET /ckyc/reports/branch-distribution — top-N branches bar chart
- GET /ckyc/reports/upload-trend — daily upload line chart
- GET /ckyc/customers — paginated, filterable customer list
- GET /ckyc/customers/search — search by name/mobile/pan/cif (capped 50)
- GET /ckyc/customers/{cif} — customer detail with addresses
- GET /ckyc/customers/export — CSV/TXT (pipe-delimited CERSAI format)
- POST /ckyc/files — parse inbound CKYC .txt file
- GET /ckyc/files — list historical uploads
- POST /ckyc/files/generate — generate outbound submission file
- GET /ckyc/branches, /states, /codes/id-types — master data
- Migration 0259: performance indexes on m_client_kyc and m_client
- Postman collection with all endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Migration 0256: EducationLevel & DegreeQualification codes with m_client FK columns
- Migration 0257: Religion & SocialCategory codes with m_client FK columns
- Migration 0258: agriculture_income & non_agriculture_income decimal columns on m_client
- Full API support: create, update, template options, and GET response for all new fields
- Postman collection for testing all new dropdowns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added accountTypeOptions and customerSegmentOptions fields to SavingsAccountData and RecurringDepositAccountData
- Added CodeValueReadPlatformService to SavingsAccountTemplateReadPlatformServiceImpl, CurrentAccountTemplateReadPlatformServiceImpl, and DepositAccountReadPlatformServiceImpl to fetch options from code values
- Added options to SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS and RD response parameters
- Updated SavingsConfiguration bean factory methods to pass new CodeValueReadPlatformService dependency
- Added validCheckSum for modified Liquibase changeset 0272
- Fixed AccountNumberGeneratorTest constructor to include GLAccountRepository and SavingsProductRepository
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>