Compare commits

..
500 Commits
Author SHA1 Message Date
Arnold GalovicsandArnold Gálovics 872f97b6b6 FINERACT-2258: Fix for making the Post Interest for Savings job properly work in a multitenant environment
Fineract Docker build - MariaDB / build (push) Canceled after 0s
Fineract Docker build - PostgreSQL / build (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract ActiveMQ Smoke / build (push) Canceled after 0s
Fineract Build Progressive Loan Embeddable Jar & Test with a Sample Application / build (push) Canceled after 0s
Fineract Build & Test - MariaDB / build (push) Canceled after 0s
Fineract Build & Test - MySQL / build (push) Canceled after 0s
Fineract Build & Test - PostgreSQL / build (push) Canceled after 0s
Fineract Tests / build (push) Canceled after 0s
Fineract Kafka Smoke / build (push) Canceled after 0s
2025-04-10 11:01:48 +02:00
mark.vituskaandAdam Saghy 09879800e0 FINERACT-2181: modified id and id related fields in api objects to long datatype 2025-04-10 11:01:25 +02:00
viktorpavlenkoandAdam Saghy 3260f76ca7 FINERACT-2169: Refactor calendar controller and service; 2025-04-10 10:59:59 +02:00
Adam Saghy 64b453e9ab FINERACT-2181: Introduce Loop guard to avoid endless loops 2025-04-10 10:58:44 +02:00
Soma SörösandAdam Saghy 7f9a812048 FINERACT-2220: Make ProgressiveLoanInterestScheduleModel serializable and deserializable 2025-04-09 10:12:51 +02:00
Jose Alberto HernandezandAdam Saghy 8f6b8cbc47 FINERACT-2081: Add ChargeOffReasonOptions in the Loan Transaction Template swagger class 2025-04-08 17:02:22 +02:00
mdmytrivandAdam Saghy 5f4f7ef95f FINERACT-1981: added e2e tests for disbursement charge feature 2025-04-08 16:56:34 +02:00
adam.magyariandAdam Saghy 89f7fd257c FINERACT-1981: progressive loan disbursement charge fixes
- Update PutChargesChargeIdRequest swagger def
- undo disbursement fix
- non-tranche multidisburse fix
- disbursement transaction order fix
2025-04-08 16:56:34 +02:00
viktorpavlenkoandAdam Saghy 0769b41b26 FINERACT-2169: Refactor standing instruction api controller; 2025-04-08 16:39:15 +02:00
viktorpavlenkoandAdam Saghy 056a20f58e FINERACT-2169: Refactor standing instruction history api controller; 2025-04-08 16:38:11 +02:00
Oleksii NovikovandAdam Saghy 2ba7e28108 FINERACT-2080: Extract DTO mappers from loan entity 2025-04-07 13:13:35 +02:00
oleksandr.masliukandAdam Saghy b01dca3ecc FINERACT-2169: Apply dto in ProductMixApiResource 2025-04-07 12:58:32 +02:00
Adam MonsenandAdam Saghy efabd341df FINERACT-2231: remove links unhelpful in future releases
The Fineract dev list thread links were specific to the 1.11.0 release.
It wouldn't make sense to include them in future [VOTE] emails.
2025-04-07 12:56:54 +02:00
Adam MonsenandAdam Saghy 97dd2bbbde FINERACT-2230: improve test and doc building info in README
Update and add detail to doing the most common types of test runs on a developer workstation.

Add info on how to build asciidoc-based documentation.
2025-04-07 12:56:44 +02:00
Adam Saghy 10d281f9fa FINERACT-2211: Fix endless loop issue 2025-04-04 20:03:27 +02:00
Adam Saghy 4213f56a98 FINERACT-2181: Add customData field to LoanChargeBusinessEvent 2025-04-04 15:31:13 +02:00
Rustam ZeinalovandAdam Saghy 9bf365b62d FINERACT-2226: added e2e tests for validation of progressive loan charge-off reverse replay events fix 2025-04-04 14:29:30 +02:00
adam.magyariandAdam Saghy 49feaab3e5 FINERACT-2226: progressive loan charge-off reverse replay events fix 2025-04-04 14:29:30 +02:00
Adam Saghy eb7ec383f8 FINERACT-2190: Improve copyFrom of LoanRepaymentScheduleInstallment 2025-04-04 14:29:10 +02:00
Jose Alberto HernandezandAdam Saghy 52c41b1471 FINERACT-2190: Avoid recreation of loan repayment schedule periods 2025-04-04 14:29:10 +02:00
oleksandr.masliukandAdam Saghy b9cfa470ff FINERACT-2169: Apply dto in LoanCollateralManagementApiResource 2025-04-03 12:09:08 +02:00
Mend RenovateandAdam Saghy 4bbfe6a5f4 FINERACT-2181: Update dependency jakarta.annotation:jakarta.annotation-api to v3 2025-04-03 12:07:59 +02:00
Victor RomeroandAdam Saghy 692a6f0a63 Enable logging
FINERACT-2195 fix
2025-04-03 11:39:51 +02:00
budaidevandAdam Saghy 16e70527ea FINERACT-2080: separate LoanRepaymentScheduleTransactionProcessorFactory from loan 2025-04-03 10:50:50 +02:00
mdmytrivandAdam Saghy 741cdb1e42 FINERACT-2215: added e2e tests for charge-off handling reversed transactions 2025-04-02 13:24:19 +02:00
budaidevandAdam Saghy ba3aa80b7c FINERACT-2215: charge-off fix for reversed transactions 2025-04-02 13:24:19 +02:00
oleksandr.masliukandAdam Saghy a0a6d3aa14 FINERACT-2169: Apply dto in ClientCollateralManagementApiResource 2025-04-02 10:55:53 +02:00
Oleksii NovikovandAdam Saghy f3606bb2d1 FINERACT-2225: Save journal entries for accrual transaction created by charge adjustment 2025-04-01 14:23:55 +02:00
oleksandr.masliukandAdam Saghy 510b4853e7 FINERACT-2169: Apply dto in CollateralManagementApiResource 2025-04-01 10:02:47 +02:00
mariiaKraievskaandAdam Saghy 957666df18 FINERACT-2210: Incorrect accounting of reversed charge-off transaction. 2025-04-01 10:01:13 +02:00
oleksandr.masliukandAdam Saghy b7ba9bd570 FINERACT-2169: Apply dto in CollateralsApiResource 2025-04-01 10:01:00 +02:00
Adam Saghy 833f4a0d33 FINERACT-2228: Fix incorrect current balance calculation and unintentional transaction reversals 2025-04-01 08:50:14 +02:00
Peter MwangiandGitHub b7845166c4 FINERACT-2227: Fix triggered SMS messages error (#4511) 2025-03-31 20:43:30 -06:00
Adam Saghy 43bf94cd8f FINERACT-2229: Fix incorrect repayment schedule in case of Merchant issued refund 2025-03-31 17:11:15 +02:00
Abhinav CillankiandAdam Saghy be9e44fa44 FINERACT-2215: Exclude reversed transactions from hasMonetaryActivityAfter check 2025-03-31 14:17:52 +02:00
viktorpavlenkoandAdam Saghy 97515fcba9 FINERACT-2169: Refactor external assets owner api controller; 2025-03-31 11:23:47 +02:00
oleksandr.masliukandAdam Saghy 53c36ba4f8 FINERACT-2169: Apply dto in DelinquencyApiResource 2025-03-31 10:38:38 +02:00
Adam Saghy a313608cd3 FINERACT-2181: Use correct loan status conversion for Loan status change history 2025-03-28 21:26:07 +01:00
abudai-moveandAdam Saghy 65583a1661 FINERACT-2207: add both of the validCheckSum to make it work all of the dbs 2025-03-28 12:23:59 +01:00
abudai-moveandAdam Saghy c613d9c0fe FINERACT-2207: fix jdbc connection string in liquibase script
FINERACT-2207: add valid checksum
2025-03-28 12:23:59 +01:00
viktorpavlenkoandAdam Saghy 1b9459b7b4 FINERACT-2169: Refactor field configuration api controller; 2025-03-27 15:08:37 +01:00
Rustam ZeinalovandAdam Saghy 65b74ee87b FINERACT-2148: added e2e test covering charge-off after backdated repayment for cumulative loan 2025-03-27 15:05:14 +01:00
mariiaKraievskaandAdam Saghy acabf16003 FINERACT-2148: Cumulative Loan - accrual handling in case of charged-off loan 2025-03-27 15:05:14 +01:00
Adam Saghy 0fc1c3e20e FINERACT-2181: Upgrade Spring dependencies 2025-03-27 14:07:40 +01:00
viktorpavlenkoandAdam Saghy bb5078b5da FINERACT-2169: Refactor account transfer api controller; 2025-03-27 11:45:35 +01:00
Oleksii NovikovandAdam Saghy e584e26c39 FINERACT-2191: ThreadLocal context handling during job execution 2025-03-27 11:44:12 +01:00
Arnold GalovicsandArnold Gálovics 0f1d53cbdc FINERACT-2181: Fix for using the proper ChargeData object in LoanProduct APIs 2025-03-26 22:49:44 +01:00
Adam SaghyandArnold Gálovics 83039b498a FINERACT-2181: Downgrade postgres driver - performance issue with v42.7.5 2025-03-26 13:24:51 +01:00
Adam SaghyandArnold Gálovics 4792484336 FINERACT-2181: Downgrade postgres driver - performance issue with v42.7.5 2025-03-26 13:24:51 +01:00
Arnold GalovicsandArnold Gálovics 134b097a5e FINERACT-2181: Added threadlocal context reset during parallel accrual processing 2025-03-26 13:24:32 +01:00
dependabot[bot]andArnold Gálovics 961e6d1aa6 Bump gradle/actions from 4.3.0 to 4.3.1
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/94baf225fe0a508e581a564467443d0e2379123b...06832c7b30a0129d7fb559bcc6e43d26f6374244)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 11:15:47 +01:00
Adam Saghy f47b2b3fc2 FINEREACT-2181: Ignore null values in customData field 2025-03-26 10:09:35 +01:00
oleksandr.masliukandAdam Saghy 79c08ed7c9 FINERACT-2169: Apply dto in SearchApiResource 2025-03-26 10:03:19 +01:00
oleksandr.masliukandAdam Saghy f73b352727 FINERACT-2169: refactoring FloatingRatesApiResource - update swagger docs 2025-03-26 10:01:45 +01:00
Arnold GalovicsandArnold Gálovics 7dd058b5f1 FINERACT-2181: Made Add Accrual Transactions job parallel on a loan-basis 2025-03-26 09:55:28 +01:00
Peter KovacsandAdam Saghy 82031f46d0 FINERACT-2181: allure report target directory changed 2025-03-25 15:02:20 +01:00
viktorpavlenkoandAdam Saghy b646ba6ca7 FINERACT-2169: Refactor loan COB catch up api controller; 2025-03-25 12:11:29 +01:00
viktorpavlenkoandAdam Saghy 20195464c9 FINERACT-2169: Refactor macker checkers api controller; 2025-03-25 12:10:27 +01:00
mariiaKraievskaandAdam Saghy 2d2cbb46b5 FINERACT-2181: Move the remaining transactions reprocessing logic out of the loan entity to ReprocessLoanTransactionsService. 2025-03-25 12:09:28 +01:00
Jose Alberto HernandezandAdam Saghy f3bb66f209 FINERACT-2197: Introduce a new type of ACTUAL during calculation of days in year, update 2025-03-24 19:30:44 +01:00
Adam MonsenandAdam Saghy 3f2079566c FINERACT-2219: capture v1.11.0 lessons learned in asciidoc and fineract release plugin
* don't bother making MD5 files, they aren't necessary
* step12 changed, stub it out and reference FINERACT-1817 for future improvements (looks like svn mv is not yet supported)
* use latest vote message template, based on https://lists.apache.org/thread/cs5t0ho39ktg2jxr74dqd7fg9nyfcz3k
* use more explicit form of `git push` to push release tag, see https://stackoverflow.com/questions/5195859/how-do-you-push-a-tag-to-a-remote-repository-using-git
* use consistent language re: tarball, file, artifact, release candidate
* fix some source listings that shouldn't be using macro expansion: this messes up URLs (hides scheme)
* fix asciidoc link to FINERACT-1229
* clean up copyright years: use variable properly and remove unnecessary extra copyright in preface (it appears directly above, in the license)
* de-indent URL in apache license -- this makes the link clickable in rendered HTML and PDF asciidoc outputs
2025-03-24 14:30:21 +01:00
Adam Saghy b2af4a635c FINERACT-2221: Fix - Interest not counted towards totalUnpaidPayableNotDueInterest after partial repayment 2025-03-24 14:23:00 +01:00
mariiaKraievskaandAdam Saghy 2251ee0da6 FINERACT-2181: Fix loan transactions filtering by type 2025-03-21 19:40:05 +01:00
viktorpavlenkoandAdam Saghy 8462c67f3c FINERACT-2169: Refactor job api controller; 2025-03-20 14:44:27 +01:00
viktorpavlenkoandAdam Saghy 3aa011b32e FINERACT-2169: Refactor loan account api controller; 2025-03-20 14:43:16 +01:00
mark.vituskaandAdam Saghy 993393b519 FINERACT-2080: use enums instead of integers in Loan, LoanProduct and LoanTransaction 2025-03-20 14:34:51 +01:00
dependabot[bot]andArnold Gálovics 8d2db7f80f Bump actions/upload-artifact from 4.6.1 to 4.6.2
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1...ea165f8d65b6e75b540449e92b4886f43607fa02)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-20 10:59:59 +01:00
viktorpavlenkoandAdam Saghy c2c157cfd7 FINERACT-2169: Refactor currencies api resource; 2025-03-19 17:40:20 +01:00
Hossam HatemandAdam Saghy 1cb5d7c371 FINERACT-2181: Improve AssertJ assertions in DefaultScheduledDateGeneratorTest 2025-03-19 17:39:01 +01:00
adam.magyariandAdam Saghy 2cfc0b2503 FINERACT-2203: LoanTransactionsApi add getLoanTransactions 2025-03-19 17:38:19 +01:00
Arnold GalovicsandAdam Saghy a8bbba0b24 FINERACT-2181: Generating the right ChargeData class for ChargesApi 2025-03-19 17:36:25 +01:00
Oleksii NovikovandAdam Saghy 1d569d9ecf FINERACT-2181: Split Datatable service 2025-03-19 17:25:48 +01:00
oleksandr.masliukandAdam Saghy 123899eac1 FINERACT-2169: Apply dto in MixTaxonomy api 2025-03-19 13:23:34 +01:00
Adam Saghy 35339a6a83 FINERACT-2181: CommandProcessingResult serialization optimisation 2025-03-19 12:44:52 +01:00
viktorpavlenkoandAdam Saghy ef47989656 FINERACT-2169: Refactor image api resource; 2025-03-19 12:27:05 +01:00
viktorpavlenkoandAdam Saghy ac8f62db51 FINERACT-2169: Refactor document management api and update swagger doc; 2025-03-19 12:26:37 +01:00
viktorpavlenkoandAdam Saghy a0ffaa1c0b FINERACT-2214: Remove pretty print parameter from api and serialization; 2025-03-19 12:23:29 +01:00
Adam MonsenandAdam Saghy 34cdf4d01f FINERACT-2216: add github project metadata
1. Link to project homepage at https://fineract.apache.org
2. Add some tags/labels/topics

See also:

* https://github.com/apache/infrastructure-asfyaml#repo_meta
* https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics
2025-03-19 12:13:45 +01:00
viktorpavlenkoandAdam Saghy 84c101ab14 FINERACT-2169: Apply dto for external event config api; 2025-03-19 12:13:21 +01:00
a7med3del1973andAdam Saghy 9fe993342b FINERACT-2181: Refactor UpdateTrialBalanceDetailsConfig injection instead of Autowired 2025-03-19 12:12:37 +01:00
carlossortegaandAdam Saghy 9d53d1cb90 FINERACT-2202: Bulk import fix 2025-03-19 12:11:34 +01:00
oleksandr.masliukandAdam Saghy c9c01a50e2 FINERACT-2169: Apply DTO instead String json in FundsApiResource 2025-03-19 11:56:08 +01:00
mariiaKraievskaandAdam Saghy 04770133d0 FINERACT-2181: No subResourceExternalId in fee waiver response after charge-off 2025-03-19 11:53:57 +01:00
Adam SaghyandGitHub 2d03ebb179 FINERACT-2181: Upgrade Spring-boot version (#4477) 2025-03-18 23:57:56 -06:00
Víctor RomeroandGitHub 7dd2a8ba1e FINERACT-2181: Update non-major (#4471) 2025-03-18 15:12:18 -06:00
Victor RomeroandAdam Saghy eacfb92545 FINERACT-2181: Update non-major 2025-03-18 12:04:27 +01:00
mariiaKraievskaandAdam Saghy 22282ded00 FINERACT-2181: Rework "createMissingAccrualTransactionDuringChargeOffIfNeeded" to avoid flushing and triggering business event as part of Transaction processor 2025-03-18 12:03:27 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
166c1981fb Bump actions/setup-node from 4.2.0 to 4.3.0 (#4467)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a...cdca7365b2dadb8aad0a33bc7601856ffabcc48e)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-17 18:29:22 -06:00
Arnold GalovicsandArnold Gálovics 03aded1105 FINERACT-2181: Minor enhancements for External Asset Owner Loan Product API 2025-03-17 10:55:08 +01:00
viktorpavlenkoandAdam Saghy e1aa632ccc FINERACT-2169: Apply dto for payment type api and upgrade swagger doc; 2025-03-14 18:17:41 +00:00
viktorpavlenkoandAdam Saghy 707fe3d0e5 FINERACT-2169: Apply dto in api and clean up unnecessary swagger; 2025-03-14 18:17:13 +00:00
viktorpavlenkoandAdam Saghy 2f5ae610d1 FINERACT-2169: Upgrade charge api to dto; 2025-03-14 18:13:39 +00:00
oleksandr.masliukandAdam Saghy b698d7247b FINERACT-2169: Apply DTO instead String json in NotesApiResource 2025-03-14 18:12:37 +00:00
Harsh-Srivastav123andAdam Saghy c236ee04ef FINERACT-2080: Refactoring Loan entity
FINERACT-2080: Refactoring Loan entity
2025-03-14 18:11:52 +00:00
viktorpavlenkoandAdam Saghy 35aa6e1f4f FINERACT-2169: Business date api refactor; 2025-03-14 18:10:48 +00:00
viktorpavlenkoandAdam Saghy 99f313e02c FINERACT-2169: Account rules api refactor; 2025-03-14 18:10:19 +00:00
viktorpavlenkoandAdam Saghy 056b3edaad FINERACT-2169: Provision entries api refactor; 2025-03-14 18:09:40 +00:00
viktorpavlenkoandAdam Saghy d96e78a4f7 FINERACT-2169: Audits API recourse refactor and clean up; 2025-03-14 18:08:59 +00:00
Adam SaghyandArnold Gálovics 1d72d4cfb8 FINERACT-1095: Remove last pieces of sqlSearch 2025-03-14 14:10:31 +01:00
mark.vituskaandAdam Saghy e44a60f327 FINERACT-2181: initial rounding-mode configuration 2025-03-14 13:05:30 +00:00
budaidevandAdam Saghy a86baeeef4 FINERACT-2181: removing the read_uncommited isolation 2025-03-14 13:04:06 +00:00
Soma SörösandAdam Saghy 3cf85e2977 FINERACT-2197: Introduce a new type of ACTUAL during calculation of days in year 2025-03-14 13:01:06 +00:00
Wilfred KigenyiandAdam Saghy 2ac1445dba Confirm existance of elements before accessing them 2025-03-14 12:59:35 +00:00
Oleksii NovikovandAdam Saghy 898a18c873 FINERACT-2081: Fix cumulative principal calculation 2025-03-14 12:57:40 +00:00
Jose Alberto HernandezandAdam Saghy e1d820756f FINERACT-2180: Trigger (internal) business event when new datatable entry was added 2025-03-14 09:46:28 +00:00
Arnold GalovicsandArnold Gálovics 3198b1b99e FINERACT-2208: Penalties are not recalculated after backdated transactions 2025-03-12 16:52:30 +01:00
Arnold GalovicsandArnold Gálovics 91f0662998 FINERACT-2212: Savings interest was sometimes posted to the wrong GL accounts 2025-03-12 16:43:51 +01:00
marta-jankovicsandGitHub 2cbca40efe FINERACT-2209: Add missing permissions
Merge pull request #4450 from openMF/feature/FINERACT-2209
2025-03-12 16:26:34 +01:00
Adam Saghy 4ea0acdcf1 FINERACT-2211: Fix-Incorrect LoanSchedule after Full Refund with LAST_INSTALLMENT rule on 12m interest bearing Term loan 2025-03-12 15:00:14 +00:00
Soma SörösandAdam Saghy a28b9332b9 FINERACT-2201: [Bug] - Negative numbers in case of progressive interest bearing loans - Reamortization 2025-03-12 12:33:42 +00:00
Marta Jankovics d61a1addd7 FINERACT-2209: Add missing permissions 2025-03-12 11:26:43 +01:00
oleksandr.masliukandAdam Saghy 1fb5e3da43 FINERACT-2169: Apply DTO instead String json in RateApiResource 2025-03-11 18:04:41 +00:00
viktorpavlenkoandAdam Saghy cc0c09df60 FINERACT-2169: Batches api refactor; 2025-03-11 18:03:43 +00:00
a7med3del1973andAdam Saghy 8371e162ba FINERACT-2181: Refactor ResultColumnHeaderData using Lombok and modern Java new features 2025-03-10 14:25:02 +00:00
Adam Saghy 0b2e0c33a7 FINERACT-2204: [TEST] - Fix accrual activity reversal logic: prevent duplicate reverse-replay, copy external ID correctly
Based on the work of @tamasszabo-dpc and @peter-kovacs-dpc
2025-03-09 20:48:32 +00:00
Oleksii NovikovandAdam Saghy d32ddc91df FINERACT-2204: Fix accrual activity reversal logic: prevent duplicate reverse-replay, copy external ID correctly 2025-03-09 20:48:32 +00:00
Adam Saghy 03bbad21e8 FINERACT-2181: Remove unnecessary long to int conversion 2025-03-07 21:07:42 +00:00
mark.vituskaandAdam Saghy 24cee73dc0 FINERACT-2181: modified inject-comment sql validation pattern to not match single dashes 2025-03-07 20:44:41 +00:00
Arnold GalovicsandArnold Gálovics 33e9f7f3db FINERACT-2141: Delayed settlement implementation for fineract loan investor module 2025-03-07 17:46:24 +01:00
Arnold GalovicsandArnold Gálovics 23887bdf1c FINERACT-2181: Tenant ID is included in the log messages 2025-03-06 22:00:52 +01:00
Arnold GalovicsandArnold Gálovics f48a3d0c08 FINERACT-2181: Tag published docker images with hash as well when built from develop 2025-03-06 19:41:13 +01:00
Arnold GalovicsandArnold Gálovics 7b45f3e4d5 FINERACT-2181: Liquibase migrations are stuck when multi-threaded migrations are enabled and one tenant migration fails 2025-03-06 19:36:34 +01:00
viktorpavlenkoandAdam Saghy f8e5d6c9fc FINERACT-2169: Provision category definition api refactor; 2025-03-05 22:39:49 +00:00
Rustam ZeinalovandAdam Saghy bd66037910 FINERACT-2174: add tests for validation of interestRecognitionOnDisbursementDate setting application under certain conditions 2025-03-05 22:37:58 +00:00
viktorpavlenkoandAdam Saghy 429374e530 FINERACT-2169: Financial activity account api refactor; 2025-03-05 14:50:18 +00:00
mark.vituskaandAdam Saghy 1921606ffc FINERACT-2181: fixed ConfigurationReadPlatformServiceImpl to handle not found cases, added tests 2025-03-05 14:06:39 +00:00
viktorpavlenkoandAdam Saghy a41891e776 FINERACT-2169: Glclosure account api refactor; 2025-03-04 20:27:44 +00:00
viktorpavlenkoandAdam Saghy d98f5b0d0e FINERACT-2169: GL account api refactor; 2025-03-04 20:26:58 +00:00
Hossam HatemandAdam Saghy e0c9da4514 FINERACT-2181: Improve DateFormat class code quality 2025-03-04 20:25:50 +00:00
viktorpavlenkoandAdam Saghy d61bbeb71a FINERACT-2169: Taxes api refactor; 2025-03-04 20:24:39 +00:00
Harsh-Srivastav123andAdam Saghy d826913753 This PR refactors the LoanStatus.fromInt method to replace the traditional switch-case statement with a modern switch expression, improving code readability and efficiency. 2025-03-04 20:23:11 +00:00
Harsh-Srivastav123andAdam Saghy 86832c927b This pull request addresses [FINERACT-2081](https://issues.apache.org/jira/browse/FINERACT-2081).
The `toString()` method in several Enum classes has been simplified. The previous implementation:
2025-03-04 20:22:43 +00:00
a7med3del1973andAdam Saghy ce5c4d36b4 FINERACT-2181: Reduce duplication and use static access for constants 2025-03-04 20:22:16 +00:00
oleksandr.masliukandAdam Saghy a0a0c15efa FINERACT-2169: Apply DTO instead String json in AdHocApiResource 2025-03-04 20:19:49 +00:00
mariiaKraievskaandAdam Saghy 6b5641f474 FINERACT-2148: Accrual handling in case of Charged-off loan and backdated transactions / reversals 2025-03-04 19:11:06 +00:00
Adam Saghy 9208ea2fe1 FINERACT-2200: Add "reversed" flag to LoanTransactionDataV1.avsc 2025-03-04 14:16:55 +00:00
tamasszabo-dpcandAdam Saghy 262f1d9e10 FINERACT-2179: Added use-cases to cover next/last installment payment allocation 2025-03-03 16:39:57 +01:00
Soma SörösandAdam Saghy b546a43ce9 FINERACT-2179: Introduce Next/Last in future allocation rule for progressive loans 2025-03-03 16:39:57 +01:00
mariiaKraievskaandAdam Saghy f0cc697c67 FINERACT-2333: Accrual handling in case of Charged-off loan 2025-03-03 16:30:46 +01:00
Hossam HatemandAdam Saghy 843b27926e FINERACT-2081: Replace switch statements with if statements for better readability 2025-02-28 16:20:21 +01:00
Oleksii NovikovandAdam Saghy 4c66151b47 FINERACT-1981: Charge-off on progressive loan when accounting is none 2025-02-28 16:18:51 +01:00
Adam Saghy 710ba39d9f FINERACT-2081: Add index on calendar instance table 2025-02-28 15:18:19 +01:00
Hossam HatemandAdam Saghy 142f63ca17 FINERACT-2081: Add assertions to BulkBusinessEventTest 2025-02-28 12:13:28 +01:00
Rustam ZeinalovandAdam Saghy e1165e813a FINERACT-1971: added test scenarios to cover repayment schedule with large charge amounts 2025-02-27 16:37:29 +01:00
Arnold GalovicsandArnold Gálovics c1ec3d7fc7 FINERACT-2194: Batch jobs arre failing in a multitenant environment 2025-02-27 16:25:18 +01:00
tamasszabo-dpcandAdam Saghy 08dc3b123f FINERACT-2148 - Fixed expected results for accruals before charge-off date 2025-02-27 15:11:08 +01:00
Oleksii NovikovandAdam Saghy 02c66b180e FINERACT-2148: Accrual handling in case of backdated Charged-off transaction 2025-02-27 15:11:08 +01:00
a7med3del1973andAdam Saghy 53c2cabf01 FINERACT-2081: Added some tests for LogParameterEscapeUtil 2025-02-27 10:31:56 +01:00
Hossam HatemandAdam Saghy 17d1aa580d FINERACT-2081: Fix code quality issues in Cashier domain class 2025-02-27 10:30:47 +01:00
oleksandr.masliukandAdam Saghy d9bb820cc6 FINERACT-2169: Replace String json to DTO in FloatingRatesApiResource - fixed 2025-02-27 10:28:47 +01:00
Rustam ZeinalovandAdam Saghy 7642530393 FINERACT-2081: Fix Accrual activity - reverse and replayed couple of times after backdated repayment 2025-02-27 10:27:33 +01:00
Adam Saghy 991a2c1af4 FINERACT-2181: Update Test annotation import
(org.junit.Test -> org.junit.jupiter.api.Test)
2025-02-26 22:17:59 +01:00
Rustam ZeinalovandAdam Saghy 786789c446 FINERACT-2081: Added tests for validation of batch api call with steps to charge off a loan 2025-02-26 16:53:38 +01:00
Hossam HatemandAdam Saghy 8c5606ea8e FINERACT-2081: fix sonar issues in EntityTables enum 2025-02-26 15:04:28 +01:00
Oleksii NovikovandAdam Saghy 2cf99818e6 FINERACT-2181: Introduce customData field for Loan and LoanTransaction business events 2025-02-26 14:27:31 +01:00
edcableandAdam Saghy ccf213aefe Update README.md
Fixed typo in header for Governance
2025-02-26 13:07:05 +01:00
viktorpavlenkoandAdam Saghy 191a35a940 FINERACT-2169: SMS API refactoring and clean up; 2025-02-26 12:55:02 +01:00
tamasszabo-dpcandAdam Saghy dcde04d506 FINERACT-2176 - Added use case to check interest portion for Charge-off when backdated full repayment happens on same period 2025-02-26 12:52:03 +01:00
adam.magyariandAdam Saghy a5e3a80683 FINERACT-2176: fix Interest portion is missing from Charge-off transaction 2025-02-26 12:52:03 +01:00
Soma SörösandAdam Saghy 3dd62803e6 FINERACT-2081: Fix ProgressiveEMICalculator caused infinite loop on AdvancedPaymentScheduleTransactionProcessor 2025-02-26 12:25:21 +01:00
Soma SörösandAdam Saghy a72d3ff423 FINERACT-2187: Fix totalUnpaidPayableNotDueInterest not correct, when there is early payment on the loan 2025-02-25 21:56:59 +01:00
Rustam ZeinalovandAdam Saghy 37f60ad8dc FINERACT-2174: Added tests for validation of calculation of fraction period in case if interestRecognitionFromDisbursementDate is set to true 2025-02-25 11:16:42 +01:00
adam.magyariandAdam Saghy 9310f0e1ea FINERACT-2181: make 'BusinessEventNotifierServiceImpl#isExternalEventConfiguredForPosting` overridable 2025-02-25 11:14:53 +01:00
adam.magyariandAdam Saghy e542ca6870 FINERACT-2081: fix tests event check to use latest relevant event 2025-02-25 11:11:26 +01:00
a7med3del1973andAdam Saghy 6821b50ed6 FINERACT-2081: Rename constants to follow naming convention and conflict
solved
2025-02-25 11:10:59 +01:00
dependabot[bot]andArnold Gálovics 8be31bd06b Bump actions/upload-artifact from 4.6.0 to 4.6.1
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08...4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-24 16:58:58 +01:00
Peter KovacsandAdam Saghy 2456f7fa71 FINERACT-2081: Adding TestrailIds, removing Skip tags 2025-02-24 14:27:35 +01:00
Hossam HatemandAdam Saghy 44ee19ec30 FINERACT-2081: Replace String concatenation with Text block and remove ApiResponses wrapper 2025-02-24 14:25:18 +01:00
Hossam HatemandAdam Saghy eac31a925f FINERACT-2081: Remove redundant toString() calls 2025-02-24 14:24:57 +01:00
a7med3del1973andAdam Saghy e001a9c048 FINERACT-2081: Remove unnecessary toString and change replaceAll method calls 2025-02-24 14:20:05 +01:00
Hossam HatemandAdam Saghy 6837043182 FINERACT-2081: Fixed GLAccountReadPlatformServiceImpl code quality issues 2025-02-24 14:18:06 +01:00
Hossam HatemandAdam Saghy 89ba32b982 FINERACT-2081: fixed GLAccountsApiResource by replace string concatenation and remove 'ApiResponses' wrapper 2025-02-24 14:11:36 +01:00
a7med3del1973andAdam Saghy aa8e1154ff FINERACT-2081: fixed ProvisioningEntriesApiResource by replace string concatenation and remove 'ApiResponses' wrapper 2025-02-24 14:10:41 +01:00
a7med3del1973andAdam Saghy f6c50bf1c5 FINERACT-2081: Define a constant instead of duplicating 2025-02-24 14:09:54 +01:00
a7med3del1973andAdam Saghy 84b25e0f1b FINERACT-2081: throw an UnsupportedOperationException for uncomplete implementation. 2025-02-24 14:09:28 +01:00
a7med3del1973andAdam Saghy 9a9a8ede93 FINERACT-2081: Refactor constructor injection 2025-02-24 13:14:24 +01:00
viktorpavlenkoandAdam Saghy af922fe933 FINERACT-2169: Accrual account api refactor; 2025-02-24 11:19:37 +01:00
viktorpavlenkoandAdam Saghy 8c2b7bd827 FINERACT-2169: Replace to String json to dto and clean up; 2025-02-24 11:02:24 +01:00
Adam Saghy 259eb75d81 FINERACT-2081: Fix Fineract writer hung infinte loop after merchant issued refund 2025-02-21 17:21:50 +01:00
Adam Saghy 5137fd65af FINERACT-2081: Charge-off optimalization 2025-02-21 14:09:23 +01:00
Adam Saghy e02420f998 FINERACT-1981: Fix interest calculation logic (rounding issues with sub interest periods) 2025-02-20 14:24:24 +01:00
Janos MeszarosandAdam Saghy b4b9d3ea71 FINERACT-1981: Fix calculation new way when applies multi disbursement 2025-02-20 14:24:24 +01:00
viktorpavlenkoandArnold Gálovics 81ed0260b1 FINERACT-2169: Refactor api to concrete types and add validation; 2025-02-19 23:10:12 +01:00
CsengeSótiandArnold Gálovics 1b0cc27eec FINERACT-2183: Expose Tomcat thread metrics via actuator 2025-02-19 17:26:27 +01:00
mdmytrivandAdam Saghy fd1c5f0b43 FINERACT-2268: added test scenarios to cover repayment schedule with large charge amounts 2025-02-19 15:00:51 +01:00
Adam Saghy 054f146d54 FINERACT-2081: Fix failing test 2025-02-19 00:16:57 +01:00
Oleksii NovikovandAdam Saghy b1384b9cce FINERACT-2081: Refactoring for reprocess transactions and regenerate repayment schedule invocations 2025-02-18 20:10:25 +01:00
mariiaKraievskaandAdam Saghy 6bb68051ce FINERACT-2327: Forbid accrual and accrual activity creation for closed, overpaid and cancelled loans 2025-02-18 17:56:37 +01:00
Arnold GalovicsandArnold Gálovics 0f1be3c913 FINERACT-2178: Ability to retrieve a loan's balance at a certain point in time in the past 2025-02-17 21:48:05 +01:00
Rustam ZeinalovandAdam Saghy 986dceea1c FINERACT-2286: Added tests for validation of interestRecognitionFromDisbursementDate setting to be taking into account by periodic accrual job 2025-02-17 15:05:06 +01:00
mdmytrivandAdam Saghy 2fce8e5be9 FINERACT-2310: added test scenarios for backdated loan with zero interest rate 2025-02-17 11:19:22 +01:00
Soma SörösandAdam Saghy ff21304cac FINERACT-2174: Calculate fraction period - In case interestRecognitionFromDisbursementDate is true, we need to include from date and exclude due date 2025-02-17 09:48:14 +01:00
mdmytrivandAdam Saghy 7dc5dc279b FINERACT-2162: Added automations tests for chargeback with interest bearing and interest recalculation enabled 2025-02-14 15:16:59 +01:00
Janos MeszarosandAdam Saghy cdbf0df571 FINERACT-2162: Chargeback implementation for progressive interest recalculation enabled loan. 2025-02-14 15:16:59 +01:00
Jose Alberto HernandezandAdam Saghy 22f7972f60 FINERACT-1971 Repayment schedule period - Incorrect values after charge with huge amount 2025-02-14 10:29:48 +01:00
tamasszabo-dpcandAdam Saghy d9414a6e7e FINERACT-2162: Added test case to cover accrual creation behavior in case of charback interest 2025-02-13 16:51:50 +01:00
adam.magyariandAdam Saghy 78c7abf05c FINERACT-2162: Progressive loan chargeback accrual handling 2025-02-13 16:51:50 +01:00
Peter KovacsandAdam Saghy f1e9afd3e3 FINERACT-2081: Adding additional E2E tests to improve test coverage - pt2: feature files 2025-02-13 16:40:06 +01:00
mariiaKraievskaandAdam Saghy cb0ad23f37 FINERACT-2290: Handling accelerate maturity date charge-off in case LAST_INSTALLMENT strategy 2025-02-13 13:20:39 +01:00
adam.magyariandAdam Saghy e2157501c0 FINERACT-2081: Allow loan charge-off via batch api 2025-02-13 09:51:58 +01:00
Adam Saghy 2ce1ece4ae FINERACT-2081: Fix to use locally built docker image 2025-02-12 19:35:17 +01:00
Peter KovacsandAdam Saghy 48a0630b47 FINERACT-2081: Adding additional E2E tests to improve test coverage - pt1: stepdef files 2025-02-12 12:00:20 +01:00
Andrii KulminskyiandAdam Saghy 4a0c52ea8b FINERACT-2081: Accrual not created for closed or overpaid loans 2025-02-11 16:28:24 +01:00
a7med3del1973andAdam Saghy 352d282d7c FINERACT-2081: Remove unnecessary toString method calls and fix typo 2025-02-11 15:06:47 +01:00
Adam Saghy b2bfca2556 FINERACT-2081: Github Actions workflow cleanup 2025-02-10 17:02:03 +01:00
Adam Saghy f4897508ed FINERACT-2081: Github Actions workflow cleanup 2025-02-10 12:53:40 +01:00
Adam Saghy 1a49af1897 FINERACT-2081: Fix sonarqube analysis 2025-02-10 12:53:02 +01:00
David Lemayian ✨andAdam Saghy 25bca18a8f FINERACT-2081: Update fineract.yml docker image ref 2025-02-10 11:36:47 +01:00
adam.magyariandAdam Saghy 9fa64eccf8 FINERACT-2081: EmbeddableProgressiveLoanScheduleGenerator add totalOutstandingLoanBalance field 2025-02-07 15:06:46 +01:00
Adam Saghy 486ecb18f2 FINERACT-2081: Fix "group" for ACME Fineract Event External Event 2025-02-06 20:13:04 +01:00
Adam Saghy d2b4b3858d FINERACT-2081: Update "Transaction summary report": Fix incorrect fees portion in case of repayment reversal 2025-02-06 16:23:05 +01:00
Adam Saghy 79c5fc1649 FINERACT-2081: Interest recalculation only for interest bearing loans 2025-02-06 14:11:06 +01:00
adam.magyariandAdam Saghy 493012bf8c FINERACT-2174: Enhance Apply periodic accrual job to take into consideration the interestRecognitionOnDisbursementDate field 2025-02-06 13:44:02 +01:00
Jose Alberto HernandezandAdam Saghy 3a5888ee4b FINERACT-2174: Allow for recognition of Interest on first day end of day - Introducing the flag 2025-02-05 19:52:44 +01:00
Oleksii NovikovandAdam Saghy 7d0474cc17 FINERACT-2162: Support chargeback for progressive loans with disabled interest recalculation and payment allocation 2025-02-05 14:55:15 +01:00
tamasszabo-dpcandArnold Gálovics 224e84a9c2 FINERACT-2162: Added use cases for chargeback in case no chargeback rule and recalculation disabled 2025-02-04 20:24:21 +01:00
adam.magyariandAdam Saghy 157a492bcc FINERACT-1981: fix progressive accruals on late repayment 2025-02-04 11:47:52 +01:00
Adam Saghy 0b9687d2aa FINERACT-2081: Fix end of month and partial interest calculation for 360/30 2025-02-03 16:29:55 +01:00
mariiaKraievskaandAdam Saghy 69317d62c8 FINERACT-2205: Add tests when charge is added after the charge off date and forbid backdated charge-off transactions in the middle of the monetary activity. 2025-02-03 16:29:45 +01:00
tamasszabo-dpcandAdam Saghy bc0293bfa1 FINERACT-2081: Added loan accrual activity test to check the case when undo happens on duedate repayment 2025-01-31 15:01:09 +01:00
Adam Saghy 894b6cbf6f FINERACT-2081: Integration test clean up - Remove Google truth dependency and Extract Fineract client out from IntegrationTest class 2025-01-31 15:00:45 +01:00
Oleksii NovikovandAdam Saghy de462082de FINERACT-2152: Interest pause and existing interest periods 2025-01-31 15:00:36 +01:00
Mend RenovateandPetri Tuomola 00d3158074 Update plugin org.sonarqube to v6 2025-01-31 21:27:39 +08:00
dependabot[bot]andPetri Tuomola 4e40925950 Bump actions/setup-java from 4.6.0 to 4.7.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/7a6d8a8234af8eb26422e24e3006232cccaa061b...3a4f6e1af504cf6a31855fa899c6aa5355ba6c12)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-31 21:26:37 +08:00
dependabot[bot]andPetri Tuomola c01553a7ca Bump gradle/actions from 4.2.2 to 4.3.0
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.2 to 4.3.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/0bdd871935719febd78681f197cd39af5b6e16a6...94baf225fe0a508e581a564467443d0e2379123b)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-31 21:25:49 +08:00
Adam Saghy 26bc89ed59 FINERACT-2081: Update transaction summary report - Handling Accrual adjustment properly 2025-01-30 16:35:47 +01:00
Soma SörösandAdam Saghy 78cd5b106a FINERACT-2081: Fix Accrual Activity Reversal on Installment Due Date.
* Accrual Activity is not recalculated during reopen transaction recalculation
* integration tests cover reverse replay verification and extra use cases
2025-01-30 12:45:38 +01:00
Adam MonsenandGitHub 8b80f5a934 FINERACT-2170: improve asciidoc: clean URIs, more (#4271)
* disable broken fineract-client API asciidoc include
  * this has a side effect of removing ~1,200 pages from the PDF
* remove duplicated/outdated text, reduce sources of truth
  * link to current FAQ
  * introduction is all references now... it was way out of date and also needed formatting fixes
* unset `show-link-uri` to improve PDF rendering
* remove unnecessary/unused `.asciidoctorconfig` file
2025-01-29 12:41:34 -06:00
Soma SörösandArnold Gálovics 66f3de8702 FINERACT-2081: Fix Interest Payment Waiver reverse-replay transaction on transaction creation. 2025-01-29 17:34:40 +01:00
adam.magyariandAdam Saghy 6e8024a004 FINERACT-1981: fix progressive accruals on early repayment 2025-01-29 15:05:47 +01:00
Jose Alberto HernandezandAdam Saghy 15fcaed61f FINERACT-1971: Loan totalUnpaidPayableNotDueInterest field has wrong value after maturity 2025-01-29 10:16:30 +01:00
Andrii KulminskyiandAdam Saghy 093054b3db FINERACT-1981: Consolidated accrual activity created instead of individual ones 2025-01-29 10:13:12 +01:00
mariiaKraievskaandArnold Gálovics de1555f68f FINERACT-2205: Handling charges which belong to installments that got removed. 2025-01-27 21:27:33 +01:00
dependabot[bot]andArnold Gálovics df838cb113 Bump actions/setup-node from 4.1.0 to 4.2.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/39370e3970a6d050c480ffad4ff0ed4d3fdee5af...1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 21:26:57 +01:00
Oleksii NovikovandArnold Gálovics 69fed07e5c FINERACT-2152: Backdated and multiple interest pauses 2025-01-25 13:57:45 +01:00
Oleksii NovikovandAdam Saghy af37e07f31 FINERACT-2152: Interest pause during one period and between two periods 2025-01-24 09:58:02 +01:00
Adam Saghy a9d76ef3dd FINERACT-2165: Mark all the RestAssured solutions as deprecated in the integration tests module 2025-01-22 21:40:10 +01:00
Adam Saghy 6348140716 FINERACT-2081: Fix "resolve dependent variable" logic of Batch request resolver 2025-01-22 18:42:08 +01:00
Kristof JozsaandAdam Saghy 384a52bbaf FINERACT-2122: Add devRun gradle task, optimize build caching, fix deprecations 2025-01-22 17:03:24 +01:00
Clay JohnsonandAdam Saghy ddc8b1a0a7 [FINERACT-2168]: Publish build scans to develocity.apache.org 2025-01-22 10:17:43 +01:00
dependabot[bot]andArnold Gálovics 37174f7891 Bump actions/stale from 9.0.0 to 9.1.0
Bumps [actions/stale](https://github.com/actions/stale) from 9.0.0 to 9.1.0.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/28ca1036281a5e5922ead5184a1bbf96e5fc984e...5bef64f19d7facfb25b37b414482c7164d639639)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-21 20:03:22 +01:00
mariiaKraievskaandAdam Saghy 771b48b078 FINERACT-2235: Accelerate maturity to charge-off date when interest recalculation is disabled. 2025-01-21 17:21:10 +01:00
Andrii KulminskyiandAdam Saghy 9a8db7e82e FINERACT-2152: fix overlapping for update interest pause 2025-01-21 13:38:15 +01:00
adam.magyariandAdam Saghy 5729af5e76 FINERACT-1981: fix progressive installment reschedule 2025-01-21 13:35:48 +01:00
Jose Alberto HernandezandAdam Saghy 6ec0401820 FINERACT-2152: Fix Invalid Loan term variation type on Interest Pause case 2025-01-21 11:41:40 +01:00
mariiaKraievskaandAdam Saghy 75701daa09 FINERACT-2148: Accelerate maturity to charge-off date when a regular/backdated repayment or other monetary activity occurs before/after the charge-off date. 2025-01-21 10:44:47 +01:00
Janos MeszarosandAdam Saghy 8ab9c9ea9a FINERACT-1981: Fix interest calculation when applies multi disbursement 2025-01-20 13:39:09 +01:00
Soma SörösandAdam Saghy 6e6ac8ffd6 FINERACT-1971: Verify Lan Charge Adjustment Business event's content 2025-01-20 09:54:34 +01:00
Andrii KulminskyiandAdam Saghy 9258be777c FINERACT-1971: update and delete interest pause by external id 2025-01-20 09:53:02 +01:00
Andrii KulminskyiandAdam Saghy 001797b97a FINERACT-1971: external_id column of accrual activity transaction gets updated to null when an accrual activity is reversed 2025-01-20 09:33:11 +01:00
Kristof JozsaandGitHub 9325172aeb Merge pull request #4267 from janez89/bugfix/fix-date-bug-in-saving-account-test
Fix date bug in saving account test
2025-01-17 06:42:50 +01:00
Janos Meszaros fd765988a4 Fix date bug in saving account test 2025-01-16 21:41:46 +01:00
Janos MeszarosandArnold Gálovics d704852228 FINERACT-1981: Enhance EMI Adjustment 2025-01-15 13:30:39 +01:00
Marta JankovicsandAdam Saghy 6994bc687b PS-2245: Negative amount for Accrual Activity 2025-01-15 10:08:49 +00:00
Andrii KulminskyiandArnold Gálovics c22844d432 FINERACT-1971: Event sequence id int overflow 2025-01-14 18:56:27 +01:00
Soma SörösandAdam Saghy 2151b19e27 FINERACT-1971: Fix EMI Calculator: Last Unpaid Repayment Period Handling.
call calculateLastUnpaidRepaymentPeriodEMI recursively when emi would be less than already paid to adjust remaining emi correction for previous period.
Fixing AdvancedPaymentScheduleTransactionProcessor to handle division by installment count adjustment part in case of IN_ADVANCED payment
2025-01-14 13:24:02 +00:00
Arnold GalovicsandArnold Gálovics d2bdbf843a FINERACT-2081: Ability to assign settlement information to loan product when asset is externally owned 2025-01-14 13:55:46 +01:00
Adam Saghy 3481761e74 FINERACT-2081: Make MeterRegistry optional 2025-01-10 19:38:18 +01:00
Adam Saghy 62635a3473 FINERACT-2081: Make MeterRegistry optional 2025-01-10 16:39:09 +01:00
Soma SörösandAdam Saghy 100405a579 FINERACT-1971: Fix EMI Calculator: Last Unpaid Repayment Period Handling.
call calculateLastUnpaidRepaymentPeriodEMI recursively when emi would be less than zero to adjust remaining emi correction for previous period.
2025-01-10 13:53:07 +01:00
dependabot[bot]andArnold Gálovics cee5b43b71 Bump actions/upload-artifact from 4.5.0 to 4.6.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/6f51ac03b9356f520e9adb1b1b7802705f340c2b...65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-10 09:33:23 +01:00
Arnold GalovicsandArnold Gálovics da5dad27f3 FINERACT-2166: Expose tenant connection pool metrics 2025-01-10 09:31:04 +01:00
Adam Saghy 81883a18aa FINERACT-2081: Add missing index on GL annual summary table 2025-01-09 19:35:55 +01:00
Adam Saghy 9fc4e2f948 FINERACT-2152: Loan term variations audit fields 2025-01-09 16:58:17 +01:00
Adam Saghy 095b1a8ce0 FINERACT-2081: Transaction Summary Report with aggregated accrual & accrual adjustment 2025-01-09 16:57:54 +01:00
Andrii KulminskyiandAdam Saghy 8663c054a0 FINERACT-2152: API update and delete interest pause 2025-01-08 16:18:44 +01:00
Adam Saghy d2afd1aac7 FINERACT-1806: Fix mapping to handle multiple loan product configuration 2025-01-08 15:12:54 +01:00
Soma SörösandAdam Saghy afc09b5333 FINERACT-1971: Fix Accrual Activity reversal on Progressive interest bearing loans during loan reopening 2025-01-08 13:04:46 +01:00
Jose Alberto HernandezandAdam Saghy 4f1184f20b FINERACT-2148: Stop recalculating interest if the loan is charged-off 2025-01-08 12:07:23 +01:00
Adam Saghy e1d043bb2d FINERACT-1806: Advanced accounting - Charge-off reason 2025-01-07 19:40:45 +01:00
Oleksii NovikovandAdam Saghy b7e6f356da FINERACT-2148: Resolve rounding issue in interest period calculation 2025-01-07 19:40:32 +01:00
leksinomiandAdam Saghy 2fbdbe9df7 FINERACT-2148: Add 'Accelerate maturity' charge-off behaviour option 2025-01-07 18:21:24 +01:00
mariiaKraievskaandAdam Saghy dfc1d3ebad FINERACT-2148: Fix and add tests when interestRecalculation = false and zero interest charge-off behaviour 2025-01-06 09:42:19 +01:00
mariiaKraievskaandAdam Saghy faaf44d91a FINERACT-2148: Process monetary transaction after zero interest charge-off. 2025-01-01 15:52:57 +01:00
Adam Saghy 23850a1b73 FINERACT-2081: Fix Connection is not available, request timed out after 30000ms issue of "read-only" check 2024-12-22 18:06:28 +01:00
Andrii KulminskyiandAdam Saghy c2ec015e64 FINERACT-2152: API Create and retrieve interest pause 2024-12-19 22:24:02 +01:00
Adam Saghy e94e6c421a FINERACT-1806: Unify Charge-off reason mapping fields 2024-12-19 17:26:24 +01:00
Adam Saghy 8123276981 FINERACT-1806: Rework Product to GL account mapping and fix charge-off reason typos 2024-12-18 15:13:43 +01:00
adam.magyariandAdam Saghy 696be57c7c FINERACT-1806: fix ProductToGLAccountMapping 2024-12-18 15:13:43 +01:00
dependabot[bot]andArnold Gálovics d56e422a33 Bump actions/setup-java from 4.5.0 to 4.6.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/8df1039502a15bceb9433410b1a100fbe190c53b...7a6d8a8234af8eb26422e24e3006232cccaa061b)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-18 14:04:51 +01:00
dependabot[bot]andArnold Gálovics 61b65ee1d6 Bump actions/upload-artifact from 4.4.3 to 4.5.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882...6f51ac03b9356f520e9adb1b1b7802705f340c2b)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-18 14:04:46 +01:00
dependabot[bot]andArnold Gálovics a98fb07659 Bump gradle/actions from 4.2.1 to 4.2.2
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/cc4fc85e6b35bafd578d5ffbc76a5518407e1af0...0bdd871935719febd78681f197cd39af5b6e16a6)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-18 14:04:38 +01:00
Adam Saghy d03973165d FINERACT-2081: Support read-only transactions via Batch API 2024-12-17 14:49:18 +01:00
Jose Alberto HernandezandAdam Saghy 8716751c88 FINERACT-1971: Fix boolean values used in Loan product charge-off reasons accounting mappings 2024-12-17 10:51:30 +01:00
leksinomiandAdam Saghy 3735aea598 FINERACT-2081: Refactor LoanSummaryWrapper 2024-12-16 18:20:14 +01:00
Adam Saghy 4f470606ff FINERACT-2081: Update Trial Balance Summary Report with Year end retaining 2024-12-16 16:14:10 +01:00
Oleksii NovikovandAdam Saghy 51433ffb66 FINERACT-2148: Update instalments interest with zero after charge off with interest recalculation enabled 2024-12-16 14:31:54 +01:00
Marta JankovicsandArnold Gálovics 136bb0009a FINERACT-2060: Progressive loan - final accrual calculation on closed loan 2024-12-13 16:48:07 +01:00
Michael VorburgerandArnold Gálovics c4cdd6f51a Fix broken "Badges" on README 2024-12-13 14:58:48 +01:00
Michael VorburgerandArnold Gálovics 3800ad5cbf Revert "Update plugin org.sonarqube to v6"
This reverts commit a9a7e77afc.
2024-12-13 10:00:53 +01:00
Adam Saghy 5667bc89be FINERACT-2081: Consider read-only all the transactions when the connection is read-only 2024-12-12 17:43:31 +01:00
Adam Saghy d57906d4fa FINERACT-2081: Refactor - Simplify LoanSchedulePeriodData.java class 2024-12-11 19:10:10 +01:00
Arnold GalovicsandArnold Gálovics 263866c03a FINERACT-2081: Avoid creating unnecessary connection pools 2024-12-11 17:26:07 +01:00
Janos MeszarosandAdam Saghy bb8480a03d FINERACT-1981: Embeddable Progressive Loan Schedule generator 2024-12-10 15:04:16 +01:00
adam.magyariandAdam Saghy d0006a2169 FINERACT-1971: update installment accrued portions on charge accrual 2024-12-10 15:04:01 +01:00
adam.magyariandArnold Gálovics bc2ef08e0c FINERACT-2148: Add test for charge-off accruals 2024-12-07 13:25:19 +01:00
adam.magyariandArnold Gálovics 56350673ad FINERACT-1981: fix post maturity charge removed by cob on progressive loan 2024-12-07 13:25:07 +01:00
Jose Alberto HernandezandArnold Gálovics 84f01e0687 FINERACT-2081: Remove skip tag E2E test case C2501 2024-12-07 13:23:49 +01:00
Adam Saghy 3490219577 FINERACT-2148: Backdated Charge-off with interest recalculation 2024-12-06 16:49:55 +01:00
Oleksii NovikovandAdam Saghy a2c01d25e5 FINERACT-2080: Move loan validation from entity to validator classes 2024-12-05 13:58:47 +01:00
Andrii KulminskyiandAdam Saghy 56ee2eaaff FINERACT-1806: Advanced Charge-off Expense Accounting - "Advanced Accounting Rule" takes priority 2024-12-04 17:43:51 +01:00
adam.magyariandArnold Gálovics 5c08afec5f FINERACT-1926: Additional indices to investor tables 2024-12-03 16:02:58 +01:00
Mend RenovateandArnold Gálovics af34f31463 Update postgres Docker tag to v17 2024-12-03 07:26:26 +01:00
Mend RenovateandArnold Gálovics a9a7e77afc Update plugin org.sonarqube to v6 2024-12-03 07:26:20 +01:00
Marta JankovicsandAdam Saghy 7bd1f510a3 FINERACT-1981: COB/Inline COB fails while creating accruals for a overpaid account 2024-12-02 15:34:39 +01:00
Adam Saghy f63df474f6 FINERACT-2154: Store "Pre-close interest calculation strategy" on loan level 2024-11-29 21:39:08 +01:00
Jose Alberto HernandezandAdam Saghy 8d12bc04e9 FINERACT-2081: Retrieving Loan Product details with invalid external-id results in 500 error 2024-11-29 19:36:20 +01:00
Marta JankovicsandAdam Saghy d57e72ec2f FINERACT-2060: Accrual reverse replay logic and Handling 2024-11-29 19:17:23 +01:00
Jose Alberto HernandezandAdam Saghy 8d25b0c765 FINERACT-1981: Reverse Interest Refund transaction not allowed directly with 403 error 2024-11-29 12:28:37 +01:00
adam.magyariandAdam Saghy ff5e993b16 FINERACT-2148: return loan charge off behaviour with loan details 2024-11-29 12:24:23 +01:00
Janos MeszarosandAdam Saghy 4cb849fa17 FINERACT-1981: Introduce Interest should not be calculated on past due principal amount 2024-11-29 11:59:18 +01:00
Kristof JozsaandGitHub 3858611190 Merge pull request #4194 from adamsaghy/update_logback_of_e2e_runner
FINERACT-2081: Update logger level for E2E tests
2024-11-28 17:07:40 +01:00
Adam Saghy 0a8ddd24e7 FINERACT-2081: Update logger level for E2E tests 2024-11-28 14:40:49 +01:00
Soma SörösandAdam Saghy f5d9a3a025 FINERACT-1981: Update Payable Interest Calculation For LoanSummaryData 2024-11-28 13:14:45 +01:00
leksinomiandAdam Saghy 32cc83fdbf FINERACT-2153: Enable immediate charge accrual creation post maturity 2024-11-27 17:49:42 +01:00
Jose Alberto HernandezandAdam Saghy 1ef9c0f4bb FINERACT-1981: Reverse Interest Refund transaction not allowed directly 2024-11-27 17:43:08 +01:00
Adam MonsenandAdam Saghy 4956bdd574 FINERACT-2149: disable liquibase phone home
At some point, Liquibase added an analytics/telemetry "phone home" to gather user data.

I get how companies and FOSS projects greatly benefit from knowing more about their users, but I think this should only be done with proper consent. I would not assume/expect any libraries to send analytics home. The default behavior of trackers like these should always be to *not* phone home without a user first consenting / opting in.

---

I'm not sure this is the right change, but it works (feedback welcome). My naïve test/check to know this works is visually inspecting the output of the log message I added in TenantDatabaseUpgradeService to see if `liquibase.analytics.enabled` is set, and to what. I'm happy to automate this, but it seemed unnecessary (again, feedback welcome).

We could also disable the phone home with an environment variable ( LIQUIBASE_ANALYTICS_ENABLED=false , see https://docs.liquibase.com/analytics/home.html ). I'm not sure where that should be set if we did. Likely several places (for build/dev/test/ci/qa/etc.).

I originally noticed the phone home because I block requests from my computer to 3rd party tracking/analytics services, and I was getting this error in my console:

    java.net.ConnectException: Connection refused at liquibase.analytics.LiquibaseAnalyticsListener

At first I misunderstood this as a database connection error, but it was really just liquibase throwing an error because it couldn't connect to its own analytics server.

If we were to create a configurable property, I think the ideal place to set the value would be in fineract-provider/src/main/resources/application.properties, (default to false, allow override) but our version of spring boot doesn't support it yet ( see https://github.com/spring-projects/spring-boot/issues/43067 ) so we'd have to add the usual scaffolding required for a Fineract setting. I'm not sure we want/need that, because I can't imagine anyone *wanting* to set the flag to `true`, and my naïve hardcoded global change is less code to maintain than providing a configurable setting. If there's demand to make it configurable later, we could of course do that (especially once it becomes easier to do so). I feel like how we let Liquibase behave is up to us, though. An under the hood / upstream decision we get to make.

See also:

* https://lists.apache.org/thread/5s7zjsxdcqk1qvjnc1wbhqqv66shpl54
* Analytics is enabled by default in OSS version · Issue #6503 · liquibase/liquibase · GitHub https://github.com/liquibase/liquibase/issues/6503
* Add a method for Spring Boot to configure the new analytics feature · Issue #6501 · liquibase/liquibase · GitHub https://github.com/liquibase/liquibase/issues/6501
2024-11-27 17:13:23 +01:00
Andrii KulminskyiandAdam Saghy bfae135b54 FINERACT-1806: fetch loan product details must return the chargeoffreason mapping 2024-11-27 15:37:38 +01:00
adam.magyariandAdam Saghy 479e71cdad FINERACT-2148: loan charge off behaviour configuration 2024-11-27 11:31:05 +01:00
Jose Alberto HernandezandAdam Saghy 240a6dffc8 FINERACT-2081: Enable inline COB execution for locked loans 2024-11-27 10:09:49 +01:00
Adam Saghy 2e89c40e6e FINERACT-2081: Refactor GL account balance calculation 2024-11-26 18:06:09 +01:00
Adam Saghy ccfb8c3c9c FINERACT-2081: Add "Interest Refund" transaction into r_enum_value table 2024-11-26 13:58:05 +01:00
Mend RenovateandArnold Gálovics 97ac413874 Update plugin net.ltgt.errorprone to v4 2024-11-26 12:02:01 +01:00
adam.magyariandArnold Gálovics dcb18d8f5e FINERACT-2081: async liquibase 2024-11-25 16:22:55 +01:00
adam.magyariandAdam Saghy 4e3bcbf970 FINERACT-2150: disbursement not allowed on charged-off loan 2024-11-25 15:13:23 +01:00
Adam Saghy 0c7ef324b9 FINERACT-2070: COB business step order 2024-11-24 17:30:32 +01:00
Janos MeszarosandAdam Saghy 34b165aa0e FINERACT-1981: Fix interest refund transaction with zero amount 2024-11-22 17:11:15 +01:00
Peter KovacsandAdam Saghy e95b3ff25c FINERACT-2070: E2E test synchronization 2024-11-22 2024-11-22 14:59:41 +01:00
Adam Saghy ebfc11c041 FINERACT-2081: Remove duplicate LoanTestLifecycleExtension annotations 2024-11-21 22:34:40 +01:00
Andrii KulminskyiandAdam Saghy 4ccb33a95e FINERACT-2113: Advanced Charge-off Expense Accounting - Add new "Advanced Accounting Rule" 2024-11-21 17:18:06 +01:00
Janos MeszarosandAdam Saghy 75fa740ff6 FINERACT-2114: Fix EMI adjustment when Interest Rate change has with Early Repayment Adjust Last case 2024-11-21 12:46:42 +01:00
adam.magyariandArnold Gálovics 425085f4eb FINERACT-2081: External event sending make mark as sent async 2024-11-20 20:17:01 +01:00
Adam MonsenandArnold Gálovics bb18aadb74 FINERACT-2147: fix extra swagger UI endpoint paths
I noticed an extra `v1/` while visiting https://localhost:8443/fineract-provider/swagger-ui/index.html

All fineract-provider (unit) tests passed.

I also manually tested out hitting a few /api/v1/ endpoints via the Swagger UI web page at the URL above and those worked as well.
2024-11-19 16:54:58 +01:00
dependabot[bot]andArnold Gálovics 89e9ea8a54 Bump gradle/actions from 4.2.0 to 4.2.1
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/473878a77f1b98e2b5ac4af93489d1656a80a5ed...cc4fc85e6b35bafd578d5ffbc76a5518407e1af0)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-19 10:21:36 +01:00
Mend RenovateandArnold Gálovics a0d55d2f25 Update mysql Docker tag to v9 2024-11-18 18:25:40 +01:00
Mend RenovateandArnold Gálovics e305cb9b0a Update dependency ubuntu to v24 2024-11-18 18:25:31 +01:00
Mend RenovateandArnold Gálovics b39bc527ed Update non-major 2024-11-18 18:25:23 +01:00
Soma SörösandAdam Saghy c9dc87f62a FINERACT-1981: Fix Charge Creation After Maturity Date on Interest Bearing Progressive Loan 2024-11-18 15:04:35 +01:00
adam.magyariandAdam Saghy 6e86ac853a FINERACT-2081: External event sending enhance findBy query 2024-11-18 15:01:24 +01:00
Oleksii NovikovandAdam Saghy 628f27bf32 FINERACT-1806: Add charge-off reasons to loan product response 2024-11-14 15:10:42 +01:00
Soma SörösandAdam Saghy 6286d1f821 FINERACT-2107: Interest Refund - Transaction Amount Recalculation 2024-11-14 09:59:08 +01:00
Peter KovacsandAdam Saghy 4e8fc2c0a8 FINERACT-2070: E2E testcase migration - feature files - pt6 2024-11-13 16:20:18 +01:00
leksinomiandAdam Saghy 7408f10cb6 FINERACT-2107: Adjust event trigger on refund reversion 2024-11-13 11:45:38 +01:00
dependabot[bot]andArnold Gálovics a5acbd803a Bump gradle/actions from 4.1.0 to 4.2.0
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/d156388eb19639ec20ade50009f3d199ce1e2808...473878a77f1b98e2b5ac4af93489d1656a80a5ed)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-13 09:37:42 +01:00
Arnold GalovicsandArnold Gálovics c2b36f834e Checkstyle fixes 2024-11-12 12:16:16 +01:00
Mend RenovateandArnold Gálovics b7467c1a62 Update non-major 2024-11-12 12:16:16 +01:00
Arnold GalovicsandArnold Gálovics 15ea6f5b93 Downgraded org.openapi.generator to 7.8.0 2024-11-11 16:33:45 +01:00
Mend RenovateandArnold Gálovics e367cbae29 Update all non-major dependencies 2024-11-11 16:33:45 +01:00
Adam Saghy 39da68820b FINERACT-2081: Fix inconsistent transaction processing order 2024-11-11 12:32:21 +01:00
leksinomiandArnold Gálovics 832b84f574 FINERACT-2081: Update password policy 2024-11-11 10:29:36 +01:00
Peter KovacsandAdam Saghy 975a176610 FINERACT-2070: E2E testcase migration - feature files - pt5 2024-11-10 22:06:14 +01:00
Peter KovacsandAdam Saghy ab1a634d17 FINERACT-2070: E2E testcase migration - feature files - pt3 2024-11-10 14:38:04 +01:00
Peter KovacsandAdam Saghy 6770352da8 FINERACT-2070: E2E testcase migration - feature files - pt1 2024-11-10 14:35:18 +01:00
Adam Saghy 64976f1eb4 FINERACT-2070: Update test configurations 2024-11-09 20:04:13 +01:00
Janos MeszarosandAdam Saghy 17a4392c26 FINERACT-2114: Align recalculated EMI after Interest Rate change 2024-11-08 23:13:27 +01:00
Oleksii NovikovandAdam Saghy 75ad6082d0 FINERACT-2107: update related relationship for interest refund transaction 2024-11-08 23:13:00 +01:00
Peter KovacsandAdam Saghy 87ad93216e FINERACT-2070: E2E testcase migration - feature files - pt4 2024-11-08 23:01:55 +01:00
Peter KovacsandAdam Saghy 637539a63b FINERACT-2070: E2E testcase migration - feature files - pt2 2024-11-08 11:40:58 +01:00
leksinomiandAdam Saghy 4013172ab0 FINERACT-2107: Allow interest rate change on fully paid installment for Progressive loans 2024-11-07 21:51:57 +01:00
Peter KovacsandAdam Saghy 6feb10a87e FINERACT-2070: e2e test framework update 2024-11-07 21:49:46 +01:00
Adam Saghy 492ec44cef FINERACT-1981: Prepayment calculation 2024-11-06 15:04:17 +01:00
Jose Alberto HernandezandAdam Saghy 474623b6e3 FINERACT-2081: Fix WholeTerm loan frequency saved on account level 2024-11-06 09:48:09 +01:00
Janos MeszarosandAdam Saghy 51b6dfa08e FINERACT-1981: Enhance EMI Calculator Daily Interest calculation 2024-11-06 09:46:36 +01:00
Wilfred KigenyiandAdam Saghy 0bc14ab7f3 FINERACT-2144: Fix DepositApiConstant names 2024-11-04 17:35:06 +01:00
Soma SörösandAdam Saghy 174f4178b7 FINERACT-2107: Interest Refund - Multiple Partial Repayment 2024-11-04 13:06:34 +01:00
Andrii KulminskyiandAdam Saghy 7288037614 FINERACT-1981: When paying down payment, that should not be reflected on the InterestModel 2024-11-04 12:38:09 +01:00
Wilfred KigenyiandArnold Gálovics d202145a88 rectify query for cashier-teller transactions 2024-10-31 08:59:22 +01:00
Kristof JozsaandGitHub 542cad793d Merge pull request #4136 from kjozsa/FINERACT-1981/last_installment_strategy
FINERACT-1981: revise last installment payment strategy
2024-10-30 17:46:23 +01:00
Adam SaghyandKristof Jozsa 3b2ba174e1 FINERACT-1981: revise last installment payment strategy 2024-10-30 16:26:23 +01:00
Marta JankovicsandAdam Saghy 9812f14ad7 FINERACT-2081: Code quality, eliminate duplicated methods, use utility 2024-10-30 15:54:48 +01:00
Soma SörösandAdam Saghy 001d586e30 FINERACT-2107: Interest Refund - Partial Repayment 2024-10-30 13:16:41 +01:00
adam.magyariandAdam Saghy 745dc80886 FINERACT-2107: Merchant issued refund linked to related interest refund
- on reversal reverse interest refund aswell
2024-10-30 13:16:29 +01:00
Adam MonsenandAdam Saghy b053c90009 bump MariaDB version
EOL for 11.2 is coming up in a few weeks, see: https://mariadb.org/about/#maintenance-policy

I upgraded my local MariaDB dev database to 11.4 and performed a few cursory GETs and PUTs via the Fineract API--just what I've learned to do so far. Nothing broke. I didn't run all the tests though.

I didn't see any obvious red flags in https://mariadb.com/kb/en/changes-improvements-in-mariadb-11-4/
2024-10-29 21:53:21 +01:00
Andrii KulminskyiandAdam Saghy f92290b355 FINERACT-1981: Pay-off does not consider the overdue installments 2024-10-29 21:53:07 +01:00
Janos MeszarosandAdam Saghy 94360c9f8a FINERACT-2114: Interest rate modification - Advanced payment allocation processor support 2024-10-29 12:24:23 +01:00
Adam SaghyandArnold Gálovics 3e64cc1536 FINERACT-2107: Fix transaction order 2024-10-29 08:23:11 +01:00
Jose Alberto HernandezandAdam Saghy 15e013b90e FINERACT-2081: Fix reversal of loan accruals when loan repayment got reversed 2024-10-28 19:17:36 +01:00
leksinomiandAdam Saghy f76cef768e FINERACT-2081: Handle unauthenticated requests in LoanCOBApiFilter 2024-10-28 12:04:36 +01:00
dependabot[bot]andArnold Gálovics 7b05432695 Bump actions/setup-java from 4.4.0 to 4.5.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/b36c23c0d998641eff861008f374ee103c25ac73...8df1039502a15bceb9433410b1a100fbe190c53b)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-25 14:44:26 +02:00
dependabot[bot]andArnold Gálovics 9c9aec985b Bump actions/setup-node from 4.0.4 to 4.1.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.4 to 4.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/0a44ba7841725637a19e28fa30b79a866c81b0a6...39370e3970a6d050c480ffad4ff0ed4d3fdee5af)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-25 14:44:22 +02:00
Adam Saghy e60ce9a401 FINERACT-1981: Generate loan schedule as dto 2024-10-25 14:35:29 +02:00
adam.magyariandAdam Saghy b23fe71864 FINERACT-1981: loan pay off test on disbursement date 2024-10-25 13:09:33 +02:00
Oleksii NovikovandAdam Saghy 8a2560fe2e FINERACT-1981: do not calculate interest for charge adjustment when the activities occur on the same day 2024-10-25 09:55:59 +02:00
Adam Saghy 4198b0bcf9 FINERACT-2107: Extract Merchant issued refund and Payout refund handling 2024-10-24 15:51:41 +02:00
Janos MeszarosandAdam Saghy 393f4398ac FINERACT-1981: Fix 2nd disburse on repayment due date results wrong EMI 2024-10-24 13:44:31 +02:00
adam.magyariandAdam Saghy 2469f22280 FINERACT-1981: Enable backdated Interest Payment Waiver on progressive loans 2024-10-24 11:34:22 +02:00
adam.magyariandAdam Saghy 256afa1e51 FINERACT-1876: Add missing fields into the LoanAccountDataV1 avro schema 2024-10-24 11:34:11 +02:00
dependabot[bot]andArnold Gálovics 0bca9897a6 Bump actions/checkout from 4.2.1 to 4.2.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871...11bd71901bbe5b1630ceea73d27597364c9af683)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-24 11:13:41 +02:00
Marta JankovicsandAdam Saghy 6a381df784 FINERACT-2081: fix integrationtest init 2024-10-22 18:46:21 +02:00
Andrii KulminskyiandAdam Saghy a9ecdf97a4 FINERACT-1981: Remove hardcoded MoneyHelper dependency during progressive loan 2024-10-22 18:45:28 +02:00
Marta JankovicsandAdam Saghy 806ee69798 FINERACT-2081: fix incorrect exception type 2024-10-21 18:42:06 +02:00
Mend RenovateandArnold Gálovics 4e46e19dfe Update dependency org.jetbrains.kotlin:kotlin-bom to v2 2024-10-21 17:41:41 +02:00
Soma SörösandAdam Saghy 5722891477 FINERACT-2107: Interest Refund - Allocation, Business Event & Full Repayment 2024-10-21 17:36:20 +02:00
Janos MeszarosandAdam Saghy 84d606552d FINERACT-1981: Fix Better EMI check in EMI Calculator 2024-10-21 17:34:44 +02:00
Arnold GalovicsandArnold Gálovics 482983f23d FINERACT-2142: Journal entries are created for accrual disbursement fees 2024-10-18 20:13:10 +02:00
Kristof JozsaandGitHub 66f5633ed8 Merge pull request #4106 from kjozsa/feature/FINERACT-1981-enable-backdated-repayment-for-progressive-loans
FINERACT-1981: Progressive loan enable backdated transactions
2024-10-18 10:42:04 +02:00
adam.magyariandKristof Jozsa e848160dd0 FINERACT-1981: Progressive loan enable backdated transactions 2024-10-18 09:13:26 +02:00
Janos MeszarosandAdam Saghy f94e293d1e FINERACT-1981: EMI Calculator performance optimization 2024-10-17 17:33:37 +02:00
Adam Saghy c8e6adba1e FINERACT-2081: Include Interest waiver and Interest refund transaction in Transaction summary with asset owner report 2024-10-16 14:55:35 +02:00
Janos MeszarosandAdam Saghy bb072709b5 FINERACT-1981: EMI calculator rework 2024-10-16 14:35:58 +02:00
Andrii KulminskyiandAdam Saghy d4190ede90 FINERACT-1981: separate library to generate loan schedule 2024-10-16 11:37:09 +02:00
Arnold GalovicsandArnold Gálovics b6ccb06159 Dep updates 2024-10-14 16:22:47 +02:00
Mend RenovateandArnold Gálovics 0a838e68aa Update all non-major dependencies 2024-10-14 16:22:47 +02:00
Mend RenovateandArnold Gálovics 5199ccd75a Update non-major 2024-10-14 11:44:08 +02:00
Henrique PaesandArnold Gálovics a5f1dc1227 FINERACT-1221 Fix Client SDK Broken Methods
FINERACT-1221 Fix Client SDK Broken Methods
2024-10-12 08:05:57 +02:00
Oleksii NovikovandArnold Gálovics 25866edfc9 FINERACT-2081: Add all loan term validations to loan details 2024-10-12 08:05:30 +02:00
dependabot[bot]andArnold Gálovics fe16b61c72 Bump actions/upload-artifact from 4.4.2 to 4.4.3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/84480863f228bb9747b473957fcc9e309aa96097...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-10 08:46:47 +02:00
Mend RenovateandArnold Gálovics 2ab0635fd1 Update non-major 2024-10-09 13:29:04 +02:00
dependabot[bot]andArnold Gálovics 45502e8c7e Bump actions/upload-artifact from 4.4.1 to 4.4.2
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/604373da6381bf24206979c74d06a550515601b9...84480863f228bb9747b473957fcc9e309aa96097)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 10:28:09 +02:00
Arnold GalovicsandArnold Gálovics e2b86788d2 Rolled back openAPI generator version 2024-10-08 20:59:53 +02:00
Mend RenovateandArnold Gálovics 900477e2aa Update all non-major dependencies 2024-10-08 20:59:53 +02:00
adam.magyariandArnold Gálovics 4462127295 FINERACT-1981: Fix progressive loan undo previous repayment 2024-10-08 18:21:31 +02:00
dependabot[bot]andArnold Gálovics c71a39bb18 Bump actions/upload-artifact from 4.4.0 to 4.4.1
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...604373da6381bf24206979c74d06a550515601b9)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-08 11:24:40 +02:00
dependabot[bot]andArnold Gálovics f72788318c Bump actions/checkout from 4.2.0 to 4.2.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-08 11:24:35 +02:00
Mend RenovateandArnold Gálovics 7601bed493 Update dependency org.eclipse.jgit:org.eclipse.jgit.ssh.apache to v7 2024-10-07 16:29:14 +02:00
Adam Saghy a7965a7f9d FINERACT-1981: Fix loan transaction relationship 2024-10-05 09:06:13 +02:00
dependabot[bot]andArnold Gálovics 8aaad45be9 Bump actions/setup-java from 4.3.0 to 4.4.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/2dfa2011c5b2a0f1489bf9e433881c92c1631f88...b36c23c0d998641eff861008f374ee103c25ac73)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-04 18:02:02 +02:00
dependabot[bot]andArnold Gálovics a85f90b7f2 Bump actions/checkout from 4.1.7 to 4.2.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-04 18:01:57 +02:00
Arnold GalovicsandArnold Gálovics 33f666ccc3 FINERACT-2134: Missing charge paid by references and journal entries for disbursement occuring accruals 2024-10-02 15:30:07 +02:00
Adam Saghy 39d95f393b FINERACT-2081: Fetch configurations by name 2024-10-02 01:20:04 +02:00
adam.magyariandAdam Saghy 03d320e513 FINERACT-2081: Fix progressive loan schedule for non-yearly interest rates 2024-10-01 13:54:02 +02:00
Kristof JozsaandAdam Saghy 04ec56382a FINERACT-1981: interest recalculation fix for overdue cases 2024-09-30 19:41:42 +02:00
Kristof JozsaandGitHub a29aaf0d3c Merge pull request #4077 from fiter-distribution/FINERACT-2081/enhance_technical_documentation
Fineract 2081: Enhance technical documentation
2024-09-30 16:43:09 +02:00
Soma SörösandAdam Saghy 4a950ac476 FINERACT-1981: Interest recalculation - Late Repayment Test Cases 2024-09-30 15:05:48 +02:00
Napho 85acd3f4ab FINERACT-2081: Enhance technical documentation 2024-09-30 14:42:36 +03:00
Kristof JozsaandAdam Saghy d7c5947760 FINERACT-2081: downgrade Develocity for ge.apache.org compatibility 2024-09-26 10:32:09 +02:00
Andrii KulminskyiandAdam Saghy b68149c6cd FINERACT-2081: Skip loan processing if no overdue 2024-09-26 10:31:24 +02:00
Adam Saghy a655707e0a FINERACT-1968: Remove WIP tags from advanced-payment-allocation.adoc 2024-09-26 10:25:53 +02:00
Oleksii NovikovandAdam Saghy 837dc6da26 FINERACT-2081: Improve transaction summary with asset owner report 2024-09-25 16:42:46 +02:00
Kristof JozsaandGitHub 7ed0a825df Merge pull request #4042 from alberto-art3ch/enhancement/loan_account_mandatory_parameters
FINERACT-2081: Validation error in mandatory loan account parameters
2024-09-25 15:33:42 +02:00
Marta JankovicsandAdam Saghy 31c9079ad9 FINERACT-2081: Payments after loan charge - payment allocation 2024-09-25 10:31:08 +02:00
Jose Alberto Hernandez cc3a492d57 FINERACT-2081: Validation error in mandatory loan account parameters 2024-09-24 16:51:10 -05:00
dependabot[bot]andArnold Gálovics 2d0c8db993 Bump actions/setup-node from 4.0.3 to 4.0.4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.3 to 4.0.4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/1e60f620b9541d16bece96c5465dc8ee9832be0b...0a44ba7841725637a19e28fa30b79a866c81b0a6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-20 13:40:20 +02:00
Kristof JozsaandAdam Saghy f30264ccfc FINERACT-2081: fix last installment marking on pay-off 2024-09-18 15:07:02 +02:00
Soma SörösandAdam Saghy 35c04c0141 FINERACT-1981: Interest recalculation - Recalculate Interest For Loan - Business step for COB - progressive loan 2024-09-18 15:04:18 +02:00
Andrii KulminskyiandAdam Saghy 7b058d6772 FINERACT-2067: NPE business date partition step COB 2024-09-18 13:32:36 +02:00
Kristof JozsaandAdam Saghy c511385946 FINERACT-1981: fix pay-off for TILL_REST_FREQUENCY_DATE 2024-09-18 09:33:00 +02:00
Arnold GalovicsandArnold Gálovics e0071715d2 Fixed caching request body 2024-09-17 18:39:49 +02:00
Mend RenovateandArnold Gálovics df1b6b3632 Update all non-major dependencies 2024-09-17 18:39:49 +02:00
adam.magyariandAdam Saghy 1cb9a36865 FINERACT-2114: show interest rate change in business events 2024-09-17 13:52:01 +02:00
Mend RenovateandArnold Gálovics 77b8eeef95 Update dependency com.google.errorprone:error_prone_core to v2.32.0 2024-09-17 12:21:26 +02:00
dependabot[bot]andAdam Saghy 4d8abe559e Bump gradle/actions from 4.0.1 to 4.1.0
Bumps [gradle/actions](https://github.com/gradle/actions) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/16bf8bc8fe830fa669c3c9f914d3eb147c629707...d156388eb19639ec20ade50009f3d199ce1e2808)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-13 23:31:13 +02:00
Janos MeszarosandAdam Saghy 49e9005109 FINERACT-1981: Pay-off schedule handling 2024-09-13 13:26:32 +02:00
Marta JankovicsandAdam Saghy e94ce7f1df FINERACT-2081: Payments after loan charge - COB accrual 2024-09-13 12:12:06 +02:00
Arnold GalovicsandArnold Gálovics 889fc8d2cb Explicitly defaulting to null containers for Java client code generation 2024-09-12 13:20:52 +02:00
Arnold GalovicsandArnold Gálovics b44269d9d9 Reverted spring upgrades 2024-09-12 13:20:52 +02:00
Mend RenovateandArnold Gálovics 3baf807942 Update all non-major dependencies 2024-09-12 13:20:52 +02:00
Janos MeszarosandAdam Saghy 0e08903811 FINERACT-2081: Fix backdated multi disbursement on Progressive Loan 2024-09-12 09:53:19 +02:00
Mend RenovateandArnold Gálovics b4a68ba502 Update non-major 2024-09-11 16:57:25 +02:00
Arnold GalovicsandArnold Gálovics 13413f19f3 Spotbugs adjustments 2024-09-11 15:27:03 +02:00
Mend RenovateandArnold Gálovics 3ab648c088 Update plugin com.github.spotbugs to v6 2024-09-11 15:27:03 +02:00
Janos MeszarosandAdam Saghy 5215e565f7 FINERACT-2114: Fix EMI Calculator get payment details 2024-09-11 14:13:22 +02:00
dependabot[bot]andArnold Gálovics 02296305cb Bump actions/setup-java from 4.2.2 to 4.3.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.2 to 4.3.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/6a0805fcefea3d4657a47ac4c165951e33482018...2dfa2011c5b2a0f1489bf9e433881c92c1631f88)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 09:42:45 +02:00
Mend RenovateandArnold Gálovics 9ce4c9f7ea Update gradle/wrapper-validation-action digest to f9c9c57 2024-09-09 09:32:46 +02:00
Jaswanth-Sriram-VeturiandAdam Saghy 8a28f1e39f FINERACT-2081: Fix update client identifier 2024-09-08 16:13:30 +01:00
dependabot[bot]andArnold Gálovics b86a24084b Bump actions/setup-java from 4.2.1 to 4.2.2
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/99b8673ff64fbf99d8d325f52d9a5bdedb8483e9...6a0805fcefea3d4657a47ac4c165951e33482018)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 09:37:37 +02:00
dependabot[bot]andArnold Gálovics 4b65f3815e Bump gradle/actions from 3.5.0 to 4.0.1
Bumps [gradle/actions](https://github.com/gradle/actions) from 3.5.0 to 4.0.1.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/d9c87d481d55275bb5441eef3fe0e46805f9ef70...16bf8bc8fe830fa669c3c9f914d3eb147c629707)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 09:37:29 +02:00
dependabot[bot]andArnold Gálovics 21e9ef7e91 Bump actions/upload-artifact from 4.3.4 to 4.4.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.4.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...50769540e7f4bd5e21e526ee35c689e35e0d6874)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 09:37:20 +02:00
zeyad2003andAdam Saghy 367ff0fd96 FINERACT-2106: fix the misuse of ToApiJsonSerializer 2024-09-05 18:10:10 +01:00
Clay JohnsonandAdam Saghy 24c83b5f9f Enable local build cache to take advantage of cache from setup-gradle action 2024-09-05 12:01:30 +01:00
Clay JohnsonandAdam Saghy 112a3f23e9 Use setup-gradle action where Gradle builds occur 2024-09-05 12:01:30 +01:00
Clay JohnsonandAdam Saghy c2984228f6 Remove the gradle caching flag from the setup-java action 2024-09-05 12:01:30 +01:00
Clay JohnsonandAdam Saghy a87b8c6f9a Set project id to "fineract" 2024-09-05 12:01:13 +01:00
Clay JohnsonandAdam Saghy db6fe2c86f Migrate GitHub Actions to set the DEVELOCITY_ACCESS_KEY env var 2024-09-05 12:01:13 +01:00
Clay JohnsonandAdam Saghy 3057c3a519 Replace Gradle Enterprise Gradle Plugin with new Develocity Gradle Plugin 2024-09-05 12:01:13 +01:00
Clay JohnsonandAdam Saghy 911c6e4b33 Update Common Custom User Data Gradle Plugin to 2.0.2 2024-09-05 12:01:13 +01:00
Marta JankovicsandAdam Saghy 1ba689dba3 FINERACT-2081: org.eclipse.persistence version 4.0.2 2024-09-05 09:27:51 +01:00
Janos MeszarosandAdam Saghy 803a575b04 FINERACT-2114: EMI Calculator outstanding balance correction 2024-09-04 15:30:13 +01:00
kaibalya-fynarfinandGitHub a6e35170a7 FINERACT-2120: Add new global configuration - enable payment hub integration (#4043) 2024-09-04 12:54:01 +05:30
Marta JankovicsandAleksandar Vidakovic d5d7f7f5a0 FINERACT-2119: Address jobs by name - fix liquibase order for custom modules 2024-09-01 18:55:23 +02:00
Soma SörösandAdam Saghy 8b89aaf413 FINERACT-1981: Interest recalculation - Recalculate Interest For Loan - Business step for COB 2024-08-29 21:18:05 +02:00
Jose Alberto HernandezandAdam Saghy b4f5a3a175 FINERACT-2081: fix Loan product validation error messages with an extra period 2024-08-29 19:23:59 +02:00
Marta JankovicsandAdam Saghy 3aaa2ffe9c FINERACT-2119: Address jobs by name 2024-08-28 16:46:20 +02:00
Adam Saghy eef6a8da5e FINERACT-2081: Make Accrual posting business step optional 2024-08-28 16:42:37 +02:00
Jose Alberto HernandezandAdam Saghy 284022abba FINERACT-2081: fix NPE Loan product creation with null values in Interest rate or Number of repayments 2024-08-28 15:38:11 +02:00
Adam Saghy 6ca02686d2 FINERACT-2114: Reschedule validations enhancement 2024-08-26 13:18:10 +02:00
Dhruv SonagaraandGitHub 5384236220 FINERACT-2115: Create account transfers validation tests (#4011)
* FINERACT-2115 Create credit bureau and account transfers validation tests

* Added account trasnfer validation TCs and updated accot trasnfer helper class

* Updated the spotless changes

* Removed the unnescessary code and updated the account transfer validation tests

* Added the test cases for invalid date format for the account transfer test

* Updated the code according to the right response code for account transfer negative TC
2024-08-25 12:48:39 +05:30
Jose Alberto HernandezandAdam Saghy 4698764e07 FINERACT-2081: Loan account approval and disburse validation error code changed 2024-08-23 21:06:45 +02:00
Adam Saghy 3456e1eb81 FINERACT-2114: Reschedule validations enhancement 2024-08-23 14:25:48 +02:00
Janos MeszarosandAdam Saghy 1286b1e72f FINERACT-2114: Interest rate modification 2024-08-22 13:37:02 +02:00
Kristof JozsaandAdam Saghy 80f4627623 FINERACT-1981: pay-off transaction for progressive loans 2024-08-22 13:17:30 +02:00
Jose Alberto HernandezandAdam Saghy 5ebd3429c6 FINERACT-2107: Loan transaction Interest Refund GL Entries 2024-08-22 11:04:26 +02:00
Kristof JozsaandAdam Saghy 7304af57bc FINERACT-1981: pay-off transaction for progressive loans 2024-08-21 17:55:18 +02:00
Adam Saghy 721ceaa32c FINERACT-2081: Fix missing application.properties during build issue 2024-08-21 08:12:41 +02:00
Jose Alberto HernandezandAdam Saghy 04533f03c6 FINERACT-2117: Early and late payment - In advance payment strategy: Adjust last, unpaid period 2024-08-19 13:20:47 +02:00
Janos MeszarosandAdam Saghy 3e923bf483 FINERACT-2114: Interest rate modification, adjust reschedule validation 2024-08-16 15:04:20 +02:00
Adam Saghy 2a445a5862 FINERACT-2081: Rework Loan Account data 2024-08-15 13:19:01 +02:00
Kristof JozsaandAdam Saghy 22b10d933b FINERACT-2081: fix loan status on CBR reverse 2024-08-11 15:13:36 +02:00
Kristof JozsaandAdam Saghy c70007e30a FINERACT-2081: clear closed_on date on repayment reversal 2024-08-09 07:51:48 +02:00
Janos MeszarosandAdam Saghy 69dd19c74b FINERACT-1981: Repayment Schedule Interest Model with multi disbursement support, AdvPymnTxProcessor fix 2024-08-07 23:04:29 +02:00
Kristof JozsaandAdam Saghy 26388ea518 FINERACT-2081: loan validation code improvements 2024-08-07 11:18:34 +02:00
Soma SörösandAdam Saghy 81bf50d29e FINERACT-1694: Improve Testing of External Business Events 2024-08-07 09:57:42 +02:00
Kristof JozsaandAdam Saghy 23b50994cc FINERACT-2081: fix loan creation validations 2024-08-06 21:45:57 +02:00
Jose Alberto HernandezandAdam Saghy e967426db7 FINERACT-2081: Loan Unpaid Payable Interest fix to include amount paid 2024-08-05 12:00:10 +02:00
Kristof JozsaandAdam Saghy ec1c55d022 FINERACT-2081: fix disburse error scenario 2024-08-02 22:48:24 +02:00
Jose Alberto HernandezandAdam Saghy bd19f28cb6 FINERACT-2111: Performance issue on Retrieve Loan API - Transactions 2024-08-02 22:48:01 +02:00
Soma SörösandAdam Saghy 141e26fda7 FINERACT-1694: Upgrade External Business Event Handling 2024-08-02 14:50:26 +02:00
Jose Alberto HernandezandAdam Saghy 06edb4c7d9 FINERACT-2081: Loan account summary with Unpaid Payable Interest 2024-08-01 23:12:38 +02:00
Adam Saghy 526fb6b3a8 Revert "FINERACT-2081: Loan account summary with Unpaid Payable Interest"
This reverts commit 737455f52f.
2024-07-31 09:20:43 +02:00
Jose Alberto HernandezandAdam Saghy 737455f52f FINERACT-2081: Loan account summary with Unpaid Payable Interest 2024-07-31 09:19:09 +02:00
1511 changed files with 120770 additions and 30549 deletions
+17
View File
@@ -0,0 +1,17 @@
github:
description: "Apache Fineract"
homepage: https://fineract.apache.org
labels:
- apache
- banking
- finance
- fintech
- group-lending
- group-savings
- java
- lending
- loans
- microfinance
- savings
- social-impact
- tech4good
+11 -15
View File
@@ -1,35 +1,31 @@
name: Fineract Docker build - MariaDB name: Fineract Docker build - MariaDB
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
IMAGE_NAME: fineract
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build the image - name: Build the image
run: ./gradlew --no-daemon --console=plain :fineract-provider:clean :fineract-provider:build :fineract-provider:jibDockerBuild -x test -x cucumber run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber
- name: Start the stack - name: Start the stack
run: docker compose up -d run: docker compose -f docker-compose.yml up -d
- name: Wait for stack to come up
run: sleep 500
- name: Check the stack - name: Check the stack
run: docker ps run: docker ps
- name: Check health - name: Check health
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health
- name: Check info - name: Check info
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 ))
+10 -14
View File
@@ -1,35 +1,31 @@
name: Fineract Docker build - PostgreSQL name: Fineract Docker build - PostgreSQL
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
IMAGE_NAME: fineract
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build the image - name: Build the image
run: ./gradlew --no-daemon --console=plain :fineract-provider:clean :fineract-provider:build :fineract-provider:jibDockerBuild -x test -x cucumber run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber
- name: Start the Standalone Stack - name: Start the Standalone Stack
run: docker compose -f docker-compose-postgresql.yml up -d run: docker compose -f docker-compose-postgresql.yml up -d
- name: Wait for stack to come up
run: sleep 500
- name: Check the stack - name: Check the stack
run: docker ps run: docker ps
- name: Check health - name: Check health
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health
- name: Check info - name: Check info
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 ))
+8 -10
View File
@@ -1,36 +1,34 @@
name: Fineract Documentation build name: Fineract Documentation build
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4 uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with: with:
node-version: 16 node-version: 16
- name: Congfigure vega-cli - name: Congfigure vega-cli
run: npm i -g vega-cli --unsafe run: npm i -g vega-cli --unsafe
- name: Validate Gradle wrapper - name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142 uses: gradle/wrapper-validation-action@f9c9c575b8b21b6485636a91ffecd10e558c62f6
- name: Install additional software - name: Install additional software
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install ghostscript graphviz -y sudo apt-get install ghostscript graphviz -y
- name: Documentation build - name: Documentation build
run: ./gradlew --no-daemon --console=plain doc run: ./gradlew --no-daemon --console=plain doc
@@ -0,0 +1,47 @@
name: Fineract Build Progressive Loan Embeddable Jar & Test with a Sample Application
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with:
java-version: '17'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build Embeddable Jar
run: ./gradlew --no-daemon --console=plain :fineract-progressive-loan-embeddable-schedule-generator:shadowJar
- name: Pick up the JAR filename
run: |
EMBEDDABLE_JAR_FILE=(`ls fineract-progressive-loan-embeddable-schedule-generator/build/libs/*-all.jar | head -n 1`)
echo "EMBEDDABLE_JAR_FILE=$EMBEDDABLE_JAR_FILE" >> $GITHUB_ENV
- name: Run unit tests
run: ./gradlew --no-daemon --console=plain :fineract-progressive-loan-embeddable-schedule-generator:test
- name: Build Sample Application
run: |
mkdir sample-app
javac -cp $EMBEDDABLE_JAR_FILE -d sample-app fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java
env:
EMBEDDABLE_JAR_FILE: ${{ env.EMBEDDABLE_JAR_FILE }}
- name: Run Schedule Generator Sample Application
run: |
java -cp $EMBEDDABLE_JAR_FILE:sample-app Main
java -cp $EMBEDDABLE_JAR_FILE:sample-app Main 25
env:
EMBEDDABLE_JAR_FILE: ${{ env.EMBEDDABLE_JAR_FILE }}
- name: Archive test results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: test-results
path: |
build/reports/
fineract-progressive-loan-embeddable-schedule-generator/build/reports/
+11 -27
View File
@@ -1,51 +1,42 @@
name: Fineract Build & Test - MariaDB name: Fineract Build & Test - MariaDB
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
services: services:
mariad: mariad:
image: mariadb:11.2 image: mariadb:11.5.2
ports: ports:
- 3306:3306 - 3306:3306
env: env:
MARIADB_ROOT_PASSWORD: mysql MARIADB_ROOT_PASSWORD: mysql
options: --health-cmd="healthcheck.sh --su-mysql --connect --innodb_initialized" --health-interval=5s --health-timeout=2s --health-retries=3 options: --health-cmd="healthcheck.sh --su-mysql --connect --innodb_initialized" --health-interval=5s --health-timeout=2s --health-retries=3
mock-oauth2-server: mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:2.1.1 image: ghcr.io/navikt/mock-oauth2-server:2.1.10
ports: ports:
- 9000:9000 - 9000:9000
env: env:
SERVER_PORT: 9000 SERVER_PORT: 9000
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }' JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
env: env:
TZ: Asia/Kolkata TZ: Asia/Kolkata
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle and Validate Wrapper
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4 uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
with: with:
node-version: 16 validate-wrappers: true
- name: Congfigure vega-cli
run: npm i -g vega-cli --unsafe
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142
- name: Verify MariaDB connection - name: Verify MariaDB connection
run: | run: |
while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do
@@ -66,11 +57,6 @@ jobs:
sleep 10 sleep 10
docker exec localstack awslocal s3api create-bucket --bucket fineract-reports docker exec localstack awslocal s3api create-bucket --bucket fineract-reports
echo "LocalStack initialization complete" echo "LocalStack initialization complete"
- name: Install additional software
run: |
sudo apt-get update
sudo apt-get install ghostscript graphviz -y
- name: Build & Test - name: Build & Test
env: env:
AWS_ENDPOINT_URL: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566
@@ -85,10 +71,9 @@ jobs:
./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test -x :fineract-e2e-tests-runner:test ./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test -x :fineract-e2e-tests-runner:test
./gradlew --no-daemon --console=plain :twofactor-tests:test ./gradlew --no-daemon --console=plain :twofactor-tests:test
./gradlew --no-daemon --console=plain :oauth2-tests:test ./gradlew --no-daemon --console=plain :oauth2-tests:test
- name: Archive test results - name: Archive test results
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: test-results name: test-results
path: | path: |
@@ -96,10 +81,9 @@ jobs:
integration-tests/build/reports/ integration-tests/build/reports/
twofactor-tests/build/reports/ twofactor-tests/build/reports/
oauth2-tests/build/reports/ oauth2-tests/build/reports/
- name: Archive server logs - name: Archive server logs
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: server-logs name: server-logs
path: | path: |
+11 -27
View File
@@ -1,51 +1,42 @@
name: Fineract Build & Test - MySQL name: Fineract Build & Test - MySQL
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
services: services:
mariad: mariad:
image: mysql:8.3 image: mysql:9.1
ports: ports:
- 3306:3306 - 3306:3306
env: env:
MYSQL_ROOT_PASSWORD: mysql MYSQL_ROOT_PASSWORD: mysql
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
mock-oauth2-server: mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:2.1.1 image: ghcr.io/navikt/mock-oauth2-server:2.1.10
ports: ports:
- 9000:9000 - 9000:9000
env: env:
SERVER_PORT: 9000 SERVER_PORT: 9000
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }' JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
env: env:
TZ: Asia/Kolkata TZ: Asia/Kolkata
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle and Validate Wrapper
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4 uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
with: with:
node-version: 16 validate-wrappers: true
- name: Congfigure vega-cli
run: npm i -g vega-cli --unsafe
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142
- name: Verify MariaDB connection - name: Verify MariaDB connection
run: | run: |
while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do
@@ -66,11 +57,6 @@ jobs:
sleep 10 sleep 10
docker exec localstack awslocal s3api create-bucket --bucket fineract-reports docker exec localstack awslocal s3api create-bucket --bucket fineract-reports
echo "LocalStack initialization complete" echo "LocalStack initialization complete"
- name: Install additional software
run: |
sudo apt-get update
sudo apt-get install ghostscript graphviz -y
- name: Build & Test - name: Build & Test
env: env:
AWS_ENDPOINT_URL: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566
@@ -85,10 +71,9 @@ jobs:
./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test :fineract-e2e-tests-runner:test -PdbType=mysql ./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test :fineract-e2e-tests-runner:test -PdbType=mysql
./gradlew --no-daemon --console=plain :twofactor-tests:test -PdbType=mysql ./gradlew --no-daemon --console=plain :twofactor-tests:test -PdbType=mysql
./gradlew --no-daemon --console=plain :oauth2-tests:test -PdbType=mysql ./gradlew --no-daemon --console=plain :oauth2-tests:test -PdbType=mysql
- name: Archive test results - name: Archive test results
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: test-results name: test-results
path: | path: |
@@ -96,10 +81,9 @@ jobs:
integration-tests/build/reports/ integration-tests/build/reports/
twofactor-tests/build/reports/ twofactor-tests/build/reports/
oauth2-tests/build/reports/ oauth2-tests/build/reports/
- name: Archive server logs - name: Archive server logs
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: server-logs name: server-logs
path: | path: |
+11 -27
View File
@@ -1,52 +1,43 @@
name: Fineract Build & Test - PostgreSQL name: Fineract Build & Test - PostgreSQL
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
services: services:
postgresql: postgresql:
image: postgres:16.1 image: postgres:17.4
ports: ports:
- 5432:5432 - 5432:5432
env: env:
POSTGRES_USER: root POSTGRES_USER: root
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
options: --health-cmd="pg_isready -q -d postgres -U root" --health-interval=5s --health-timeout=2s --health-retries=3 options: --health-cmd="pg_isready -q -d postgres -U root" --health-interval=5s --health-timeout=2s --health-retries=3
mock-oauth2-server: mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:2.1.1 image: ghcr.io/navikt/mock-oauth2-server:2.1.10
ports: ports:
- 9000:9000 - 9000:9000
env: env:
SERVER_PORT: 9000 SERVER_PORT: 9000
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }' JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
env: env:
TZ: Asia/Kolkata TZ: Asia/Kolkata
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle and Validate Wrapper
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4 uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
with: with:
node-version: 16 validate-wrappers: true
- name: Congfigure vega-cli
run: npm i -g vega-cli --unsafe
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142
- name: Verify PostgreSQL connection - name: Verify PostgreSQL connection
run: | run: |
while ! pg_isready -d postgres -U root -h 127.0.0.1 -p 5432 ; do while ! pg_isready -d postgres -U root -h 127.0.0.1 -p 5432 ; do
@@ -67,11 +58,6 @@ jobs:
sleep 10 sleep 10
docker exec localstack awslocal s3api create-bucket --bucket fineract-reports docker exec localstack awslocal s3api create-bucket --bucket fineract-reports
echo "LocalStack initialization complete" echo "LocalStack initialization complete"
- name: Install additional software
run: |
sudo apt-get update
sudo apt-get install ghostscript graphviz -y
- name: Build & Test - name: Build & Test
env: env:
AWS_ENDPOINT_URL: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566
@@ -86,10 +72,9 @@ jobs:
./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test :fineract-e2e-tests-runner:test -PdbType=postgresql ./gradlew --no-daemon --console=plain test -x :twofactor-tests:test -x :oauth2-test:test :fineract-e2e-tests-runner:test -PdbType=postgresql
./gradlew --no-daemon --console=plain :twofactor-tests:test -PdbType=postgresql ./gradlew --no-daemon --console=plain :twofactor-tests:test -PdbType=postgresql
./gradlew --no-daemon --console=plain :oauth2-tests:test -PdbType=postgresql ./gradlew --no-daemon --console=plain :oauth2-tests:test -PdbType=postgresql
- name: Archive test results - name: Archive test results
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: test-results name: test-results
path: | path: |
@@ -97,10 +82,9 @@ jobs:
integration-tests/build/reports/ integration-tests/build/reports/
twofactor-tests/build/reports/ twofactor-tests/build/reports/
oauth2-tests/build/reports/ oauth2-tests/build/reports/
- name: Archive server logs - name: Archive server logs
if: always() if: always()
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: server-logs name: server-logs
path: | path: |
+15 -16
View File
@@ -1,42 +1,41 @@
name: Fineract Tests name: Fineract Tests
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
IMAGE_NAME: fineract
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build the image - name: Build the image
run: ./gradlew --no-daemon --console=plain :fineract-provider:clean :fineract-provider:build :fineract-provider:jibDockerBuild -x test -x cucumber run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber
- name: Start the Fineract stack - name: Start the Fineract stack
run: docker compose -f docker-compose-postgresql.yml up -d run: docker compose -f docker-compose-postgresql-test-activemq.yml up -d
- name: Wait for stack to come up
run: sleep 400
- name: Check the stack - name: Check the stack
run: docker ps run: docker ps
- name: Check health Manager - name: Check health Manager
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health
- name: Execute tests - name: Execute tests
env: env:
BASE_URL: https://localhost:8443 BASE_URL: https://localhost:8443
TEST_USERNAME: mifos TEST_USERNAME: mifos
TEST_PASSWORD: password TEST_PASSWORD: password
TEST_STRONG_PASSWORD: A1b2c3d4e5f$
TEST_TENANT_ID: default TEST_TENANT_ID: default
INITIALIZATION_ENABLED: true INITIALIZATION_ENABLED: true
EVENT_VERIFICATION_ENABLED: false EVENT_VERIFICATION_ENABLED: true
run: ./gradlew --no-daemon --console=plain fineract-e2e-tests-runner:cucumber --tags 'not @Skip' allureReport ACTIVEMQ_BROKER_URL: tcp://localhost:61616
ACTIVEMQ_TOPIC_NAME: events
run: ./gradlew --no-daemon --console=plain :fineract-e2e-tests-runner:cucumber --tags 'not @Skip' allureReport
+26 -10
View File
@@ -1,36 +1,52 @@
name: Fineract Publish to DockerHub name: Fineract Publish to DockerHub
on: on:
push: push:
branches: branches:
- develop - develop
tags: tags:
- 1.* - 1.*
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
steps: steps:
- name: Checkout Source Code - name: Checkout Source Code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Extract branch name - name: Extract branch name
shell: bash shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch id: extract_branch
- name: Get Git Hashes
run: |
echo "short_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "long_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
id: git_hashes
- name: Build the Apache Fineract image - name: Build the Apache Fineract image
run: | run: |
./gradlew --no-daemon --console=plain :fineract-provider:jib -x test -x cucumber -Djib.to.auth.username=${{secrets.DOCKERHUB_USER}} -Djib.to.auth.password=${{secrets.DOCKERHUB_TOKEN}} -Djib.from.platforms=linux/amd64,linux/arm64 -Djib.to.image=apache/fineract -Djib.to.tags=${{ steps.extract_branch.outputs.branch }} TAGS=${{ steps.extract_branch.outputs.branch }}
if [ "${{ steps.extract_branch.outputs.branch }}" == "develop" ]; then
TAGS="$TAGS,${{ steps.git_hashes.outputs.short_hash }},${{ steps.git_hashes.outputs.long_hash }}"
fi
./gradlew --no-daemon --console=plain :fineract-provider:jib -x test -x cucumber \
-Djib.to.auth.username=${{secrets.DOCKERHUB_USER}} \
-Djib.to.auth.password=${{secrets.DOCKERHUB_TOKEN}} \
-Djib.from.platforms=linux/amd64,linux/arm64 \
-Djib.to.image=apache/fineract \
-Djib.to.tags=$TAGS
+12 -16
View File
@@ -1,41 +1,37 @@
name: Fineract ActiveMQ Smoke name: Fineract ActiveMQ Smoke
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
IMAGE_NAME: fineract
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build the image - name: Build the image
run: ./gradlew --no-daemon --console=plain :fineract-provider:clean :fineract-provider:build :fineract-provider:jibDockerBuild -x test -x cucumber run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber
- name: Start the ActiveMQ Stack - name: Start the ActiveMQ Stack
run: docker compose -f docker-compose-postgresql-activemq.yml up --scale fineract-worker=1 -d run: docker compose -f docker-compose-postgresql-activemq.yml up --scale fineract-worker=1 -d
- name: Wait for stack to come up
run: sleep 500
- name: Check the stack - name: Check the stack
run: docker ps run: docker ps
- name: Check health Manager - name: Check health Manager
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health
- name: Check health Worker1 - name: Check health Worker1
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/health
- name: Check info Manager - name: Check info Manager
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 ))
- name: Check info Worker1 - name: Check info Worker1
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/info | wc --chars) > 100 ))
- name: Run Smoke Test with Remote COB - name: Run Smoke Test with Remote COB
run: ./gradlew --no-daemon --console=plain :integration-tests:cleanTest :integration-tests:test --tests "org.apache.fineract.integrationtests.investor.externalassetowner.InitiateExternalAssetOwnerTransferTest.saleActiveLoanToExternalAssetOwnerAndBuybackADayLater" -PcargoDisabled run: ./gradlew --no-daemon --console=plain :integration-tests:cleanTest :integration-tests:test --tests "org.apache.fineract.integrationtests.investor.externalassetowner.InitiateExternalAssetOwnerTransferTest.saleActiveLoanToExternalAssetOwnerAndBuybackADayLater" -PcargoDisabled
+12 -16
View File
@@ -1,41 +1,37 @@
name: Fineract Kafka Smoke name: Fineract Kafka Smoke
on: [push, pull_request] on: [push, pull_request]
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
IMAGE_NAME: fineract
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
- name: Build the image - name: Build the image
run: ./gradlew --no-daemon --console=plain :fineract-provider:clean :fineract-provider:build :fineract-provider:jibDockerBuild -x test -x cucumber run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber
- name: Start the Kafka Stack - name: Start the Kafka Stack
run: docker compose -f docker-compose-postgresql-kafka.yml up --scale fineract-worker=1 -d run: docker compose -f docker-compose-postgresql-kafka.yml up --scale fineract-worker=1 -d
- name: Wait for stack to come up
run: sleep 500
- name: Check the stack - name: Check the stack
run: docker ps run: docker ps
- name: Check health Manager - name: Check health Manager
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/health
- name: Check health Worker1 - name: Check health Worker1
run: curl -f -k --retry 10 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/health run: curl -f -k --retry 60 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/health
- name: Check info Manager - name: Check info Manager
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8443/fineract-provider/actuator/info | wc --chars) > 100 ))
- name: Check info Worker1 - name: Check info Worker1
run: (( $(curl -f -k --retry 5 --retry-connrefused --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/info | wc --chars) > 100 )) run: (( $(curl -f -k --retry 5 --retry-all-errors --connect-timeout 30 --retry-delay 30 https://localhost:8444/fineract-provider/actuator/info | wc --chars) > 100 ))
- name: Run Smoke Test with Remote COB - name: Run Smoke Test with Remote COB
run: ./gradlew --no-daemon --console=plain :integration-tests:cleanTest :integration-tests:test --tests "org.apache.fineract.integrationtests.investor.externalassetowner.InitiateExternalAssetOwnerTransferTest.saleActiveLoanToExternalAssetOwnerAndBuybackADayLater" -PcargoDisabled run: ./gradlew --no-daemon --console=plain :integration-tests:cleanTest :integration-tests:test --tests "org.apache.fineract.integrationtests.investor.externalassetowner.InitiateExternalAssetOwnerTransferTest.saleActiveLoanToExternalAssetOwnerAndBuybackADayLater" -PcargoDisabled
+13 -11
View File
@@ -3,13 +3,11 @@ on:
push: push:
branches: branches:
- develop - develop
permissions: permissions:
contents: read contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
env: env:
TZ: Asia/Kolkata TZ: Asia/Kolkata
SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }} SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }}
@@ -17,19 +15,23 @@ jobs:
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }} SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
JAVA_BINARIES: .
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4 uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
with: with:
java-version: '17' java-version: '17'
distribution: 'zulu' distribution: 'zulu'
cache: gradle - name: Setup Gradle and Validate Wrapper
- name: Validate Gradle wrapper uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142 with:
- name: Sonarqube validate-wrappers: true
run: ./gradlew --no-daemon --console=plain -Dsonar.verbose=true -Dsonar.login=$SONAR_TOKEN -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.organization=$SONAR_ORGANIZATION -Dsonar.projectKey=$SONAR_PROJECT_KEY --info --stacktrace sonarqube - name: Build
run: ./gradlew --no-daemon --console=plain :fineract-provider:build -x test -x cucumber
- name: Sonar
run: ./gradlew --no-daemon --console=plain -Dsonar.verbose=true -Dsonar.token=$SONAR_TOKEN -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.organization=$SONAR_ORGANIZATION -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.java.binaries=$JAVA_BINARIES --info --stacktrace sonar
+1 -2
View File
@@ -7,7 +7,6 @@ on:
- cron: "0 0 * * *" - cron: "0 0 * * *"
permissions: permissions:
contents: read contents: read
jobs: jobs:
stale: stale:
permissions: permissions:
@@ -15,7 +14,7 @@ jobs:
pull-requests: write # for actions/stale to close stale PRs pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9 - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
# stale-issue-message: 'Stale issue message' # stale-issue-message: 'Stale issue message'
+3
View File
@@ -30,3 +30,6 @@ fineract-provider/src/main/generated/
**/out/ **/out/
gradleExp/ gradleExp/
.run/
.java-version
+70 -14
View File
@@ -1,6 +1,10 @@
Apache Fineract: A Platform for Microfinance Apache Fineract: A Platform for Microfinance
============ ============
[![Swagger Validation](https://validator.swagger.io/validator?url=https://sandbox.mifos.community/fineract-provider/swagger-ui/fineract.yaml)](https://validator.swagger.io/validator/debug?url=https://sandbox.mifos.community/fineract-provider/swagger-ui/fineract.yaml) [![build](https://github.com/apache/fineract/actions/workflows/build.yml/badge.svg)](https://github.com/apache/fineract/actions/workflows/build.yml) [![Docker Hub](https://img.shields.io/docker/pulls/apache/fineract.svg?logo=Docker)](https://hub.docker.com/r/apache/fineract) [![Docker Build](https://img.shields.io/docker/cloud/build/apache/fineract.svg?logo=Docker)](https://hub.docker.com/r/apache/fineract/builds) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=apache_fineract&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=apache_fineract) <!-- TODO Reactivate when there is a working CI-CD instance: [![Swagger Validation](https://validator.swagger.io/validator?url=https://sandbox.mifos.community/fineract-provider/swagger-ui/fineract.yaml)](https://validator.swagger.io/validator/debug?url=https://sandbox.mifos.community/fineract-provider/swagger-ui/fineract.yaml) -->
[![Build](https://github.com/apache/fineract/actions/workflows/build-mariadb.yml/badge.svg?branch=develop)](https://github.com/apache/fineract/actions/workflows/build-mariadb.yml)
[![Docker Hub](https://img.shields.io/docker/pulls/apache/fineract.svg?logo=Docker)](https://hub.docker.com/r/apache/fineract)
[![Docker Build](https://github.com/apache/fineract/actions/workflows/publish-dockerhub.yml/badge.svg)](https://github.com/apache/fineract/actions/workflows/publish-dockerhub.yml)
[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=apache_fineract&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=apache_fineract)
</b> </b>
@@ -22,15 +26,15 @@ If you are interested in contributing to this project, but perhaps don't quite k
REQUIREMENTS REQUIREMENTS
============ ============
* `Java >= 17` (Azul Zulu JVM is tested by our CI on GitHub Actions) * `Java >= 17` (Azul Zulu JVM is tested by our CI on GitHub Actions)
* MariaDB `11.2` * MariaDB `11.5.2`
You can run the required version of the database server in a container, instead of having to install it, like this: You can run the required version of the database server in a container, instead of having to install it, like this:
docker run --name mariadb-11.2 -p 3306:3306 -e MARIADB_ROOT_PASSWORD=mysql -d mariadb:11.2 docker run --name mariadb-11.5 -p 3306:3306 -e MARIADB_ROOT_PASSWORD=mysql -d mariadb:11.5.2
and stop and destroy it like this: and stop and destroy it like this:
docker rm -f mariadb-11.2 docker rm -f mariadb-11.5
<br>Beware that this database container database keeps its state inside the container and not on the host filesystem. It is lost when you destroy (rm) this container. This is typically fine for development. See [Caveats: Where to Store Data on the database container documentation](https://hub.docker.com/_/mariadb) re. how to make it persistent instead of ephemeral.<br> <br>Beware that this database container database keeps its state inside the container and not on the host filesystem. It is lost when you destroy (rm) this container. This is typically fine for development. See [Caveats: Where to Store Data on the database container documentation](https://hub.docker.com/_/mariadb) re. how to make it persistent instead of ephemeral.<br>
@@ -75,11 +79,9 @@ Run the following commands:
============ ============
1. Clone the repository or download and extract the archive file to your local directory. 1. Clone the repository or download and extract the archive file to your local directory.
2. Run `./gradlew clean bootJar` to build a modern cloud native fully self contained JAR file which will be created at `fineract-provider/build/libs` directory. 2. Run `./gradlew clean bootJar` to build a modern cloud native fully self contained JAR file which will be created at `fineract-provider/build/libs` directory.
3. As we are not allowed to include a JDBC driver in the built JAR, download a JDBC driver of your choice. For example: `wget https://downloads.mariadb.com/Connectors/java/connector-java-3.3.2/mariadb-java-client-3.3.2.jar` 3. As we are not allowed to include a JDBC driver in the built JAR, download a JDBC driver of your choice. For example: `wget https://dlm.mariadb.com/4174416/Connectors/java/connector-java-3.5.2/mariadb-java-client-3.5.2.jar`
4. Start the jar and pass the directory where you have downloaded the JDBC driver as loader.path, for example: `java -Dloader.path=. -jar fineract-provider/build/libs/fineract-provider.jar` (does not require external Tomcat) 4. Start the jar and pass the directory where you have downloaded the JDBC driver as loader.path, for example: `java -Dloader.path=. -jar fineract-provider/build/libs/fineract-provider.jar` (does not require external Tomcat)
NOTE: we cannot upgrade to version 3.0.x of the MariaDB driver just yet; have to wait until 3.0.4 is out for a bug fix.
The tenants database connection details are configured [via environment variables (as with Docker container)](#instructions-to-run-using-docker-and-docker-compose), e.g. like this: The tenants database connection details are configured [via environment variables (as with Docker container)](#instructions-to-run-using-docker-and-docker-compose), e.g. like this:
export FINERACT_HIKARI_PASSWORD=verysecret export FINERACT_HIKARI_PASSWORD=verysecret
@@ -142,15 +144,54 @@ We recommend using the JAR instead of the WAR file deployment, because it's much
Note that with the 1.4 release the tenants database pool configuration changed from Tomcat DBCP in XML to an embedded Hikari, configured by environment variables, see above. Note that with the 1.4 release the tenants database pool configuration changed from Tomcat DBCP in XML to an embedded Hikari, configured by environment variables, see above.
INSTRUCTIONS: How to execute Integration Tests INSTRUCTIONS: How to run tests
============ ============
> Note that if this is the first time to access MySQL DB, then you may need to reset your password.
Run the following commands: Unit tests
1. `./gradlew createDB -PdbName=fineract_tenants` ----------
1. `./gradlew createDB -PdbName=fineract_default`
1. `./gradlew clean test`
Here's how to run the set of relatviely fast and indepedent Fineract tests:
```bash
./gradlew test -x :twofactor-tests:test -x :oauth2-tests:test -x :integration-tests:test
```
This runs nearly 1,000 tests and completes in a few minutes on decent hardware.
They shouldn't need any special servers/services running.
Integration tests
-----------------
Running tests with external dependencies yourself is a multi-step process with many moving parts.
Sometimes there are arbitrary failures and the prerequisite setup can be daunting.
A full local integration test run (on a developer workstation) covering every possible test using every external service and every supported relational database engine could take an entire day, and that's assuming everything is properly configured and runs as expected.
Right now we depend on GitHub to know if "the build" is passing (it's actually multiple builds).
The authoritative source of truth for what commands/services/tests to run, how, and when are the files in `.github/workflows/`.
Output from runs based on those configuration files appears at <https://github.com/apache/fineract/actions>.
Note these builds are run in [short-lived virtual machines](https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners), so locally reproducing the same may require additional effort, such as these extra clean-up procedures:
```bash
# Destroy anything untracked by git.
# ⚠️ This may delete something important, e.g. a finely-tuned IDE configuration.
git clean --force -dx
# Destroy various caches and configs.
# ⚠️ This may delete gibibytes of cached data, making the next build very slow.
rm -rf ~/.gradle ~/.m2 /tmp/cargo*
# Destroy any Java containers left running.
# 💚 This is generally very safe to run between builds.
ps auxwww | grep [c]argo | awk '{ print $2 }' | xargs -r kill
```
Testing within IDEs
-----------------
See the next section for testing in Eclipse.
See <https://fineract-academy.com> for testing in IntelliJ.
INSTRUCTIONS: How to run and debug in Eclipse IDE INSTRUCTIONS: How to run and debug in Eclipse IDE
============ ============
@@ -218,6 +259,21 @@ id -u ${GROUP}
Please make sure that you are not checking in your changed values. The defaults should normally work for most people. Please make sure that you are not checking in your changed values. The defaults should normally work for most people.
INSTRUCTIONS: How to build documentation
===================================================
Run the following command:
```bash
./gradlew doc
```
Some dependencies are required (e.g. Ghostscript, Graphviz), see `.github/workflows/build-documentation.yml` for hints.
Additionally, IDEs such as IntelliJ are useful for editing the AsciiDoc source files while providing a live rendered preview.
HTML rendered from the AsciiDoc source files is also available online at <https://fineract.apache.org/docs/current/>.
Connection pool configuration Connection pool configuration
============================= =============================
@@ -448,7 +504,7 @@ Apache Fineract / Mifos X Demo (November 2016) - <https://www.youtube.com/watch?
We use Swagger-UI to generate and maintain our API documentation, you can see the demo video [here](https://www.youtube.com/watch?v=FlVd-0YAo6c) or a live version We use Swagger-UI to generate and maintain our API documentation, you can see the demo video [here](https://www.youtube.com/watch?v=FlVd-0YAo6c) or a live version
[here](https://sandbox.mifos.community/fineract-provider/swagger-ui/index.html). If you interested to know more about Swagger-UI you can check their [website](https://swagger.io/). [here](https://sandbox.mifos.community/fineract-provider/swagger-ui/index.html). If you interested to know more about Swagger-UI you can check their [website](https://swagger.io/).
<br>GORVENANCE AND POLICIES <br>GOVERNANCE AND POLICIES
======================= =======================
[Becoming a Committer](https://cwiki.apache.org/confluence/display/FINERACT/Becoming+a+Committer) [Becoming a Committer](https://cwiki.apache.org/confluence/display/FINERACT/Becoming+a+Committer)
+127 -46
View File
@@ -19,7 +19,7 @@
// TODO: this is work in progress, please follow FINERACT-1171 // TODO: this is work in progress, please follow FINERACT-1171
buildscript { buildscript {
ext { ext {
jacocoVersion = '0.8.11' jacocoVersion = '0.8.12'
retrofitVersion = '2.9.0' retrofitVersion = '2.9.0'
okhttpVersion = '4.9.3' okhttpVersion = '4.9.3'
oltuVersion = '1.0.1' oltuVersion = '1.0.1'
@@ -46,7 +46,8 @@ buildscript {
'fineract-avro-schemas', 'fineract-avro-schemas',
'fineract-e2e-tests-core', 'fineract-e2e-tests-core',
'fineract-e2e-tests-runner', 'fineract-e2e-tests-runner',
'fineract-progressive-loan' 'fineract-progressive-loan',
'fineract-progressive-loan-embeddable-schedule-generator'
].contains(it.name) ].contains(it.name)
} }
fineractPublishProjects = subprojects.findAll{ fineractPublishProjects = subprojects.findAll{
@@ -66,7 +67,8 @@ buildscript {
'fineract-report', 'fineract-report',
'fineract-branch', 'fineract-branch',
'fineract-document', 'fineract-document',
'fineract-progressive-loan' 'fineract-progressive-loan',
'fineract-progressive-loan-embeddable-schedule-generator'
].contains(it.name) ].contains(it.name)
} }
npmRepository = 'https://npm.pkg.github.com' npmRepository = 'https://npm.pkg.github.com'
@@ -77,42 +79,39 @@ buildscript {
dependencies { dependencies {
classpath 'com.bmuschko:gradle-cargo-plugin:2.9.0' classpath 'com.bmuschko:gradle-cargo-plugin:2.9.0'
classpath 'org.eclipse.persistence:eclipselink:4.0.0' classpath 'org.eclipse.persistence:eclipselink:4.0.2'
classpath 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' classpath 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0'
classpath 'com.google.cloud.tools:jib-layer-filter-extension-gradle:0.3.0' classpath 'com.google.cloud.tools:jib-layer-filter-extension-gradle:0.3.0'
classpath 'org.apache.commons:commons-lang3:3.14.0' classpath 'org.apache.commons:commons-lang3:3.17.0'
classpath 'io.swagger.core.v3:swagger-jaxrs2-jakarta:2.2.22' classpath 'io.swagger.core.v3:swagger-jaxrs2-jakarta:2.2.22'
classpath 'jakarta.servlet:jakarta.servlet-api:6.0.0' classpath 'jakarta.servlet:jakarta.servlet-api:6.1.0'
} }
} }
plugins { plugins {
id 'me.qoomon.git-versioning' version '6.4.3' id 'me.qoomon.git-versioning' version '6.4.4'
id "org.barfuin.gradle.taskinfo" version "2.2.0" id "org.barfuin.gradle.taskinfo" version "2.2.0"
id 'com.adarshr.test-logger' version '4.0.0' id 'com.adarshr.test-logger' version '4.0.0'
id 'com.diffplug.spotless' version '6.25.0' apply false id 'com.diffplug.spotless' version '6.25.0' apply false
id 'org.nosphere.apache.rat' version '0.8.1' apply false id 'org.nosphere.apache.rat' version '0.8.1' apply false
id 'com.github.hierynomus.license' version '0.16.1' apply false id 'com.github.hierynomus.license' version '0.16.1' apply false
id 'com.github.jk1.dependency-license-report' version '2.5' apply false id 'com.github.jk1.dependency-license-report' version '2.9' apply false
id 'org.zeroturnaround.gradle.jrebel' version '1.2.0' apply false id 'org.zeroturnaround.gradle.jrebel' version '1.2.0' apply false
id 'org.springframework.boot' version '3.2.6' apply false id 'org.springframework.boot' version '3.4.4' apply false
id 'net.ltgt.errorprone' version '3.1.0' apply false id 'net.ltgt.errorprone' version '4.1.0' apply false
id 'io.swagger.core.v3.swagger-gradle-plugin' version '2.2.19' apply false id 'io.swagger.core.v3.swagger-gradle-plugin' version '2.2.23' apply false
id 'com.gorylenko.gradle-git-properties' version '2.4.1' apply false id 'com.gorylenko.gradle-git-properties' version '2.4.2' apply false
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.asciidoctor.jvm.epub' version '3.3.2' apply false id 'com.google.cloud.tools.jib' version '3.4.4' apply false
// id 'org.asciidoctor.jvm.revealjs' version '3.3.2' apply false id 'org.sonarqube' version '6.0.1.5171'
// id 'org.asciidoctor.jvm.gems' version '3.3.2' apply false id 'com.github.andygoossens.modernizer' version '1.10.0' apply false
id 'org.asciidoctor.kindlegen.base' version '3.2.0' apply false
id 'com.google.cloud.tools.jib' version '3.4.2' apply false
id 'org.sonarqube' version '4.4.1.3373'
id 'com.github.andygoossens.modernizer' version '1.9.0' apply false
// TODO: upgrade to 6.0.4 // TODO: upgrade to 6.0.4
id 'com.github.spotbugs' version '5.2.5' apply false id 'com.github.spotbugs' version '6.0.26' apply false
id 'se.thinkcode.cucumber-runner' version '0.0.11' apply false id 'se.thinkcode.cucumber-runner' version '0.0.11' apply false
id "com.github.davidmc24.gradle.plugin.avro-base" version "1.9.1" apply false id "com.github.davidmc24.gradle.plugin.avro-base" version "1.9.1" apply false
id 'org.openapi.generator' version '7.2.0' apply false id 'org.openapi.generator' version '7.8.0' apply false
id 'com.gradleup.shadow' version '8.3.5' apply false
} }
apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle" apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle"
@@ -151,11 +150,39 @@ allprojects {
mavenCentral() mavenCentral()
} }
configurations {
api {
canBeResolved = true
}
}
apply plugin: 'com.adarshr.test-logger' apply plugin: 'com.adarshr.test-logger'
apply plugin: 'com.diffplug.spotless' apply plugin: 'com.diffplug.spotless'
apply plugin: 'com.github.hierynomus.license' apply plugin: 'com.github.hierynomus.license'
apply plugin: 'net.ltgt.errorprone'
tasks.withType(JavaCompile).configureEach {
options.errorprone {
disableWarningsInGeneratedCode = true
disable(
"CanIgnoreReturnValueSuggester",
"SameNameButDifferent", // Until errorprone recognizes Lombok
"MultiVariableDeclaration", // Until errorprone recognizes Lombok
"UnnecessaryDefaultInEnumSwitch", // FINERACT-1911
"AssertEqualsArgumentOrderChecker",
"RemoveUnusedImports" // For generated code
)
error(
"DefaultCharset",
"StringSplitter",
"MutablePublicArray",
"EqualsGetClass",
"FutureReturnValueIgnored"
)
}
}
apply plugin: 'org.nosphere.apache.rat' apply plugin: 'org.nosphere.apache.rat'
apply plugin: 'project-report'
apply plugin: 'com.github.jk1.dependency-license-report' apply plugin: 'com.github.jk1.dependency-license-report'
// Configuration for the sonarqube plugin is now in GitHub Actions // Configuration for the sonarqube plugin is now in GitHub Actions
@@ -173,7 +200,7 @@ allprojects {
groovyGradle { groovyGradle {
target '*.gradle', '**/*.gradle' target '*.gradle', '**/*.gradle'
targetExclude '**/build/**' targetExclude '**/build/**', '/**/build.gradle' // TODO: temporary disabled on build.gradle until someone fix this issue: https://github.com/diffplug/spotless/issues/1807
greclipse() greclipse()
indentWithSpaces(4) indentWithSpaces(4)
endWithNewline() endWithNewline()
@@ -222,7 +249,6 @@ allprojects {
'**/licenses/**', '**/licenses/**',
'**/*.md', '**/*.md',
'**/*.adoc', '**/*.adoc',
'**/.asciidoctorconfig',
'**/*.puml', '**/*.puml',
'**/*.github/**', '**/*.github/**',
'**/MANIFEST.MF', '**/MANIFEST.MF',
@@ -299,7 +325,9 @@ allprojects {
'**/images/diag-*.svg', '**/images/diag-*.svg',
'**/*.avsc', '**/*.avsc',
"**/generated/**/*MapperImpl.java", "**/generated/**/*MapperImpl.java",
'**/META-INF/fineract-test.config' '**/META-INF/fineract-test.config',
// Apache-specific GitHub metadata settings file
'/.asf.yaml'
] ]
} }
} }
@@ -325,10 +353,49 @@ configure(project.fineractJavaProjects) {
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'idea' apply plugin: 'idea'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
withSourcesJar()
withJavadocJar()
}
tasks.withType(ProcessResources) {
destinationDir = layout.buildDirectory.dir('classes/java/main').get().asFile
}
// Add performance optimizations
configurations.all {
resolutionStrategy {
cacheChangingModulesFor 0, 'seconds'
cacheDynamicVersionsFor 0, 'seconds'
}
}
tasks.withType(JavaCompile).configureEach {
options.incremental = true
options.fork = true
outputs.cacheIf { true }
options.compilerArgs << '-parameters'
options.encoding = 'UTF-8'
options.compilerArgs << '-Xlint:unchecked'
options.compilerArgs << '-Xlint:deprecation'
if (project.hasProperty('warnings') && project.warnings.contains('fail')) {
options.compilerArgs << '-Werror'
}
if (project.hasProperty('warnings') && project.warnings.contains('none')) {
options.compilerArgs << '-nowarn'
}
if (project.plugins.hasPlugin('org.springframework.boot')) {
options.generatedSourceOutputDirectory = file("$buildDir/generated/sources/annotationProcessor/java/main")
}
options.generatedSourceOutputDirectory = file("$buildDir/generated/sources/annotationProcessor/java/main")
}
apply plugin: 'eclipse' apply plugin: 'eclipse'
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'net.ltgt.errorprone'
apply plugin: 'com.github.spotbugs' apply plugin: 'com.github.spotbugs'
apply plugin: 'com.github.andygoossens.modernizer' apply plugin: 'com.github.andygoossens.modernizer'
apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle" apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle"
@@ -336,9 +403,9 @@ configure(project.fineractJavaProjects) {
group = 'org.apache.fineract' group = 'org.apache.fineract'
/* define the valid syntax level for source files */ /* define the valid syntax level for source files */
sourceCompatibility = JavaVersion.VERSION_17 // sourceCompatibility = JavaVersion.VERSION_17
/* define binary compatibility version */ // /* define binary compatibility version */
targetCompatibility = JavaVersion.VERSION_17 // targetCompatibility = JavaVersion.VERSION_17
/* http://stackoverflow.com/questions/19653311/jpa-repository-works-in-idea-and-production-but-not-in-gradle */ /* http://stackoverflow.com/questions/19653311/jpa-repository-works-in-idea-and-production-but-not-in-gradle */
sourceSets.main.output.resourcesDir = sourceSets.main.java.classesDirectory sourceSets.main.output.resourcesDir = sourceSets.main.java.classesDirectory
@@ -354,7 +421,6 @@ configure(project.fineractJavaProjects) {
} }
configurations { configurations {
implementation.setCanBeResolved(true)
api.setCanBeResolved(true) api.setCanBeResolved(true)
} }
tasks.withType(Copy) { tasks.withType(Copy) {
@@ -362,10 +428,8 @@ configure(project.fineractJavaProjects) {
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.compilerArgs += [ options.compilerArgs += [
"-Xlint:unchecked",
"-Xlint:cast", "-Xlint:cast",
"-Xlint:auxiliaryclass", "-Xlint:auxiliaryclass",
"-Xlint:deprecation",
"-Xlint:dep-ann", "-Xlint:dep-ann",
"-Xlint:divzero", "-Xlint:divzero",
"-Xlint:empty", "-Xlint:empty",
@@ -399,6 +463,10 @@ configure(project.fineractJavaProjects) {
options.deprecation = true options.deprecation = true
} }
check {
dependsOn(rat, licenseMain, licenseTest)
}
dependencies { dependencies {
spotbugsPlugins 'jp.skypencil.findbugs.slf4j:bug-pattern:1.5.0@jar' spotbugsPlugins 'jp.skypencil.findbugs.slf4j:bug-pattern:1.5.0@jar'
} }
@@ -455,7 +523,7 @@ configure(project.fineractJavaProjects) {
// Configuration for the Checkstyle plugin // Configuration for the Checkstyle plugin
// https://docs.gradle.org/current/userguide/checkstyle_plugin.html // https://docs.gradle.org/current/userguide/checkstyle_plugin.html
dependencies { dependencies {
checkstyle 'com.puppycrawl.tools:checkstyle:10.14.2' checkstyle 'com.puppycrawl.tools:checkstyle:10.20.1'
checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.44.1' checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.44.1'
} }
@@ -470,21 +538,22 @@ configure(project.fineractJavaProjects) {
reports { reports {
html.required = true html.required = true
xml.required = true xml.required = true
html.destination file("${buildDir}/code-coverage") html.outputLocation = layout.buildDirectory.dir('code-coverage')
} }
} }
// Configuration for the errorprone plugin // Configuration for the errorprone plugin
// https://github.com/tbroyer/gradle-errorprone-plugin // https://github.com/tbroyer/gradle-errorprone-plugin
dependencies { dependencies {
errorprone "com.google.errorprone:error_prone_core:2.26.1" errorprone "com.google.errorprone:error_prone_core:2.35.1"
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile).configureEach {
options.errorprone { options.errorprone {
enabled = project.gradle.startParameter.taskNames.contains('build') || project.gradle.startParameter.taskNames.contains('check') enabled = project.gradle.startParameter.taskNames.contains('build') || project.gradle.startParameter.taskNames.contains('check')
disableWarningsInGeneratedCode = true if (project.path == ':fineract-client') {
excludedPaths = ".*/build/.*" excludedPaths = '.*/build/generated/java/src/main/java/.*'
}
disable( disable(
// TODO Remove disabled checks from this list, by fixing remaining usages // TODO Remove disabled checks from this list, by fixing remaining usages
"UnusedVariable", "UnusedVariable",
@@ -608,9 +677,10 @@ configure(project.fineractJavaProjects) {
'org.bouncycastle:bcpkix-jdk15to18', 'org.bouncycastle:bcpkix-jdk15to18',
'org.bouncycastle:bcprov-jdk15to18', 'org.bouncycastle:bcprov-jdk15to18',
'org.awaitility:awaitility', 'org.awaitility:awaitility',
'com.google.truth:truth',
'com.google.truth.extensions:truth-java8-extension'
) )
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
} }
test { test {
@@ -667,6 +737,19 @@ configure(project.fineractJavaProjects) {
'java/util/Optional.get:()Ljava/lang/Object;' // Disable forcing the usage of Optional.orElseThrow(java.util.function.Supplier<? extends X>) 'java/util/Optional.get:()Ljava/lang/Object;' // Disable forcing the usage of Optional.orElseThrow(java.util.function.Supplier<? extends X>)
] ]
} }
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
// Disable strict checking to prevent build failures on invalid javadoc
options.addBooleanOption('html5', true)
// Add this if you're using Java 17 records or other modern features
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
options.addBooleanOption('html5', true)
}
// Ignore any errors during javadoc generation
failOnError = false
}
} }
configure(project.fineractCustomProjects) { configure(project.fineractCustomProjects) {
@@ -708,14 +791,12 @@ configure(project.fineractCustomProjects) {
'org.bouncycastle:bcpkix-jdk15to18', 'org.bouncycastle:bcpkix-jdk15to18',
'org.bouncycastle:bcprov-jdk15to18', 'org.bouncycastle:bcprov-jdk15to18',
'org.awaitility:awaitility', 'org.awaitility:awaitility',
'com.google.truth:truth',
'com.google.truth.extensions:truth-java8-extension',
'io.github.classgraph:classgraph', 'io.github.classgraph:classgraph',
'io.cucumber:cucumber-core', 'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java:7.16.1', 'io.cucumber:cucumber-java:7.20.1',
'io.cucumber:cucumber-java8:7.16.1', 'io.cucumber:cucumber-java8:7.20.1',
'io.cucumber:cucumber-junit-platform-engine:7.16.1', 'io.cucumber:cucumber-junit-platform-engine:7.20.1',
'io.cucumber:cucumber-spring:7.16.1', 'io.cucumber:cucumber-spring:7.20.1',
) )
testCompileOnly('org.projectlombok:lombok') testCompileOnly('org.projectlombok:lombok')
+1 -1
View File
@@ -20,7 +20,7 @@
import static org.slf4j.LoggerFactory.* import static org.slf4j.LoggerFactory.*
plugins { plugins {
id 'io.spring.dependency-management' version '1.1.4' id 'io.spring.dependency-management' version '1.1.6'
id 'groovy' id 'groovy'
id 'java-gradle-plugin' id 'java-gradle-plugin'
id 'groovy-gradle-plugin' id 'groovy-gradle-plugin'
@@ -23,19 +23,19 @@ apply plugin: 'io.spring.dependency-management'
dependencyManagement { dependencyManagement {
imports { imports {
mavenBom 'com.squareup.okhttp3:okhttp-bom:4.12.0' mavenBom 'com.squareup.okhttp3:okhttp-bom:4.12.0'
mavenBom 'org.slf4j:slf4j-bom:2.0.12' mavenBom 'org.slf4j:slf4j-bom:2.0.17'
mavenBom 'io.micrometer:micrometer-bom:1.12.2' mavenBom 'io.micrometer:micrometer-bom:1.13.6'
mavenBom 'org.springframework:spring-framework-bom:6.1.3' mavenBom 'org.springframework:spring-framework-bom:6.2.5'
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.2.6' mavenBom 'org.springframework.boot:spring-boot-dependencies:3.4.4'
mavenBom 'io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0' mavenBom 'io.awspring.cloud:spring-cloud-aws-dependencies:3.2.1'
mavenBom 'io.opentelemetry:opentelemetry-bom:1.34.1' mavenBom 'io.opentelemetry:opentelemetry-bom:1.44.1'
mavenBom 'org.jetbrains.kotlin:kotlin-bom:1.9.24' mavenBom 'org.jetbrains.kotlin:kotlin-bom:2.0.21'
mavenBom 'org.junit:junit-bom:5.10.2' mavenBom 'org.junit:junit-bom:5.11.3'
mavenBom 'com.fasterxml.jackson:jackson-bom:2.16.1' mavenBom 'com.fasterxml.jackson:jackson-bom:2.18.3'
mavenBom 'io.cucumber:cucumber-bom:7.15.0' mavenBom 'io.cucumber:cucumber-bom:7.20.1'
mavenBom 'io.netty:netty-bom:4.1.110.Final' mavenBom 'io.netty:netty-bom:4.1.119.Final'
mavenBom 'org.mockito:mockito-bom:5.10.0' mavenBom 'org.mockito:mockito-bom:5.14.2'
mavenBom 'software.amazon.awssdk:bom:2.23.18' mavenBom 'software.amazon.awssdk:bom:2.29.9'
mavenBom 'io.github.resilience4j:resilience4j-bom:2.2.0' mavenBom 'io.github.resilience4j:resilience4j-bom:2.2.0'
} }
@@ -44,40 +44,38 @@ dependencyManagement {
// We do not use :+ to get the latest available version available on Maven Central, as that could suddenly break things. // We do not use :+ to get the latest available version available on Maven Central, as that could suddenly break things.
// We use the Renovate Bot to automatically propose Pull Requests (PRs) when upgrades for all of these versions are available. // We use the Renovate Bot to automatically propose Pull Requests (PRs) when upgrades for all of these versions are available.
dependency 'ch.qos.logback:logback-core:1.5.3' dependency 'ch.qos.logback:logback-core:1.5.17'
dependency 'ch.qos.logback:logback-classic:1.5.3' dependency 'ch.qos.logback:logback-classic:1.5.17'
dependency 'ch.qos.logback.contrib:logback-json-classic:0.1.5' dependency 'ch.qos.logback.contrib:logback-json-classic:0.1.5'
dependency 'ch.qos.logback.contrib:logback-jackson:0.1.5' dependency 'ch.qos.logback.contrib:logback-jackson:0.1.5'
dependency 'org.codehaus.janino:janino:3.1.11' dependency 'org.codehaus.janino:janino:3.1.12'
dependency 'org.eclipse.persistence:org.eclipse.persistence.jpa:4.0.0' dependency 'org.eclipse.persistence:org.eclipse.persistence.jpa:4.0.2'
dependency 'com.google.guava:guava:32.0.0-jre' dependency 'com.google.guava:guava:32.0.0-jre'
dependency 'com.google.code.gson:gson:2.10.1' dependency 'com.google.code.gson:gson:2.11.0'
dependency 'com.google.truth:truth:1.4.0' dependency 'com.google.googlejavaformat:google-java-format:1.24.0'
dependency 'com.google.truth.extensions:truth-java8-extension:1.4.0'
dependency 'com.google.googlejavaformat:google-java-format:1.19.2'
dependency 'org.apache.commons:commons-collections4:4.4' dependency 'org.apache.commons:commons-collections4:4.4'
dependency ('software.amazon.msk:aws-msk-iam-auth:2.0.3') { dependency ('software.amazon.msk:aws-msk-iam-auth:2.2.0') {
exclude 'commons-logging:commons-logging:' exclude 'commons-logging:commons-logging:'
} }
dependency ('org.apache.commons:commons-email:1.6.0') { dependency ('org.apache.commons:commons-email:1.6.0') {
exclude 'com.sun.mail:javax.mail' exclude 'com.sun.mail:javax.mail'
exclude 'javax.activation:activation' exclude 'javax.activation:activation'
} }
dependency 'commons-io:commons-io:2.15.1' dependency 'commons-io:commons-io:2.17.0'
dependency 'com.github.librepdf:openpdf:2.0.0' dependency 'com.github.librepdf:openpdf:2.0.3'
dependency ('org.mnode.ical4j:ical4j:3.2.14') { dependency ('org.mnode.ical4j:ical4j:3.2.19') {
exclude 'com.sun.mail:javax.mail' exclude 'com.sun.mail:javax.mail'
exclude 'org.codehaus.groovy:groovy' exclude 'org.codehaus.groovy:groovy'
} }
dependency 'org.apache.commons:commons-csv:1.10.0' dependency 'org.apache.commons:commons-csv:1.12.0'
dependency 'org.quartz-scheduler:quartz:2.3.2' dependency 'org.quartz-scheduler:quartz:2.5.0'
dependency 'org.ehcache:ehcache:3.10.8' dependency 'org.ehcache:ehcache:3.10.8'
dependency 'com.github.spullara.mustache.java:compiler:0.9.11' dependency 'com.github.spullara.mustache.java:compiler:0.9.14'
dependency 'com.jayway.jsonpath:json-path:2.9.0' dependency 'com.jayway.jsonpath:json-path:2.9.0'
dependency 'org.apache.tika:tika-core:2.9.1' dependency 'org.apache.tika:tika-core:2.9.3'
dependency ('org.apache.tika:tika-parser-microsoft-module:2.9.2') { dependency ('org.apache.tika:tika-parser-microsoft-module:2.9.3') {
exclude 'org.bouncycastle:bcprov-jdk15on' exclude 'org.bouncycastle:bcprov-jdk15on'
exclude 'org.bouncycastle:bcmail-jdk15on' exclude 'org.bouncycastle:bcmail-jdk15on'
exclude 'org.bouncycastle:bcprov-jdk18on' exclude 'org.bouncycastle:bcprov-jdk18on'
@@ -94,7 +92,7 @@ dependencyManagement {
exclude 'org.apache.commons:commons-compress' exclude 'org.apache.commons:commons-compress'
exclude 'xml-apis:xml-apis' exclude 'xml-apis:xml-apis'
} }
dependency ('org.apache.tika:tika-parser-miscoffice-module:2.9.2') { dependency ('org.apache.tika:tika-parser-miscoffice-module:2.9.3') {
exclude 'org.bouncycastle:bcprov-jdk15on' exclude 'org.bouncycastle:bcprov-jdk15on'
exclude 'org.bouncycastle:bcmail-jdk15on' exclude 'org.bouncycastle:bcmail-jdk15on'
exclude 'org.bouncycastle:bcprov-jdk18on' exclude 'org.bouncycastle:bcprov-jdk18on'
@@ -117,64 +115,63 @@ dependencyManagement {
dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4' dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4'
dependency 'jakarta.jms:jakarta.jms-api:3.1.0' dependency 'jakarta.jms:jakarta.jms-api:3.1.0'
dependency 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' dependency 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0'
dependency 'org.glassfish.jersey.media:jersey-media-multipart:3.1.5' dependency 'org.glassfish.jersey.media:jersey-media-multipart:3.1.10'
dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version
dependency 'org.apache.bval:org.apache.bval.bundle:3.0.0' dependency 'org.apache.bval:org.apache.bval.bundle:3.0.2'
dependency 'joda-time:joda-time:2.12.7' dependency 'joda-time:joda-time:2.13.1'
dependency 'io.github.classgraph:classgraph:4.8.168' dependency 'io.github.classgraph:classgraph:4.8.179'
dependency 'org.awaitility:awaitility:4.2.1' dependency 'org.awaitility:awaitility:4.2.2'
// TODO: upgrade to 4.8.3 // TODO: upgrade to 4.8.3
dependency 'com.github.spotbugs:spotbugs-annotations:4.8.3' dependency 'com.github.spotbugs:spotbugs-annotations:4.8.6'
dependency 'javax.cache:cache-api:1.1.1' dependency 'javax.cache:cache-api:1.1.1'
dependency 'org.mock-server:mockserver-junit-jupiter:5.15.0' dependency 'org.mock-server:mockserver-junit-jupiter:5.15.0'
dependency 'org.webjars:webjars-locator-core:0.55' dependency 'org.webjars:webjars-locator-core:0.59'
dependency 'com.icegreen:greenmail-junit5:2.0.1' dependency 'com.icegreen:greenmail-junit5:2.0.1'
dependency 'com.intuit.karate:karate-junit5:1.4.1'
// fineract client dependencies // fineract client dependencies
dependency "com.squareup.retrofit2:retrofit:2.9.0" dependency "com.squareup.retrofit2:retrofit:2.11.0"
dependency "com.squareup.retrofit2:retrofit-mock:2.9.0" dependency "com.squareup.retrofit2:retrofit-mock:2.11.0"
dependency "com.squareup.retrofit2:adapter-java8:2.9.0" dependency "com.squareup.retrofit2:adapter-java8:2.11.0"
dependency "com.squareup.retrofit2:adapter-rxjava2:2.9.0" dependency "com.squareup.retrofit2:adapter-rxjava2:2.11.0"
dependency "com.squareup.retrofit2:adapter-rxjava3:2.9.0" dependency "com.squareup.retrofit2:adapter-rxjava3:2.11.0"
dependency "com.squareup.retrofit2:adapter-guava:2.9.0" dependency "com.squareup.retrofit2:adapter-guava:2.11.0"
dependency "com.squareup.retrofit2:converter-wire:2.9.0" dependency "com.squareup.retrofit2:converter-wire:2.11.0"
dependency "com.squareup.retrofit2:converter-jackson:2.9.0" dependency "com.squareup.retrofit2:converter-jackson:2.11.0"
dependency "com.squareup.retrofit2:converter-simplexml:2.9.0" dependency "com.squareup.retrofit2:converter-simplexml:2.11.0"
dependency "com.squareup.retrofit2:converter-jaxb:2.9.0" dependency "com.squareup.retrofit2:converter-jaxb:2.11.0"
dependency "com.squareup.retrofit2:converter-java8:2.9.0" dependency "com.squareup.retrofit2:converter-java8:2.11.0"
dependency "com.squareup.retrofit2:converter-scalars:2.9.0" dependency "com.squareup.retrofit2:converter-scalars:2.11.0"
dependency "com.squareup.retrofit2:converter-gson:2.9.0" dependency "com.squareup.retrofit2:converter-gson:2.11.0"
dependency "com.squareup.retrofit2:converter-protobuf:2.9.0" dependency "com.squareup.retrofit2:converter-protobuf:2.11.0"
dependency 'io.reactivex.rxjava2:rxjava:2.2.21' dependency 'io.reactivex.rxjava2:rxjava:2.2.21'
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.1" dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.1"
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.1" dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.1"
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.httpclient4:1.0.1" dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.httpclient4:1.0.1"
dependency "io.gsonfire:gson-fire:1.9.0" dependency "io.gsonfire:gson-fire:1.9.0"
dependency "com.google.code.findbugs:jsr305:3.0.2" dependency "com.google.code.findbugs:jsr305:3.0.2"
dependency "commons-codec:commons-codec:1.16.0" dependency "commons-codec:commons-codec:1.17.1"
dependency "org.projectlombok:lombok:1.18.32" dependency "org.projectlombok:lombok:1.18.36"
dependency 'org.bouncycastle:bcpkix-jdk15to18:1.77' dependency 'org.bouncycastle:bcpkix-jdk15to18:1.79'
dependency 'org.bouncycastle:bcprov-jdk15to18:1.77' dependency 'org.bouncycastle:bcprov-jdk15to18:1.79'
dependency 'org.bouncycastle:bcprov-jdk15on:1.70' dependency 'org.bouncycastle:bcprov-jdk15on:1.70'
dependency 'org.bouncycastle:bcpg-jdk15on:1.70' dependency 'org.bouncycastle:bcpg-jdk15on:1.70'
dependency 'org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r' dependency 'org.eclipse.jgit:org.eclipse.jgit:6.10.0.202406032230-r'
dependency 'org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r' dependency 'org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.0.0.202409031743-r'
dependency 'org.tmatesoft.svnkit:svnkit:1.10.11' dependency 'org.tmatesoft.svnkit:svnkit:1.10.11'
dependency 'com.vdurmont:semver4j:3.1.0' dependency 'com.vdurmont:semver4j:3.1.0'
dependency 'org.beryx:text-io:3.4.1' dependency 'org.beryx:text-io:3.4.1'
dependency ('org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0') { dependency ('org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0') {
exclude 'io.swagger.core.v3:swagger-core' exclude 'io.swagger.core.v3:swagger-core'
} }
dependency 'com.google.cloud.sql:mysql-socket-factory-connector-j-8:1.15.2' dependency 'com.google.cloud.sql:mysql-socket-factory-connector-j-8:1.23.1'
dependency ('org.apache.activemq:activemq-client:6.0.1') { dependency ('org.apache.activemq:activemq-client:6.1.6') {
exclude 'javax.annotation:javax.annotation-api' exclude 'javax.annotation:javax.annotation-api'
} }
@@ -186,20 +183,20 @@ dependencyManagement {
exclude 'jakarta.activation:jakarta.activation-api' exclude 'jakarta.activation:jakarta.activation-api'
} }
dependency 'jakarta.annotation:jakarta.annotation-api:2.1.1' dependency 'jakarta.annotation:jakarta.annotation-api:3.0.0'
dependency 'jakarta.activation:jakarta.activation-api:2.1.2' dependency 'jakarta.activation:jakarta.activation-api:2.1.3'
dependency ('com.sun.mail:jakarta.mail:2.0.1') { dependency ('com.sun.mail:jakarta.mail:2.0.1') {
// Spring needs this version // Spring needs this version
exclude 'com.sun.activation:jakarta.activation' exclude 'com.sun.activation:jakarta.activation'
} }
dependency ('jakarta.xml.bind:jakarta.xml.bind-api:4.0.1') { dependency ('jakarta.xml.bind:jakarta.xml.bind-api:4.0.2') {
exclude 'jakarta.activation:jakarta.activation-api' exclude 'jakarta.activation:jakarta.activation-api'
} }
dependency ('org.liquibase:liquibase-core:4.25.1') { dependency ('org.liquibase:liquibase-core:4.31.1') {
exclude 'javax.xml.bind:jaxb-api' exclude 'javax.xml.bind:jaxb-api'
} }
dependency 'org.liquibase.ext:liquibase-postgresql:4.25.1.1' dependency 'org.liquibase.ext:liquibase-postgresql:4.31.1'
dependency ('org.dom4j:dom4j:2.1.4') { dependency ('org.dom4j:dom4j:2.1.4') {
exclude 'relaxngDatatype:relaxngDatatype' // already in com.sun.xml.bind:jaxb-osgi:2.3.0.1 exclude 'relaxngDatatype:relaxngDatatype' // already in com.sun.xml.bind:jaxb-osgi:2.3.0.1
@@ -208,36 +205,37 @@ dependencyManagement {
exclude 'pull-parser:pull-parser' exclude 'pull-parser:pull-parser'
} }
dependency 'org.owasp.esapi:esapi:2.5.3.1' dependency 'org.owasp.esapi:esapi:2.6.0.0'
dependency 'org.awaitility:awaitility:4.2.1' dependency 'org.awaitility:awaitility:4.2.2'
dependencySet(group: 'org.apache.poi', version: '5.2.5') { dependencySet(group: 'org.apache.poi', version: '5.3.0') {
entry 'poi' entry 'poi'
entry 'poi-ooxml' entry 'poi-ooxml'
entry 'poi-ooxml-schemas' entry 'poi-ooxml-schemas'
} }
dependencySet(group: 'io.rest-assured', version: '5.4.0') { dependencySet(group: 'io.rest-assured', version: '5.5.1') {
entry 'rest-assured' entry 'rest-assured'
entry 'json-path' entry 'json-path'
entry 'xml-path' entry 'xml-path'
} }
dependency 'org.apache.groovy:groovy-xml:4.0.18' dependency 'org.apache.groovy:groovy-xml:4.0.26'
dependency 'org.apache.groovy:groovy-json:4.0.18' dependency 'org.apache.groovy:groovy-json:4.0.26'
dependency 'org.mapstruct:mapstruct:1.5.5.Final' dependency 'org.mapstruct:mapstruct:1.6.3'
dependency 'org.mapstruct:mapstruct-processor:1.5.5.Final' dependency 'org.mapstruct:mapstruct-processor:1.6.3'
dependency "org.apache.avro:avro:1.11.3" dependency "org.apache.avro:avro:1.12.0"
dependency ('org.mariadb.jdbc:mariadb-java-client:3.3.2') { dependency ('org.mariadb.jdbc:mariadb-java-client:3.5.2') {
exclude 'org.slf4j:jcl-over-slf4j' exclude 'org.slf4j:jcl-over-slf4j'
exclude 'org.slf4j:slf4j-api' exclude 'org.slf4j:slf4j-api'
} }
dependency 'org.postgresql:postgresql:42.7.3' //v42.7.5: performance issue: https://github.com/pgjdbc/pgjdbc/issues/3511#issuecomment-2637277977
dependency 'org.postgresql:postgresql:42.7.4'
dependency 'org.assertj:assertj-core:3.25.3' dependency 'org.assertj:assertj-core:3.26.3'
dependency 'org.apache.commons:commons-math3:3.6.1' dependency 'org.apache.commons:commons-math3:3.6.1'
@@ -245,9 +243,9 @@ dependencyManagement {
dependency 'com.github.tomakehurst:wiremock-standalone:3.0.1' dependency 'com.github.tomakehurst:wiremock-standalone:3.0.1'
dependency 'io.cucumber:cucumber-java:7.16.1' dependency 'io.cucumber:cucumber-java:7.20.1'
dependency 'io.cucumber:cucumber-java8:7.16.1' dependency 'io.cucumber:cucumber-java8:7.20.1'
dependency 'io.cucumber:cucumber-junit-platform-engine:7.16.1' dependency 'io.cucumber:cucumber-junit-platform-engine:7.20.1'
dependency 'io.cucumber:cucumber-spring:7.16.1' dependency 'io.cucumber:cucumber-spring:7.20.1'
} }
} }
@@ -334,8 +334,6 @@ class FineractPlugin implements Plugin<Project> {
gpgService.sign(step.gpg) gpgService.sign(step.gpg)
step.gpg.files.findAll { step.gpg.files.findAll {
gpgService.md5(step.gpg)
gpgService.sha512(step.gpg) gpgService.sha512(step.gpg)
} }
} }
@@ -440,29 +438,10 @@ class FineractPlugin implements Plugin<Project> {
project.tasks.register("fineractReleaseStep12") { project.tasks.register("fineractReleaseStep12") {
doFirst { doFirst {
FineractPluginExtension.FineractPluginStep step = step(extension, "step12") FineractPluginExtension.FineractPluginStep step = step(extension, "step12")
String version = project.properties?['fineract.release.version']
if(!version) {
TextIO textIO = TextIoFactory.getTextIO()
version = textIO.newStringInputReader()
.withPattern("\\d+.\\d+.\\d+")
.read("Release Version");
}
// TODO: input validation, see FINERACT-1610 // TODO: input validation, see FINERACT-1610
subversionService.checkout(step.subversion) // TODO: implement this, see FINERACT-1817
printInstructions(project, "step12")
def directory = step.subversion.directory?:System.getProperty("java.io.tmpdir") + "/fineract-dist-release"
def source = new File("fineract-war/build/distributions")
def target = new File("${directory}/${version}")
FileUtils.copyDirectory(source, target, true)
subversionService.commit(step.subversion)
} }
} }
@@ -116,15 +116,6 @@ class GpgService {
return generator.generate(); return generator.generate();
} }
void md5(FineractPluginExtension.FineractPluginGpgParams params) {
params.files.findAll {
def result = calc(new FileInputStream(it), MessageDigest.getInstance("MD5", BouncyCastleProvider.PROVIDER_NAME))
def file = new File("${it}.md5")
file.write result
}
}
void sha512(FineractPluginExtension.FineractPluginGpgParams params) { void sha512(FineractPluginExtension.FineractPluginGpgParams params) {
params.files.findAll { params.files.findAll {
def result = calc(new FileInputStream(it), MessageDigest.getInstance("SHA-512", BouncyCastleProvider.PROVIDER_NAME)) def result = calc(new FileInputStream(it), MessageDigest.getInstance("SHA-512", BouncyCastleProvider.PROVIDER_NAME))
@@ -20,15 +20,15 @@
--> -->
Hello everyone, Hello everyone,
... we have created Apache Fineract ${project['fineract.release.version']} release, with the artifacts below up for a vote. We are proud to present Apache Fineract ${project['fineract.release.version']}, with the artifacts below up for a vote. Releases are important for a number of reasons: They put a stamp of approval on a set of code changes and they build momentum for future improvements.
It fixes the following issues: https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract Release notes and ChangeLog: https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract
Source & Binary files : https://dist.apache.org/repos/dist/dev/fineract/${project['fineract.release.version']}/ Source and binary artifacts: https://dist.apache.org/repos/dist/dev/fineract/${project['fineract.release.version']}/
Tag to be voted on (rc#): https://gitbox.apache.org/repos/asf?p=fineract.git;a=commit;h=refs/heads/${project['fineract.release.version']} Tagged as ${project['fineract.release.version']}
Fineract's KEYS containing the PGP key we used to sign the release: https://dist.apache.org/repos/dist/dev/fineract/KEYS Committer PGP keys, including the release signing key: https://dist.apache.org/repos/dist/dev/fineract/KEYS
Note that this release contains source and binary artifacts. Note that this release contains source and binary artifacts.
@@ -38,6 +38,16 @@ This vote will be open for 72 hours:
[ ] +0 no opinion [ ] +0 no opinion
[ ] -1 disapprove (and reason why) [ ] -1 disapprove (and reason why)
Please indicate if you are a binding vote (member of the PMC).
Please also indicate with "Tested: YES/NO/PARTIAL" if you have locally built and/or tested these artifacts and/or a clone of the code checked out to the release commit, following the form:
Tested: YES ... Verified integrity and signatures of release artifacts locally, built from source, ran jar/war: Did everything mentioned in the current release candidate verification guidance ( https://lists.apache.org/thread/hym94pdy3nk9gjspkz4qonv2v15n5dpo ). If you did more than that, please specify.
Tested: NO ... No testing performed on release candidate, e.g. relying on testing performed by other contributors and/or output of GitHub Actions, while exercising my right to vote.
Tested: PARTIAL ... Please specify.
Cheers, Cheers,
${project['fineract.config.name']} ${project['fineract.config.name']}
@@ -0,0 +1,29 @@
<#--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
[INSTRUCTIONS:START]
Move the release candidate from the dev area to the release area using a Subversion server-side copy:
svn mv https://dist.apache.org/repos/dist/dev/fineract/${project['fineract.release.version']} https://dist.apache.org/repos/dist/release /fineract/
[INSTRUCTIONS:END]
@@ -27,9 +27,9 @@ Following are the typical things we need to verify before voting on a release ca
Make sure release artifacts are hosted at https://dist.apache.org/repos/dist/dev/fineract Make sure release artifacts are hosted at https://dist.apache.org/repos/dist/dev/fineract
* Release candidates should be in format apache-fineract-${project['fineract.release.version']}-binary.tar.gz * Release candidates should be in format apache-fineract-${project['fineract.release.version']}-binary.tar.gz
* Verify signatures and hashes. You may have to import the public key of the release manager to verify the signatures. (`gpg --recv-key <key id>`) * Verify signatures and hashes. You may have to import the public key of the release manager to verify the signatures. (gpg --import KEYS or gpg --recv-key <key id>)
* Git tag matches the released bits (diff -rf) * Git tag matches the released bits (diff -rf)
* Can compile successfully from source * Can compile docs and code successfully from source
* Verify DISCLAIMER, NOTICE and LICENSE (year etc) * Verify DISCLAIMER, NOTICE and LICENSE (year etc)
* All files have correct headers (Rat check should be clean - gradlew rat) * All files have correct headers (Rat check should be clean - gradlew rat)
* No jar files in the source artifacts * No jar files in the source artifacts
+1 -1
View File
@@ -21,7 +21,7 @@ version: "3.8"
services: services:
fineract: fineract:
# user: "${FINERACT_USER}:${FINERACT_GROUP}" # user: "${FINERACT_USER}:${FINERACT_GROUP}"
image: fineract-custom:latest image: ${IMAGE_NAME:-fineract-custom:latest}
volumes: volumes:
- ${PWD}/config/docker/logback/logback-override.xml:/app/logback-override.xml - ${PWD}/config/docker/logback/logback-override.xml:/app/logback-override.xml
- ${PWD}/config/docker/aws/etc/credentials:/etc/aws/credentials:ro - ${PWD}/config/docker/aws/etc/credentials:/etc/aws/credentials:ro
+1 -1
View File
@@ -21,7 +21,7 @@ version: "3.8"
services: services:
fineract: fineract:
# user: "${FINERACT_USER}:${FINERACT_GROUP}" # user: "${FINERACT_USER}:${FINERACT_GROUP}"
image: fineract:latest image: ${IMAGE_NAME:-apache/fineract:latest}
volumes: volumes:
- ${PWD}/config/docker/logback/logback-override.xml:/app/logback-override.xml - ${PWD}/config/docker/logback/logback-override.xml:/app/logback-override.xml
- ${PWD}/config/docker/aws/etc/credentials:/etc/aws/credentials:ro - ${PWD}/config/docker/aws/etc/credentials:/etc/aws/credentials:ro
+1 -1
View File
@@ -18,7 +18,7 @@ version: "3.8"
services: services:
mariadb: mariadb:
container_name: mariadb container_name: mariadb
image: mariadb:11.2 image: mariadb:11.4
volumes: volumes:
- ${PWD}/config/docker/mysql/conf.d/server_collation.cnf:/etc/mysql/conf.d/server_collation.cnf:ro - ${PWD}/config/docker/mysql/conf.d/server_collation.cnf:/etc/mysql/conf.d/server_collation.cnf:ro
- ${PWD}/config/docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:Z,ro - ${PWD}/config/docker/mysql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:Z,ro
+5
View File
@@ -17,5 +17,10 @@
# under the License. # under the License.
# #
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_SPRING_EVENTS_ENABLED=false
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=true FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=true
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_BROKER_URL=tcp://activemq:61616 FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_BROKER_URL=tcp://activemq:61616
FINERACT_EXTERNAL_EVENTS_ENABLED=true
FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_ENABLED=true
FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_TOPIC_NAME=events
FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_BROKER_URL=tcp://activemq:61616
+1 -1
View File
@@ -52,7 +52,7 @@ FINERACT_DEFAULT_TENANTDB_PWD=skdcnwauicn2ucnaecasdsajdnizucawencascdca
FINERACT_DEFAULT_MASTER_PASSWORD=fineract FINERACT_DEFAULT_MASTER_PASSWORD=fineract
FINERACT_MANAGEMENT_ENDPOINT_WEB_EXPOSURE_INCLUDE=health,info,prometheus FINERACT_MANAGEMENT_ENDPOINT_WEB_EXPOSURE_INCLUDE=health,info,prometheus
FINERACT_MANAGEMENT_METRICS_TAGS_APPLICATION=fineract FINERACT_MANAGEMENT_METRICS_TAGS_APPLICATION=fineract
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_SPRING_EVENTS_ENABLED=false FINERACT_REMOTE_JOB_MESSAGE_HANDLER_SPRING_EVENTS_ENABLED=true
FINERACT_INSECURE_HTTP_CLIENT=true FINERACT_INSECURE_HTTP_CLIENT=true
SPRING_PROFILES_ACTIVE=test,diagnostics SPRING_PROFILES_ACTIVE=test,diagnostics
OTEL_SERVICE_NAME=fineract OTEL_SERVICE_NAME=fineract
+1
View File
@@ -17,6 +17,7 @@
# under the License. # under the License.
# #
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_SPRING_EVENTS_ENABLED=false
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=false FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=false
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_ENABLED=true FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_ENABLED=true
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_BOOTSTRAP_SERVERS=b-3-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198,b-2-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198,b-1-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198 FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_BOOTSTRAP_SERVERS=b-3-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198,b-2-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198,b-1-public.democluster1.am2oyx.c4.kafka.eu-central-1.amazonaws.com:9198
+1
View File
@@ -17,6 +17,7 @@
# under the License. # under the License.
# #
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_SPRING_EVENTS_ENABLED=false
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=false FINERACT_REMOTE_JOB_MESSAGE_HANDLER_JMS_ENABLED=false
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_ENABLED=true FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_ENABLED=true
FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_BOOTSTRAP_SERVERS=kafka:9092 FINERACT_REMOTE_JOB_MESSAGE_HANDLER_KAFKA_BOOTSTRAP_SERVERS=kafka:9092
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract external events' description = 'ACME Fineract Event External Event'
group = 'com.acme.fineract.event' group = 'com.acme.fineract.event'
archivesBaseName = 'acme-fineract-event-externalevent' base {
archivesName = 'acme-fineract-event-externalevent'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract Event Starter' description = 'ACME Fineract Event Starter'
group = 'com.acme.fineract.event' group = 'com.acme.fineract.event'
archivesBaseName = 'acme-fineract-event-starter' base {
archivesName = 'acme-fineract-event-starter'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract COB Loan' description = 'ACME Fineract Loan COB'
group = 'com.acme.fineract' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-cob' base {
archivesName = 'acme-fineract-loan-cob'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract Loan Job' description = 'ACME Fineract Loan Job'
group = 'com.acme.fineract' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-job' base {
archivesName = 'acme-fineract-loan-job'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
@@ -18,26 +18,24 @@
*/ */
package com.acme.fineract.loan.job; package com.acme.fineract.loan.job;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step; import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@Configuration @Configuration
@RequiredArgsConstructor
public class AcmeNoopJobConfiguration { public class AcmeNoopJobConfiguration {
@Autowired private final JobRepository jobRepository;
private JobRepository jobRepository; private final PlatformTransactionManager transactionManager;
@Autowired private final AcmeNoopJobTasklet tasklet;
private PlatformTransactionManager transactionManager;
@Autowired
private AcmeNoopJobTasklet tasklet;
@Bean @Bean
protected Step acmeNoopJobStep() { protected Step acmeNoopJobStep() {
@@ -42,4 +42,10 @@
<column name="is_mismatched_job" valueBoolean="true"/> <column name="is_mismatched_job" valueBoolean="true"/>
</insert> </insert>
</changeSet> </changeSet>
<changeSet author="acme" id="2">
<update tableName="job">
<column name="short_name" value="ACM_NOOP"/>
<where>name='Acme Noop Job'</where>
</update>
</changeSet>
</databaseChangeLog> </databaseChangeLog>
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract Loan Transaction Processors' description = 'ACME Fineract Loan Processor'
group = 'com.acme.fineract' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-processor' base {
archivesName = 'acme-fineract-loan-processor'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
@@ -18,6 +18,7 @@
*/ */
package com.acme.fineract.loan.processor; package com.acme.fineract.loan.processor;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -28,6 +29,10 @@ public class AcmeLoanRepaymentScheduleTransactionProcessor extends FineractStyle
public static final String STRATEGY_NAME = "ACME Corp.: standard loan transaction processing strategy"; public static final String STRATEGY_NAME = "ACME Corp.: standard loan transaction processing strategy";
public AcmeLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) {
super(externalIdFactory);
}
@Override @Override
public String getCode() { public String getCode() {
return STRATEGY_CODE; return STRATEGY_CODE;
+4 -2
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract COB Starter' description = 'ACME Fineract Loan Starter'
group = 'com.acme.fineract' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-starter' base {
archivesName = 'acme-fineract-loan-starter'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
+5 -3
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract Note Service' description = 'ACME Fineract Note Service'
group = 'com.acme.fineract.portfolio.note' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-note-service' base {
archivesName = 'acme-fineract-note-service'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
@@ -18,7 +18,7 @@
*/ */
package com.acme.fineract.portfolio.note.service; package com.acme.fineract.portfolio.note.service;
import java.util.Collection; import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.portfolio.note.data.NoteData; import org.apache.fineract.portfolio.note.data.NoteData;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformService; import org.apache.fineract.portfolio.note.service.NoteReadPlatformService;
@@ -42,7 +42,7 @@ public class AcmeNoteReadPlatformService implements NoteReadPlatformService, Ini
} }
@Override @Override
public Collection<NoteData> retrieveNotesByResource(Long resourceId, Integer noteTypeId) { public List<NoteData> retrieveNotesByResource(Long resourceId, Integer noteTypeId) {
return null; return null;
} }
} }
@@ -39,21 +39,21 @@ public class AcmeNoteWritePlatformService implements NoteWritePlatformService, I
@Override @Override
public CommandProcessingResult createNote(JsonCommand command) { public CommandProcessingResult createNote(JsonCommand command) {
return null; throw new UnsupportedOperationException("createNote() is not yet implemented.");
} }
@Override @Override
public CommandProcessingResult updateNote(JsonCommand command) { public CommandProcessingResult updateNote(JsonCommand command) {
return null; throw new UnsupportedOperationException("updateNote() is not yet implemented.");
} }
@Override @Override
public CommandProcessingResult deleteNote(JsonCommand command) { public CommandProcessingResult deleteNote(JsonCommand command) {
return null; throw new UnsupportedOperationException("deleteNote() is not yet implemented.");
} }
@Override @Override
public void createAndPersistClientNote(Client client, JsonCommand command) { public void createAndPersistClientNote(Client client, JsonCommand command) {
log.warn("createAndPersistClientNote() is intentionally left empty and does nothing.");
} }
} }
+5 -3
View File
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
description = 'ACME Corp.: Fineract Note Starter' description = 'ACME Fineract Note Starter'
group = 'com.acme.fineract.portfolio.note' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-note-starter' base {
archivesName = 'acme-fineract-note-starter'
}
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
+1 -1
View File
@@ -20,7 +20,7 @@
version: "3.7" version: "3.7"
services: services:
kafka: kafka:
image: "bitnami/kafka:3.5.1-debian-11-r7" image: "bitnami/kafka:3.8.1-debian-12-r1"
ports: ports:
- "9092:9092" - "9092:9092"
env_file: env_file:
@@ -0,0 +1,45 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
version: "3.8"
services:
activemq:
extends:
file: ./config/docker/compose/activemq.yml
service: activemq
db:
extends:
file: ./config/docker/compose/postgresql.yml
service: postgresql
fineract:
extends:
file: ./config/docker/compose/fineract.yml
service: fineract
ports:
- "8443:8443"
depends_on:
db:
condition: service_healthy
env_file:
- ./config/docker/env/fineract.env
- ./config/docker/env/fineract-common.env
- ./config/docker/env/fineract-postgresql.env
- ./config/docker/env/activemq.env
+1 -1
View File
@@ -20,7 +20,7 @@ version: "3.8"
services: services:
# Frontend service # Frontend service
community-app: community-app:
image: openmf/web-app:latest image: openmf/web-app:master
container_name: mifos-web-app container_name: mifos-web-app
restart: always restart: always
ports: ports:
+1 -1
View File
@@ -31,7 +31,7 @@ compileJava.doLast {
javaexec { javaexec {
description = 'Performs EclipseLink static weaving of entity classes' description = 'Performs EclipseLink static weaving of entity classes'
def target = source def target = source
main 'org.eclipse.persistence.tools.weaving.jpa.StaticWeave' mainClass = 'org.eclipse.persistence.tools.weaving.jpa.StaticWeave'
args '-persistenceinfo', source, source, target args '-persistenceinfo', source, source, target
classpath sourceSets.main.runtimeClasspath classpath sourceSets.main.runtimeClasspath
} }
@@ -32,6 +32,7 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.accrual.request.AccrualAccountRequest;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -54,13 +55,10 @@ public class AccrualAccountingApiResource {
@Operation(summary = "Executes Periodic Accrual Accounting", method = "POST", description = "Mandatory Fields\n" + "\n" + "tillDate\n") @Operation(summary = "Executes Periodic Accrual Accounting", method = "POST", description = "Mandatory Fields\n" + "\n" + "tillDate\n")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccrualAccountingApiResourceSwagger.PostRunaccrualsRequest.class))) @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccrualAccountingApiResourceSwagger.PostRunaccrualsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") }) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
public String executePeriodicAccrualAccounting(@Parameter(hidden = true) final String jsonRequestBody) { public CommandProcessingResult executePeriodicAccrualAccounting(@Parameter(hidden = true) AccrualAccountRequest accrualAccountRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().excuteAccrualAccounting()
final CommandWrapper commandRequest = new CommandWrapperBuilder().excuteAccrualAccounting().withJson(jsonRequestBody).build(); .withJson(apiJsonSerializerService.serialize(accrualAccountRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
} }
@@ -24,9 +24,9 @@ public final class AccrualAccountingConstants {
} }
public static final String accrueTillParamName = "tillDate"; public static final String ACCRUE_TILL_PARAM_NAME = "tillDate";
public static final String localeParamName = "locale"; public static final String LOCALE_PARAM_NAME = "locale";
public static final String dateFormatParamName = "dateFormat"; public static final String DATE_FORMAT_PARAM_NAME = "dateFormat";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME = "periodicaccrual"; public static final String PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME = "periodicaccrual";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE = "execution.failed"; public static final String PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE = "execution.failed";
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.request;
import java.io.Serial;
import java.io.Serializable;
public record AccrualAccountRequest(String locale, String dateFormat, String tillDate) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -18,8 +18,8 @@
*/ */
package org.apache.fineract.accounting.accrual.serialization; package org.apache.fineract.accounting.accrual.serialization;
import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.ACCRUE_TILL_PARAM_NAME;
import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME; import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
@@ -46,8 +46,8 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public final class AccrualAccountingDataValidator { public final class AccrualAccountingDataValidator {
private static final Set<String> LOAN_PERIODIC_REQUEST_DATA_PARAMETERS = new HashSet<>( private static final Set<String> LOAN_PERIODIC_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(ACCRUE_TILL_PARAM_NAME,
Arrays.asList(accrueTillParamName, AccrualAccountingConstants.localeParamName, AccrualAccountingConstants.dateFormatParamName)); AccrualAccountingConstants.LOCALE_PARAM_NAME, AccrualAccountingConstants.DATE_FORMAT_PARAM_NAME));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -63,11 +63,11 @@ public final class AccrualAccountingDataValidator {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME); .resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
final LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(accrueTillParamName, element); final LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(ACCRUE_TILL_PARAM_NAME, element);
baseDataValidator.reset().parameter(accrueTillParamName).value(date).notNull().validateDateBefore(DateUtils.getBusinessLocalDate()); baseDataValidator.reset().parameter(ACCRUE_TILL_PARAM_NAME).value(date).notNull()
.validateDateBefore(DateUtils.getBusinessLocalDate());
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
public void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { public void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
@@ -48,7 +48,7 @@ public enum GLClosureJsonInputParams {
@Override @Override
public String toString() { public String toString() {
return name().toString().replaceAll("_", " "); return name().replace("_", " ");
} }
public String getValue() { public String getValue() {
@@ -39,12 +39,10 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.data.GLClosureData; import org.apache.fineract.accounting.closure.data.GLClosureData;
import org.apache.fineract.accounting.closure.data.request.GLClosureRequest;
import org.apache.fineract.accounting.closure.service.GLClosureReadPlatformService; import org.apache.fineract.accounting.closure.service.GLClosureReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
@@ -66,10 +64,6 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class GLClosuresApiResource { public class GLClosuresApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("id", "officeId", "officeName", "closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId",
"createdByUsername", "lastUpdatedByUserId", "lastUpdatedByUsername"));
private static final String RESOURCE_NAME_FOR_PERMISSION = "GLCLOSURE"; private static final String RESOURCE_NAME_FOR_PERMISSION = "GLCLOSURE";
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
@@ -85,14 +79,10 @@ public class GLClosuresApiResource {
@Operation(summary = "List Accounting closures", description = "Example Requests:\n" + "\n" + "glclosures") @Operation(summary = "List Accounting closures", description = "Example Requests:\n" + "\n" + "glclosures")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GLClosuresApiResourceSwagger.GetGlClosureResponse.class)))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GLClosuresApiResourceSwagger.GetGlClosureResponse.class)))) })
public String retrieveAllClosures(@Context final UriInfo uriInfo, public List<GLClosureData> retrieveAllClosures(@QueryParam("officeId") @Parameter(name = "officeId") final Long officeId) {
@QueryParam("officeId") @Parameter(name = "officeId") final Long officeId) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final List<GLClosureData> glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId); return glClosureReadPlatformService.retrieveAllGLClosures(officeId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glClosureDatas, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@@ -103,19 +93,17 @@ public class GLClosuresApiResource {
+ "/glclosures/1?fields=officeName,closingDate") + "/glclosures/1?fields=officeName,closingDate")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.GetGlClosureResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.GetGlClosureResponse.class))) })
public String retreiveClosure(@PathParam("glClosureId") @Parameter(description = "glClosureId") final Long glClosureId, public GLClosureData retreiveClosure(@PathParam("glClosureId") @Parameter(description = "glClosureId") final Long glClosureId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final GLClosureData glClosureData = glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
if (settings.isTemplate()) { if (settings.isTemplate()) {
glClosureData.setAllowedOffices(this.officeReadPlatformService.retrieveAllOfficesForDropdown()); glClosureData.setAllowedOffices(officeReadPlatformService.retrieveAllOfficesForDropdown());
} }
return glClosureData;
return this.apiJsonSerializerService.serialize(settings, glClosureData, RESPONSE_DATA_PARAMETERS);
} }
// NOTE: proposal slight changes @Aleks // NOTE: proposal slight changes @Aleks
@@ -126,13 +114,11 @@ public class GLClosuresApiResource {
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PostGlClosuresRequest.class, description = "Request Body"))) @RequestBody(required = true, content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PostGlClosuresRequest.class, description = "Request Body")))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PostGlClosuresResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PostGlClosuresResponse.class))) })
public String createGLClosure(@Parameter(hidden = true) final String jsonRequestBody) { public CommandProcessingResult createGLClosure(@Parameter(hidden = true) GLClosureRequest glClosureRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLClosure()
.withJson(apiJsonSerializerService.serialize(glClosureRequest)).build();
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLClosure().withJson(jsonRequestBody).build(); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@PUT @PUT
@@ -143,13 +129,11 @@ public class GLClosuresApiResource {
@RequestBody(content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PutGlClosuresRequest.class, required = true))) @RequestBody(content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PutGlClosuresRequest.class, required = true)))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PutGlClosuresResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.PutGlClosuresResponse.class))) })
public String updateGLClosure(@PathParam("glClosureId") @Parameter(description = "glClosureId") final Long glClosureId, public CommandProcessingResult updateGLClosure(@PathParam("glClosureId") @Parameter(description = "glClosureId") final Long glClosureId,
@Parameter(hidden = true) final String jsonRequestBody) { @Parameter(hidden = true) GLClosureRequest glClosureRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLClosure(glClosureId).withJson(jsonRequestBody).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLClosure(glClosureId)
.withJson(apiJsonSerializerService.serialize(glClosureRequest)).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@DELETE @DELETE
@@ -159,12 +143,9 @@ public class GLClosuresApiResource {
@Operation(summary = "Delete an accounting closure", description = "Note: Only the latest accounting closure associated with a branch may be deleted.") @Operation(summary = "Delete an accounting closure", description = "Note: Only the latest accounting closure associated with a branch may be deleted.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.DeleteGlClosuresResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLClosuresApiResourceSwagger.DeleteGlClosuresResponse.class))) })
public String deleteGLClosure(@PathParam("glClosureId") @Parameter(description = "glclosureId") final Long glClosureId) { public CommandProcessingResult deleteGLClosure(
@PathParam("glClosureId") @Parameter(description = "glclosureId") final Long glClosureId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLClosure(glClosureId).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLClosure(glClosureId).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
} }
@@ -21,8 +21,6 @@ package org.apache.fineract.accounting.closure.command;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.api.GLClosureJsonInputParams; import org.apache.fineract.accounting.closure.api.GLClosureJsonInputParams;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
@@ -31,14 +29,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
/** /**
* Immutable command for adding an accounting closure * Immutable command for adding an accounting closure
*/ */
@RequiredArgsConstructor public record GLClosureCommand(Long id, Long officeId, LocalDate closingDate, String comments) {
@Getter
public class GLClosureCommand {
private final Long id;
private final Long officeId;
private final LocalDate closingDate;
private final String comments;
public void validateForCreate() { public void validateForCreate() {
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.accounting.closure.data; package org.apache.fineract.accounting.closure.data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -30,7 +32,10 @@ import org.apache.fineract.organisation.office.data.OfficeData;
* Note: no getter/setters required as google-gson will produce json from fields of object. * Note: no getter/setters required as google-gson will produce json from fields of object.
*/ */
@Data @Data
public class GLClosureData { public class GLClosureData implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final Long id; private final Long id;
private final Long officeId; private final Long officeId;
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.closure.data.request;
import java.io.Serial;
import java.io.Serializable;
public record GLClosureRequest(Long id, Long officeId, String closingDate, String comments, String dateFormat,
String locale) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -32,21 +32,21 @@ public class GLClosureInvalidException extends AbstractPlatformDomainRuleExcepti
FUTURE_DATE, ACCOUNTING_CLOSED; FUTURE_DATE, ACCOUNTING_CLOSED;
public String errorMessage() { public String errorMessage() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { if (name().equalsIgnoreCase("FUTURE_DATE")) {
return "Accounting closures cannot be made for a future date"; return "Accounting closures cannot be made for a future date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { } else if (name().equalsIgnoreCase("ACCOUNTING_CLOSED")) {
return "Accounting Closure for this branch has already been defined for a greater date"; return "Accounting Closure for this branch has already been defined for a greater date";
} }
return name().toString(); return name();
} }
public String errorCode() { public String errorCode() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { if (name().equalsIgnoreCase("FUTURE_DATE")) {
return "error.msg.glclosure.invalid.future.date"; return "error.msg.glclosure.invalid.future.date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { } else if (name().equalsIgnoreCase("ACCOUNTING_CLOSED")) {
return "error.msg.glclosure.invalid.accounting.closed"; return "error.msg.glclosure.invalid.accounting.closed";
} }
return name().toString(); return name();
} }
} }
@@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DELETE;
@@ -41,6 +40,7 @@ import jakarta.ws.rs.core.UriInfo;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.financialactivityaccount.data.FinancialActivityAccountData; import org.apache.fineract.accounting.financialactivityaccount.data.FinancialActivityAccountData;
import org.apache.fineract.accounting.financialactivityaccount.data.request.FinancialActivityAccRequest;
import org.apache.fineract.accounting.financialactivityaccount.service.FinancialActivityAccountReadPlatformService; import org.apache.fineract.accounting.financialactivityaccount.service.FinancialActivityAccountReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
@@ -54,9 +54,17 @@ import org.springframework.stereotype.Component;
@Path("/v1/financialactivityaccounts") @Path("/v1/financialactivityaccounts")
@Component @Component
@Tag(name = "Mapping Financial Activities to Accounts", description = "Organization Level Financial Activities like Asset and Liability Transfer can be mapped to GL Account. Integrated accounting takes these accounts into consideration when an Account transfer is made between a savings to loan/savings account and vice-versa\n" @Tag(name = "Mapping Financial Activities to Accounts", description = """
+ "\n" + "Field Descriptions\n" + "financialActivityId\n" + "The identifier of the Financial Activity\n" + "glAccountId\n" Organization Level Financial Activities like Asset and Liability Transfer can be mapped to GL Account. \
+ "The identifier of a GL Account ( Ledger Account) which shall be used as the default account for the selected Financial Activity") Integrated accounting takes these accounts into consideration when an Account transfer is made between \
a savings to loan/savings account and vice-versa
Field Descriptions
financialActivityId
The identifier of the Financial Activity
glAccountId
The identifier of a GL Account ( Ledger Account) which shall be used as the default account for the selected Financial Activity
""")
@RequiredArgsConstructor @RequiredArgsConstructor
public class FinancialActivityAccountsApiResource { public class FinancialActivityAccountsApiResource {
@@ -70,94 +78,72 @@ public class FinancialActivityAccountsApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@Context final UriInfo uriInfo) { public FinancialActivityAccountData retrieveTemplate() {
context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission); return financialActivityAccountReadPlatformService.getFinancialActivityAccountTemplate();
FinancialActivityAccountData financialActivityAccountData = this.financialActivityAccountReadPlatformService
.getFinancialActivityAccountTemplate();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, financialActivityAccountData,
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Financial Activities to Accounts Mappings", description = "Example Requests:\n" + "\n" @Operation(summary = "List Financial Activities to Accounts Mappings", description = """
+ "financialactivityaccounts") Example Requests:
@ApiResponses({ financialactivityaccounts""")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.GetFinancialActivityAccountsResponse.class)))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.GetFinancialActivityAccountsResponse.class))))
public String retrieveAll(@Context final UriInfo uriInfo) { public List<FinancialActivityAccountData> retrieveAll() {
context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission); return financialActivityAccountReadPlatformService.retrieveAll();
final List<FinancialActivityAccountData> financialActivityAccounts = this.financialActivityAccountReadPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, financialActivityAccounts,
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Path("{mappingId}") @Path("{mappingId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Financial Activity to Account Mapping\n", description = "Example Requests:\n" + "\n" @Operation(summary = "Retrieve a Financial Activity to Account Mapping\n", description = """
+ "financialactivityaccounts/1") Example Requests:
@ApiResponses({ financialactivityaccounts/1""")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.GetFinancialActivityAccountsResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.GetFinancialActivityAccountsResponse.class)))
public String retreive(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId, public FinancialActivityAccountData retreive(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
FinancialActivityAccountData financialActivityAccountData = financialActivityAccountReadPlatformService.retrieve(mappingId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
FinancialActivityAccountData financialActivityAccountData = this.financialActivityAccountReadPlatformService.retrieve(mappingId);
if (settings.isTemplate()) { if (settings.isTemplate()) {
financialActivityAccountData = this.financialActivityAccountReadPlatformService financialActivityAccountData = financialActivityAccountReadPlatformService.addTemplateDetails(financialActivityAccountData);
.addTemplateDetails(financialActivityAccountData);
} }
return this.apiJsonSerializerService.serialize(settings, financialActivityAccountData, return financialActivityAccountData;
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a new Financial Activity to Accounts Mapping", description = "Mandatory Fields\n" @Operation(summary = "Create a new Financial Activity to Accounts Mapping", description = """
+ "financialActivityId, glAccountId") Mandatory Fields
financialActivityId, glAccountId""")
@RequestBody(content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsResponse.class))) }) public CommandProcessingResult createGLAccount(@Parameter(hidden = true) FinancialActivityAccRequest activityAccRequest) {
public String createGLAccount(@Parameter(hidden = true) final String jsonRequestBody) { final CommandWrapper commandRequest = new CommandWrapperBuilder().createOfficeToGLAccountMapping()
.withJson(apiJsonSerializerService.serialize(activityAccRequest)).build();
final CommandWrapper commandRequest = new CommandWrapperBuilder().createOfficeToGLAccountMapping().withJson(jsonRequestBody) return commandsSourceWritePlatformService.logCommandSource(commandRequest);
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@PUT @PUT
@Path("{mappingId}") @Path("{mappingId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Financial Activity to Account Mapping", description = "the API updates the Ledger account linked to a Financial Activity \n") @Operation(summary = "Update a Financial Activity to Account Mapping", description = "the API updates the Ledger account linked to a Financial Activity")
@RequestBody(content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PostFinancialActivityAccountsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PutFinancialActivityAccountsResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.PutFinancialActivityAccountsResponse.class))) }) public CommandProcessingResult updateGLAccount(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId,
public String updateGLAccount(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId, @Parameter(hidden = true) FinancialActivityAccRequest activityAccRequest) {
@Parameter(hidden = true) final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateOfficeToGLAccountMapping(mappingId) final CommandWrapper commandRequest = new CommandWrapperBuilder().updateOfficeToGLAccountMapping(mappingId)
.withJson(jsonRequestBody).build(); .withJson(apiJsonSerializerService.serialize(activityAccRequest)).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@DELETE @DELETE
@@ -165,14 +151,9 @@ public class FinancialActivityAccountsApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a Financial Activity to Account Mapping") @Operation(summary = "Delete a Financial Activity to Account Mapping")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.DeleteFinancialActivityAccountsResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = FinancialActivityAccountsApiResourceSwagger.DeleteFinancialActivityAccountsResponse.class))) }) public CommandProcessingResult deleteGLAccount(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId) {
public String deleteGLAccount(@PathParam("mappingId") @Parameter(description = "mappingId") final Long mappingId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteOfficeToGLAccountMapping(mappingId).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteOfficeToGLAccountMapping(mappingId).build();
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
} }
@@ -48,7 +48,7 @@ public enum FinancialActivityAccountsJsonInputParams {
@Override @Override
public String toString() { public String toString() {
return name().toString().replaceAll("_", " "); return name().replace("_", " ");
} }
public String getValue() { public String getValue() {
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.financialactivityaccount.data.request;
import java.io.Serial;
import java.io.Serializable;
public record FinancialActivityAccRequest(Long financialActivityId, Long glAccountId) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -26,17 +26,19 @@ import org.springframework.dao.EmptyResultDataAccessException;
*/ */
public class FinancialActivityAccountNotFoundException extends AbstractPlatformResourceNotFoundException { public class FinancialActivityAccountNotFoundException extends AbstractPlatformResourceNotFoundException {
private static final String DOES_NOT_EXIST = " does not exist";
private static final String ERROR_CODE = "error.msg.financialActivityAccount.not.found";
public FinancialActivityAccountNotFoundException(final Long id) { public FinancialActivityAccountNotFoundException(final Long id) {
super("error.msg.financialActivityAccount.not.found", "Financial Activity account with Id " + id + " does not exist", id); super(ERROR_CODE, "Financial Activity account with Id " + id + DOES_NOT_EXIST, id);
} }
public FinancialActivityAccountNotFoundException(final Long id, EmptyResultDataAccessException e) { public FinancialActivityAccountNotFoundException(final Long id, EmptyResultDataAccessException e) {
super("error.msg.financialActivityAccount.not.found", "Financial Activity account with Id " + id + " does not exist", id, e); super(ERROR_CODE, "Financial Activity account with Id " + id + DOES_NOT_EXIST, id, e);
} }
public FinancialActivityAccountNotFoundException(final Integer financialActivityType) { public FinancialActivityAccountNotFoundException(final Integer financialActivityType) {
super("error.msg.financialActivityAccount.not.found", super(ERROR_CODE, "Financial Activity account with for the financial Activity with Id " + financialActivityType + DOES_NOT_EXIST,
"Financial Activity account with for the financial Activity with Id " + financialActivityType + " does not exist",
financialActivityType); financialActivityType);
} }
@@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DELETE;
@@ -41,14 +40,12 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants; import org.apache.fineract.accounting.common.AccountingConstants;
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService; import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
import org.apache.fineract.accounting.glaccount.command.GLAccountCommand;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService; import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
@@ -74,17 +71,13 @@ import org.springframework.stereotype.Component;
@Path("/v1/glaccounts") @Path("/v1/glaccounts")
@Component @Component
@Tag(name = "General Ledger Account", description = "Ledger accounts represent an Individual account within an Organizations Chart Of Accounts(COA) and are assigned a name and unique number by which they can be identified. \n" @Tag(name = "General Ledger Account", description = """
+ "All transactions relating to a company's assets, liabilities, owners' equity, revenue and expenses are recorded against these accounts") Ledger accounts represent an Individual account within an Organizations Chart Of Accounts(COA) and are assigned a name and unique number by which they can be identified.
All transactions relating to a company's assets, liabilities, owners' equity, revenue and expenses are recorded against these accounts
""")
@RequiredArgsConstructor @RequiredArgsConstructor
public class GLAccountsApiResource { public class GLAccountsApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "parentId", "glCode", "disabled",
"manualEntriesAllowed", "type", "usage", "description", "assetHeaderAccountOptions", "liabilityHeaderAccountOptions",
"equityHeaderAccountOptions", "incomeHeaderAccountOptions", "expenseHeaderAccountOptions", "nameDecorated", "tagId",
"allowedAssetsTagOptions", "allowedLiabilitiesTagOptions", "allowedEquityTagOptions", "allowedIncomeTagOptions",
"allowedExpensesTagOptions", "creditAccounts", "debitAccounts"));
private static final String RESOURCE_NAME_FOR_PERMISSION = "GLACCOUNT"; private static final String RESOURCE_NAME_FOR_PERMISSION = "GLACCOUNT";
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
@@ -102,91 +95,103 @@ public class GLAccountsApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { @Operation(tags = {
"General Ledger Account" }, summary = "Retrieve GL Accounts Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n" "General Ledger Account" }, summary = "Retrieve GL Accounts Template", description = """
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n" + "Example Request:\n" + "\n" + "glaccounts/template\n" This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:
+ "glaccounts/template?type=1\n" + "\n" + "type is optional and integer value from 1 to 5.\n" + "\n" + "1.Assets \n"
+ "2.Liabilities \n" + "3.Equity \n" + "4.Income \n" + "5.Expenses")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsTemplateResponse.class))) })
public String retrieveNewAccountDetails(@Context final UriInfo uriInfo,
@QueryParam("type") @Parameter(description = "type") final Integer type) {
Field Defaults
Allowed Value Lists
Example Request:
glaccounts/template
glaccounts/template?type=1
type is optional and integer value from 1 to 5.
1.Assets
2.Liabilities
3.Equity
4.Income
5.Expenses
""")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsTemplateResponse.class)))
public GLAccountData retrieveNewAccountDetails(@QueryParam("type") @Parameter(description = "type") final Integer type) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveNewGLAccountDetails(type); return handleTemplate(this.glAccountReadPlatformService.retrieveNewGLAccountDetails(type));
glAccountData = handleTemplate(glAccountData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glAccountData, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { "General Ledger Account" }, summary = "List General Ledger Accounts", description = "ARGUMENTS\n" @Operation(tags = {
+ "type Integer optional manualEntriesAllowed boolean optional usage Integer optional disabled boolean optional parentId Long optional tagId Long optional\n" "General Ledger Account" }, summary = "List General Ledger Account", description = """
+ "Example Requests:\n" + "\n" + "glaccounts\n" + "\n" + "\n" ARGUMENTS
+ "glaccounts?type=1&manualEntriesAllowed=true&usage=1&disabled=false\n" + "\n" + "glaccounts?fetchRunningBalance=true") type Integer optional manualEntriesAllowed boolean optional usage Integer optional disabled boolean optional parentId Long optional tagId Long optional
@ApiResponses({ Example Requests:
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsResponse.class)))) })
public String retrieveAllAccounts(@Context final UriInfo uriInfo, glaccounts
@QueryParam("type") @Parameter(description = "type") final Integer type,
glaccounts?type=1&manualEntriesAllowed=true&usage=1&disabled=false
glaccounts?fetchRunningBalance=true
""")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsResponse.class))))
public List<GLAccountData> retrieveAllAccounts(@QueryParam("type") @Parameter(description = "type") final Integer type,
@QueryParam("searchParam") @Parameter(description = "searchParam") final String searchParam, @QueryParam("searchParam") @Parameter(description = "searchParam") final String searchParam,
@QueryParam("usage") @Parameter(description = "usage") final Integer usage, @QueryParam("usage") @Parameter(description = "usage") final Integer usage,
@QueryParam("manualEntriesAllowed") @Parameter(description = "manualEntriesAllowed") final Boolean manualEntriesAllowed, @QueryParam("manualEntriesAllowed") @Parameter(description = "manualEntriesAllowed") final Boolean manualEntriesAllowed,
@QueryParam("disabled") @Parameter(description = "disabled") final Boolean disabled, @QueryParam("disabled") @Parameter(description = "disabled") final Boolean disabled,
@QueryParam("fetchRunningBalance") @Parameter(description = "fetchRunningBalance") final boolean runningBalance) { @QueryParam("fetchRunningBalance") @Parameter(description = "fetchRunningBalance") final boolean runningBalance) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance); JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance);
final List<GLAccountData> glAccountDatas = this.glAccountReadPlatformService.retrieveAllGLAccounts(type, searchParam, usage, return this.glAccountReadPlatformService.retrieveAllGLAccounts(type, searchParam, usage, manualEntriesAllowed, disabled,
manualEntriesAllowed, disabled, associationParametersData); associationParametersData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glAccountDatas, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@Path("{glAccountId}") @Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { "General Ledger Account" }, summary = "Retrieve a General Ledger Account", description = "Example Requests:\n" @Operation(tags = { "General Ledger Account" }, summary = "Retrieve a General Ledger Account", description = """
+ "\n" + "glaccounts/1\n" + "\n" + "\n" + "glaccounts/1?template=true\n" + "\n" + "\n" + "glaccounts/1?fields=name,glCode\n" Example Requests:
+ "\n" + "\n" + "glaccounts/1?fetchRunningBalance=true")
@ApiResponses({ glaccounts/1
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsResponse.class))) })
public String retreiveAccount(@PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId, glaccounts/1?template=true
glaccounts/1?fields=name,glCode
glaccounts/1?fetchRunningBalance=true
""")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.GetGLAccountsResponse.class)))
public GLAccountData retreiveAccount(@PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId,
@Context final UriInfo uriInfo, @Context final UriInfo uriInfo,
@QueryParam("fetchRunningBalance") @Parameter(description = "fetchRunningBalance") final boolean runningBalance) { @QueryParam("fetchRunningBalance") @Parameter(description = "fetchRunningBalance") final boolean runningBalance) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance); JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId, associationParametersData); final GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId, associationParametersData);
if (settings.isTemplate()) { return settings.isTemplate() ? handleTemplate(glAccountData) : glAccountData;
glAccountData = handleTemplate(glAccountData);
}
return this.apiJsonSerializerService.serialize(settings, glAccountData, RESPONSE_DATA_PARAMETERS);
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { @Operation(tags = { "General Ledger Account" }, summary = "Create a General Ledger Account", description = """
"General Ledger Account" }, summary = "Create a General Ledger Account", description = "Note: You may optionally create Hierarchical Chart of Accounts by using the \"parentId\" property of an Account\n" Note: You may optionally create Hierarchical Chart of Accounts by using the "parentId" property of an Account
+ "Mandatory Fields: \n" + "name, glCode, type, usage and manualEntriesAllowed") Mandatory Fields:
name, glCode, type, usage and manualEntriesAllowed
""")
@RequestBody(content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PostGLAccountsRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PostGLAccountsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PostGLAccountsResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PostGLAccountsResponse.class))) }) public CommandProcessingResult createGLAccount(@Parameter(hidden = true) GLAccountCommand glAccountCommand) {
public String createGLAccount(@Parameter(hidden = true) final String jsonRequestBody) { final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLAccount()
.withJson(apiJsonSerializerService.serialize(glAccountCommand)).build();
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLAccount().withJson(jsonRequestBody).build(); return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@PUT @PUT
@@ -195,16 +200,12 @@ public class GLAccountsApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { "General Ledger Account" }, summary = "Update a GL Account", description = "Updates a GL Account") @Operation(tags = { "General Ledger Account" }, summary = "Update a GL Account", description = "Updates a GL Account")
@RequestBody(content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PutGLAccountsRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PutGLAccountsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PutGLAccountsResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.PutGLAccountsResponse.class))) }) public CommandProcessingResult updateGLAccount(@PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId,
public String updateGLAccount(@PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId, @Parameter(hidden = true) GLAccountCommand accountCommand) {
@Parameter(hidden = true) final String jsonRequestBody) { final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLAccount(glAccountId)
.withJson(apiJsonSerializerService.serialize(accountCommand)).build();
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLAccount(glAccountId).withJson(jsonRequestBody).build(); return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@DELETE @DELETE
@@ -212,15 +213,30 @@ public class GLAccountsApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(tags = { "General Ledger Account" }, summary = "Delete a GL Account", description = "Deletes a GL Account") @Operation(tags = { "General Ledger Account" }, summary = "Delete a GL Account", description = "Deletes a GL Account")
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.DeleteGLAccountsRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = GLAccountsApiResourceSwagger.DeleteGLAccountsRequest.class))) }) public CommandProcessingResult deleteGLAccount(
public String deleteGLAccount(@PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId) { @PathParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLAccount(glAccountId).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLAccount(glAccountId).build();
return this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); @GET
@Path("downloadtemplate")
@Produces("application/vnd.ms-excel")
public Response getGlAccountsTemplate(@QueryParam("dateFormat") final String dateFormat) {
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.CHART_OF_ACCOUNTS.toString(), null, null, dateFormat);
}
return this.apiJsonSerializerService.serialize(result); @POST
@Path("uploadtemplate")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@RequestBody(description = "Upload GL accounts template", content = {
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
public Long postGlAccountsTemplate(@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
@FormDataParam("dateFormat") final String dateFormat) {
return bulkImportWorkbookService.importWorkbook(GlobalEntityType.CHART_OF_ACCOUNTS.toString(), uploadedInputStream, fileDetail,
locale, dateFormat);
} }
private GLAccountData handleTemplate(final GLAccountData glAccountData) { private GLAccountData handleTemplate(final GLAccountData glAccountData) {
@@ -262,30 +278,6 @@ public class GLAccountsApiResource {
} }
private List<GLAccountData> defaultIfEmpty(final List<GLAccountData> list) { private List<GLAccountData> defaultIfEmpty(final List<GLAccountData> list) {
List<GLAccountData> returnList = null; return list != null && !list.isEmpty() ? list : null;
if (list != null && !list.isEmpty()) {
returnList = list;
}
return returnList;
}
@GET
@Path("downloadtemplate")
@Produces("application/vnd.ms-excel")
public Response getGlAccountsTemplate(@QueryParam("dateFormat") final String dateFormat) {
return bulkImportWorkbookPopulatorService.getTemplate(GlobalEntityType.CHART_OF_ACCOUNTS.toString(), null, null, dateFormat);
}
@POST
@Path("uploadtemplate")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@RequestBody(description = "Upload GL accounts template", content = {
@Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) })
public String postGlAccountsTemplate(@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale,
@FormDataParam("dateFormat") final String dateFormat) {
Long importDocumentId = bulkImportWorkbookService.importWorkbook(GlobalEntityType.CHART_OF_ACCOUNTS.toString(), uploadedInputStream,
fileDetail, locale, dateFormat);
return this.apiJsonSerializerService.serialize(importDocumentId);
} }
} }
@@ -20,8 +20,6 @@ package org.apache.fineract.accounting.glaccount.command;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.api.GLAccountJsonInputParams; import org.apache.fineract.accounting.glaccount.api.GLAccountJsonInputParams;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.glaccount.domain.GLAccountUsage; import org.apache.fineract.accounting.glaccount.domain.GLAccountUsage;
@@ -32,20 +30,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
/** /**
* Immutable command for adding a general Ledger Account * Immutable command for adding a general Ledger Account
*/ */
@RequiredArgsConstructor
@Getter
public class GLAccountCommand {
private final Long id; public record GLAccountCommand(Long id, String name, Long parentId, String glCode, Boolean disabled, Boolean manualEntriesAllowed,
private final String name; Integer type, Integer usage, String description, Long tagId) {
private final Long parentId;
private final String glCode;
private final Boolean disabled;
private final Boolean manualEntriesAllowed;
private final Integer type;
private final Integer usage;
private final String description;
private final Long tagId;
public void validateForCreate() { public void validateForCreate() {
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.accounting.glaccount.data; package org.apache.fineract.accounting.glaccount.data;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -28,6 +29,7 @@ import lombok.experimental.Accessors;
@Accessors(chain = true) @Accessors(chain = true)
public class GLAccountDataForLookup implements Serializable { public class GLAccountDataForLookup implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Long id; private Long id;
@@ -28,24 +28,24 @@ public class GLAccountInvalidDeleteException extends AbstractPlatformDomainRuleE
/*** Enum of reasons for invalid delete **/ /*** Enum of reasons for invalid delete **/
public enum GlAccountInvalidDeleteReason { public enum GlAccountInvalidDeleteReason {
TRANSANCTIONS_LOGGED, HAS_CHILDREN; TRANSACTIONS_LOGGED, HAS_CHILDREN;
public String errorMessage() { public String errorMessage() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) { if (name().equalsIgnoreCase("TRANSACTIONS_LOGGED")) {
return "This GL Account cannot be deleted as it has transactions logged against it"; return "This GL Account cannot be deleted as it has transactions logged against it";
} else if (name().toString().equalsIgnoreCase("HAS_CHILDREN")) { } else if (name().equalsIgnoreCase("HAS_CHILDREN")) {
return "Cannot delete this Header GL Account without first deleting or reassinging its children"; return "Cannot delete this Header GL Account without first deleting or reassigning its children";
} }
return name().toString(); return name();
} }
public String errorCode() { public String errorCode() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) { if (name().equalsIgnoreCase("TRANSACTIONS_LOGGED")) {
return "error.msg.glaccount.glcode.invalid.delete.transactions.logged"; return "error.msg.glaccount.glcode.invalid.delete.transactions.logged";
} else if (name().toString().equalsIgnoreCase("HAS_CHILDREN")) { } else if (name().equalsIgnoreCase("HAS_CHILDREN")) {
return "error.msg.glaccount.glcode.invalid.delete.has.children"; return "error.msg.glaccount.glcode.invalid.delete.has.children";
} }
return name().toString(); return name();
} }
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails; package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper; import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory; import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
@@ -27,22 +28,18 @@ import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@Configuration @Configuration
@RequiredArgsConstructor
public class UpdateTrialBalanceDetailsConfig { public class UpdateTrialBalanceDetailsConfig {
@Autowired private final JobRepository jobRepository;
private JobRepository jobRepository; private final PlatformTransactionManager transactionManager;
@Autowired private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
private PlatformTransactionManager transactionManager; private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
@Autowired
private RoutingDataSourceServiceFactory dataSourceServiceFactory;
@Autowired
private TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
@Bean @Bean
protected Step updateTrialBalanceDetailsStep() { protected Step updateTrialBalanceDetailsStep() {
@@ -20,7 +20,6 @@ package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -51,7 +50,7 @@ public class UpdateTrialBalanceDetailsTasklet implements Tasklet {
.append("where je.transaction_date > (select coalesce(MAX(created_date),'2010-01-01') from m_trial_balance)"); .append("where je.transaction_date > (select coalesce(MAX(created_date),'2010-01-01') from m_trial_balance)");
final List<LocalDate> tbGaps = jdbcTemplate.queryForList(tbGapSqlBuilder.toString(), LocalDate.class); final List<LocalDate> tbGaps = jdbcTemplate.queryForList(tbGapSqlBuilder.toString(), LocalDate.class);
for (LocalDate tbGap : tbGaps) { for (LocalDate tbGap : tbGaps) {
int days = Math.toIntExact(ChronoUnit.DAYS.between(tbGap, DateUtils.getBusinessLocalDate())); int days = DateUtils.getExactDifferenceInDays(tbGap, DateUtils.getBusinessLocalDate());
if (days < 1) { if (days < 1) {
continue; continue;
} }
@@ -44,10 +44,12 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformService { public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformService {
private static final String nameDecoratedBaseOnHierarchy = "concat(substring('........................................', 1, ((LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '.', '')) - 1) * 4)), name)"; private static final String NAME_DECORATED_BASE_ON_HIERARCHY = "concat(substring('........................................', 1, ((LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '.', '')) - 1) * 4)), name)";
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private static final String SQL_AND = " and ";
private static final class GLAccountMapper implements RowMapper<GLAccountData> { private static final class GLAccountMapper implements RowMapper<GLAccountData> {
private final JournalEntryAssociationParametersData associationParametersData; private final JournalEntryAssociationParametersData associationParametersData;
@@ -65,7 +67,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
sb.append( sb.append(
" gl.id as id, name as name, parent_id as parentId, gl_code as glCode, disabled as disabled, manual_journal_entries_allowed as manualEntriesAllowed, ") " gl.id as id, name as name, parent_id as parentId, gl_code as glCode, disabled as disabled, manual_journal_entries_allowed as manualEntriesAllowed, ")
.append("classification_enum as classification, account_usage as accountUsage, gl.description as description, ") .append("classification_enum as classification, account_usage as accountUsage, gl.description as description, ")
.append(nameDecoratedBaseOnHierarchy).append(" as nameDecorated, ") .append(NAME_DECORATED_BASE_ON_HIERARCHY).append(" as nameDecorated, ")
.append("cv.id as codeId, cv.code_value as codeValue "); .append("cv.id as codeId, cv.code_value as codeValue ");
if (this.associationParametersData.isRunningBalanceRequired()) { if (this.associationParametersData.isRunningBalanceRequired()) {
sb.append(",gl_j.organization_running_balance as organizationRunningBalance "); sb.append(",gl_j.organization_running_balance as organizationRunningBalance ");
@@ -109,29 +111,23 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
public List<GLAccountData> retrieveAllGLAccounts(final Integer accountClassification, final String searchParam, final Integer usage, public List<GLAccountData> retrieveAllGLAccounts(final Integer accountClassification, final String searchParam, final Integer usage,
final Boolean manualTransactionsAllowed, final Boolean disabled, final Boolean manualTransactionsAllowed, final Boolean disabled,
JournalEntryAssociationParametersData associationParametersData) { JournalEntryAssociationParametersData associationParametersData) {
if (accountClassification != null) { if (accountClassification != null && !checkValidGLAccountType(accountClassification)) {
if (!checkValidGLAccountType(accountClassification)) {
throw new GLAccountInvalidClassificationException(accountClassification); throw new GLAccountInvalidClassificationException(accountClassification);
} }
}
if (usage != null) { if (usage != null && !checkValidGLAccountUsage(usage)) {
if (!checkValidGLAccountUsage(usage)) {
throw new GLAccountInvalidClassificationException(accountClassification); throw new GLAccountInvalidClassificationException(accountClassification);
} }
}
final GLAccountMapper rm = new GLAccountMapper(associationParametersData); final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
String sql = "select " + rm.schema(); String sql = "select " + rm.schema();
// append SQL statement for fetching account totals // append SQL statement for fetching account totals
if (associationParametersData != null) { if (associationParametersData != null && associationParametersData.isRunningBalanceRequired()) {
if (associationParametersData.isRunningBalanceRequired()) {
sql = sql + " and gl_j.id in (select t1.id from (select t2.account_id, max(t2.id) as id from " sql = sql + " and gl_j.id in (select t1.id from (select t2.account_id, max(t2.id) as id from "
+ "(select id, max(entry_date) as entry_date, account_id from acc_gl_journal_entry where is_running_balance_calculated = true " + "(select id, max(entry_date) as entry_date, account_id from acc_gl_journal_entry where is_running_balance_calculated = true "
+ "group by account_id desc, id) t3 inner join acc_gl_journal_entry t2 on t2.account_id = t3.account_id and t2.entry_date = t3.entry_date " + "group by account_id desc, id) t3 inner join acc_gl_journal_entry t2 on t2.account_id = t3.account_id and t2.entry_date = t3.entry_date "
+ "group by t2.account_id desc) t1)"; + "group by t2.account_id desc) t1)";
} }
}
final Object[] parameterArray = new Object[3]; final Object[] parameterArray = new Object[3];
int arrayPos = 0; int arrayPos = 0;
boolean filtersPresent = false; boolean filtersPresent = false;
@@ -151,7 +147,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
} }
if (StringUtils.isNotBlank(searchParam)) { if (StringUtils.isNotBlank(searchParam)) {
if (firstWhereConditionAdded) { if (firstWhereConditionAdded) {
sql += " and "; sql += SQL_AND;
} }
sql += " ( name like %?% or gl_code like %?% )"; sql += " ( name like %?% or gl_code like %?% )";
parameterArray[arrayPos] = searchParam; parameterArray[arrayPos] = searchParam;
@@ -162,7 +158,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
} }
if (usage != null) { if (usage != null) {
if (firstWhereConditionAdded) { if (firstWhereConditionAdded) {
sql += " and "; sql += SQL_AND;
} }
if (GLAccountUsage.HEADER.getValue().equals(usage)) { if (GLAccountUsage.HEADER.getValue().equals(usage)) {
sql += " account_usage = 2 "; sql += " account_usage = 2 ";
@@ -173,7 +169,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
} }
if (manualTransactionsAllowed != null) { if (manualTransactionsAllowed != null) {
if (firstWhereConditionAdded) { if (firstWhereConditionAdded) {
sql += " and "; sql += SQL_AND;
} }
sql += " manual_journal_entries_allowed = " + manualTransactionsAllowed; sql += " manual_journal_entries_allowed = " + manualTransactionsAllowed;
@@ -181,37 +177,32 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
} }
if (disabled != null) { if (disabled != null) {
if (firstWhereConditionAdded) { if (firstWhereConditionAdded) {
sql += " and "; sql += SQL_AND;
} }
sql += " disabled = " + disabled; sql += " disabled = " + disabled;
firstWhereConditionAdded = true;
} }
} }
sql += " ORDER BY gl_code ASC"; sql += " ORDER BY gl_code ASC";
final Object[] finalObjectArray = Arrays.copyOf(parameterArray, arrayPos); final Object[] finalObjectArray = Arrays.copyOf(parameterArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray); // NOSONAR return this.jdbcTemplate.query(sql, rm, (Object[]) finalObjectArray);// NOSONAR
} }
@Override @Override
public GLAccountData retrieveGLAccountById(final long glAccountId, JournalEntryAssociationParametersData associationParametersData) { public GLAccountData retrieveGLAccountById(final long glAccountId, JournalEntryAssociationParametersData associationParametersData) {
try { try {
final GLAccountMapper rm = new GLAccountMapper(associationParametersData); final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
final StringBuilder sql = new StringBuilder(); final StringBuilder sql = new StringBuilder();
sql.append("select ").append(rm.schema()); sql.append("select ").append(rm.schema());
sql.append(" where gl.id = ?");
if (associationParametersData.isRunningBalanceRequired()) { if (associationParametersData.isRunningBalanceRequired()) {
sql.append(" and gl_j.is_running_balance_calculated = true "); sql.append(" and gl_j.is_running_balance_calculated = true ")
.append(" ORDER BY gl_j.entry_date DESC,gl_j.id DESC LIMIT 1");
} }
sql.append("where gl.id = ?");
if (associationParametersData.isRunningBalanceRequired()) {
sql.append(" ORDER BY gl_j.entry_date DESC,gl_j.id DESC LIMIT 1");
}
final GLAccountData glAccountData = this.jdbcTemplate.queryForObject(sql.toString(), rm, new Object[] { glAccountId });
return glAccountData; return this.jdbcTemplate.queryForObject(sql.toString(), rm, new Object[] { glAccountId });
} catch (final EmptyResultDataAccessException e) { } catch (final EmptyResultDataAccessException e) {
throw new GLAccountNotFoundException(glAccountId, e); throw new GLAccountNotFoundException(glAccountId, e);
} }
@@ -261,15 +252,13 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
@Override @Override
public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) { public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) {
final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper(); final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper();
final String sql = "Select " + mapper.schema() + " where rule.id=? and tags.acc_type_enum=?"; final String sql = "Select " + GLAccountDataLookUpMapper.LOOKUP_SCHEMA + " where rule.id=? and tags.acc_type_enum=?";
return this.jdbcTemplate.query(sql, mapper, new Object[] { ruleId, transactionType }); // NOSONAR return this.jdbcTemplate.query(sql, mapper, (Object[]) new Object[] { ruleId, transactionType });// NOSONAR
} }
private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> { private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> {
public String schema() { private static final String LOOKUP_SCHEMA = " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
return " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
}
@Override @Override
public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
@@ -194,7 +194,7 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
final boolean journalEntriesForAccountExist = this.glJournalEntryRepository final boolean journalEntriesForAccountExist = this.glJournalEntryRepository
.exists((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("glAccountId"), glAccountId)); .exists((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("glAccountId"), glAccountId));
if (journalEntriesForAccountExist) { if (journalEntriesForAccountExist) {
throw new GLAccountInvalidDeleteException(GlAccountInvalidDeleteReason.TRANSANCTIONS_LOGGED, glAccountId); throw new GLAccountInvalidDeleteException(GlAccountInvalidDeleteReason.TRANSACTIONS_LOGGED, glAccountId);
} }
this.glAccountRepository.delete(glAccount); this.glAccountRepository.delete(glAccount);
@@ -27,6 +27,7 @@ import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
@@ -54,6 +55,7 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "currency_code", length = 3, nullable = false) @Column(name = "currency_code", length = 3, nullable = false)
private String currencyCode; private String currencyCode;
@Setter
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reversal_id") @JoinColumn(name = "reversal_id")
private JournalEntry reversalJournalEntry; private JournalEntry reversalJournalEntry;
@@ -73,6 +75,7 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "share_transaction_id") @Column(name = "share_transaction_id")
private Long shareTransactionId; private Long shareTransactionId;
@Setter
@Column(name = "reversed", nullable = false) @Column(name = "reversed", nullable = false)
private boolean reversed = false; private boolean reversed = false;
@@ -147,11 +150,8 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return JournalEntryType.DEBIT.getValue().equals(this.type); return JournalEntryType.DEBIT.getValue().equals(this.type);
} }
public void setReversalJournalEntry(final JournalEntry reversalJournalEntry) { public boolean isCreditEntry() {
this.reversalJournalEntry = reversalJournalEntry; return JournalEntryType.CREDIT.getValue().equals(this.type);
} }
public void setReversed(final boolean reversed) {
this.reversed = reversed;
}
} }
@@ -29,6 +29,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType; import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
@@ -63,10 +64,14 @@ public class ProductToGLAccountMapping extends AbstractPersistableCustom<Long> {
@Column(name = "financial_account_type", nullable = true) @Column(name = "financial_account_type", nullable = true)
private int financialAccountType; private int financialAccountType;
@ManyToOne
@JoinColumn(name = "charge_off_reason_id", nullable = true)
private CodeValue chargeOffReason;
public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType, public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType,
final int financialAccountType) { final int financialAccountType, final CodeValue chargeOffReason) {
return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType) return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType)
.setFinancialAccountType(financialAccountType); .setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReason);
} }
} }
@@ -35,7 +35,7 @@ public interface ProductToGLAccountMappingRepository
@Param("productType") int productType, @Param("financialAccountType") int financialAccountType, @Param("productType") int productType, @Param("financialAccountType") int financialAccountType,
@Param("chargeId") Long ChargeId); @Param("chargeId") Long ChargeId);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL") @Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL and mapping.chargeOffReason is NULL")
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType, ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
@Param("financialAccountType") int financialAccountType); @Param("financialAccountType") int financialAccountType);
@@ -61,4 +61,25 @@ public interface ProductToGLAccountMappingRepository
@Param("productType") int productType); @Param("productType") int productType);
List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType); List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.chargeOffReason is not NULL")
List<ProductToGLAccountMapping> findAllChargeOffReasonsMappings(@Param("productId") Long productId,
@Param("productType") int productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.chargeOffReason.id =:chargeOffReasonId AND mapping.productId =:productId AND mapping.productType =:productType")
ProductToGLAccountMapping findChargeOffReasonMapping(@Param("productId") Long productId, @Param("productType") Integer productType,
@Param("chargeOffReasonId") Long chargeOffReasonId);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge IS NULL AND mapping.paymentType IS NULL AND mapping.chargeOffReason IS NULL")
List<ProductToGLAccountMapping> findAllRegularMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.paymentType is not NULL")
List<ProductToGLAccountMapping> findAllPaymentTypeMappings(@Param("productId") Long productId,
@Param("productType") Integer productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge.penalty = TRUE")
List<ProductToGLAccountMapping> findAllPenaltyMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge.penalty = FALSE")
List<ProductToGLAccountMapping> findAllFeeMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
} }
@@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan; import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
@@ -39,7 +40,11 @@ import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGL
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingInvalidException; import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingInvalidException;
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException; import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.PortfolioProductType;
import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.Charge;
@@ -53,6 +58,7 @@ import org.springframework.stereotype.Component;
public class ProductToGLAccountMappingHelper { public class ProductToGLAccountMappingHelper {
protected static final List<GLAccountType> ASSET_LIABILITY_TYPES = List.of(GLAccountType.ASSET, GLAccountType.LIABILITY); protected static final List<GLAccountType> ASSET_LIABILITY_TYPES = List.of(GLAccountType.ASSET, GLAccountType.LIABILITY);
private static final Integer GL_ACCOUNT_EXPENSE_TYPE = 5;
protected final GLAccountRepository accountRepository; protected final GLAccountRepository accountRepository;
protected final ProductToGLAccountMappingRepository accountMappingRepository; protected final ProductToGLAccountMappingRepository accountMappingRepository;
@@ -60,6 +66,7 @@ public class ProductToGLAccountMappingHelper {
private final ChargeRepositoryWrapper chargeRepositoryWrapper; private final ChargeRepositoryWrapper chargeRepositoryWrapper;
protected final GLAccountRepositoryWrapper accountRepositoryWrapper; protected final GLAccountRepositoryWrapper accountRepositoryWrapper;
private final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper; private final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper;
private final CodeValueRepository codeValueRepository;
public void saveProductToAccountMapping(final JsonElement element, final String paramName, final Long productId, public void saveProductToAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId, final GLAccountType expectedAccountType, final PortfolioProductType portfolioProductType) { final int placeHolderTypeId, final GLAccountType expectedAccountType, final PortfolioProductType portfolioProductType) {
@@ -194,6 +201,27 @@ public class ProductToGLAccountMappingHelper {
} }
} }
public void saveChargeOffReasonToGLAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType) {
final String arrayName = LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue();
final JsonArray chargeOffReasonToExpenseAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(arrayName, element);
if (chargeOffReasonToExpenseAccountMappingArray != null) {
if (changes != null) {
changes.put(arrayName, command.jsonFragment(arrayName));
}
for (int i = 0; i < chargeOffReasonToExpenseAccountMappingArray.size(); i++) {
final JsonObject jsonObject = chargeOffReasonToExpenseAccountMappingArray.get(i).getAsJsonObject();
final Long reasonId = jsonObject.get(LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue()).getAsLong();
final Long expenseAccountId = jsonObject.get(LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue()).getAsLong();
saveChargeOffReasonToExpenseMapping(productId, reasonId, expenseAccountId, portfolioProductType);
}
}
}
/** /**
* @param command * @param command
* @param element * @param element
@@ -356,6 +384,65 @@ public class ProductToGLAccountMappingHelper {
} }
} }
public void updateChargeOffReasonToGLAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType) {
final List<ProductToGLAccountMapping> existingChargeOffReasonToGLAccountMappings = this.accountMappingRepository
.findAllChargeOffReasonsMappings(productId, portfolioProductType.getValue());
final JsonArray chargeOffReasonToGLAccountMappingArray = this.fromApiJsonHelper
.extractJsonArrayNamed(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(), element);
final Map<Long, Long> inputChargeOffReasonToGLAccountMap = new HashMap<>();
final Set<Long> existingChargeOffReasons = new HashSet<>();
if (chargeOffReasonToGLAccountMappingArray != null) {
if (changes != null) {
changes.put(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(),
command.jsonFragment(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue()));
}
for (int i = 0; i < chargeOffReasonToGLAccountMappingArray.size(); i++) {
final JsonObject jsonObject = chargeOffReasonToGLAccountMappingArray.get(i).getAsJsonObject();
final Long expenseGlAccountId = jsonObject.get(LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue()).getAsLong();
final Long chargeOffReasonCodeValueId = jsonObject
.get(LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue()).getAsLong();
inputChargeOffReasonToGLAccountMap.put(chargeOffReasonCodeValueId, expenseGlAccountId);
}
// If input map is empty, delete all existing mappings
if (inputChargeOffReasonToGLAccountMap.isEmpty()) {
this.accountMappingRepository.deleteAllInBatch(existingChargeOffReasonToGLAccountMappings);
} else {
for (final ProductToGLAccountMapping existingChargeOffReasonToGLAccountMapping : existingChargeOffReasonToGLAccountMappings) {
final Long currentChargeOffReasonId = existingChargeOffReasonToGLAccountMapping.getChargeOffReason().getId();
if (currentChargeOffReasonId != null) {
existingChargeOffReasons.add(currentChargeOffReasonId);
// update existing mappings (if required)
if (inputChargeOffReasonToGLAccountMap.containsKey(currentChargeOffReasonId)) {
final Long newGLAccountId = inputChargeOffReasonToGLAccountMap.get(currentChargeOffReasonId);
if (!newGLAccountId.equals(existingChargeOffReasonToGLAccountMapping.getGlAccount().getId())) {
final Optional<GLAccount> glAccount = accountRepository.findById(newGLAccountId);
if (glAccount.isPresent()) {
existingChargeOffReasonToGLAccountMapping.setGlAccount(glAccount.get());
this.accountMappingRepository.saveAndFlush(existingChargeOffReasonToGLAccountMapping);
}
}
} // deleted payment type
else {
this.accountMappingRepository.delete(existingChargeOffReasonToGLAccountMapping);
}
}
}
// only the newly added
for (Map.Entry<Long, Long> entry : inputChargeOffReasonToGLAccountMap.entrySet().stream()
.filter(e -> !existingChargeOffReasons.contains(e.getKey())).toList()) {
saveChargeOffReasonToExpenseMapping(productId, entry.getKey(), entry.getValue(), portfolioProductType);
}
}
}
}
/** /**
* @param productId * @param productId
* *
@@ -402,6 +489,26 @@ public class ProductToGLAccountMappingHelper {
this.accountMappingRepository.saveAndFlush(accountMapping); this.accountMappingRepository.saveAndFlush(accountMapping);
} }
private void saveChargeOffReasonToExpenseMapping(final Long productId, final Long reasonId, final Long expenseAccountId,
final PortfolioProductType portfolioProductType) {
final Optional<GLAccount> glAccount = accountRepository.findById(expenseAccountId);
final boolean reasonMappingExists = this.accountMappingRepository
.findAllChargeOffReasonsMappings(productId, portfolioProductType.getValue()).stream()
.anyMatch(mapping -> mapping.getChargeOffReason().getId().equals(reasonId));
final Optional<CodeValue> codeValueOptional = codeValueRepository.findById(reasonId);
if (glAccount.isPresent() && !reasonMappingExists && codeValueOptional.isPresent()) {
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount.get())
.setProductId(productId).setProductType(portfolioProductType.getValue())
.setFinancialAccountType(CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue()).setChargeOffReason(codeValueOptional.get());
this.accountMappingRepository.saveAndFlush(accountMapping);
}
}
private List<GLAccountType> getAllowedAccountTypesForFeeMapping() { private List<GLAccountType> getAllowedAccountTypesForFeeMapping() {
List<GLAccountType> allowedAccountTypes = new ArrayList<>(); List<GLAccountType> allowedAccountTypes = new ArrayList<>();
allowedAccountTypes.add(GLAccountType.INCOME); allowedAccountTypes.add(GLAccountType.INCOME);
@@ -455,4 +562,38 @@ public class ProductToGLAccountMappingHelper {
this.accountMappingRepository.deleteAllInBatch(productToGLAccountMappings); this.accountMappingRepository.deleteAllInBatch(productToGLAccountMappings);
} }
} }
public void validateChargeOffMappingsInDatabase(final List<JsonObject> mappings) {
final List<ApiParameterError> validationErrors = new ArrayList<>();
for (JsonObject jsonObject : mappings) {
final Long expenseGlAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue(), jsonObject);
final Long chargeOffReasonCodeValueId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue(), jsonObject);
// Validation: chargeOffReasonCodeValueId must exist in the database
CodeValue codeValue = this.codeValueRepository.findByCodeNameAndId("ChargeOffReasons", chargeOffReasonCodeValueId);
if (codeValue == null) {
validationErrors.add(ApiParameterError.parameterError("validation.msg.chargeoffreason.invalid",
"Charge-off reason with ID " + chargeOffReasonCodeValueId + " does not exist",
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue()));
}
// Validation: expenseGLAccountId must exist as a valid Expense GL account
final Optional<GLAccount> glAccount = accountRepository.findById(expenseGlAccountId);
if (glAccount.isEmpty() || !glAccount.get().getType().equals(GL_ACCOUNT_EXPENSE_TYPE)) {
validationErrors.add(ApiParameterError.parameterError("validation.msg.glaccount.not.found",
"GL Account with ID " + expenseGlAccountId + " does not exist or is not an Expense GL account",
LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue()));
}
}
// Throw all collected validation errors, if any
if (!validationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(validationErrors);
}
}
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.accounting.producttoaccountmapping.service;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
@@ -47,4 +48,5 @@ public interface ProductToGLAccountMappingReadPlatformService {
List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId); List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId);
List<ChargeOffReasonToGLAccountMapper> fetchChargeOffReasonMappingsForLoanProduct(Long loanProductId);
} }
@@ -18,8 +18,6 @@
*/ */
package org.apache.fineract.accounting.producttoaccountmapping.service; package org.apache.fineract.accounting.producttoaccountmapping.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@@ -37,14 +35,16 @@ import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAc
import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingValidations; import org.apache.fineract.accounting.common.AccountingValidations;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.PortfolioProductType;
import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Slf4j @Slf4j
@@ -54,73 +54,24 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private static final class ProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> { private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository;
public String schema() {
return " mapping.id as id, mapping.gl_account_id as glAccountId,glaccount.name as name,glaccount.gl_code as code,"
+ " mapping.product_id as productId, mapping.product_type as productType,mapping.financial_account_type as financialAccountType, "
+ " mapping.payment_type as paymentTypeId,pt.value as paymentTypeValue, mapping.charge_id as chargeId, charge.is_penalty as penalty, "
+ " charge.name as chargeName "
+ " from acc_product_mapping mapping left join m_charge charge on mapping.charge_id=charge.id "
+ " left join acc_gl_account as glaccount on mapping.gl_account_id = glaccount.id"
+ " left join m_payment_type pt on mapping.payment_type=pt.id" + " where mapping.product_type= ? ";
}
@Override
public Map<String, Object> mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long glAccountId = rs.getLong("glAccountId");
final Long productId = rs.getLong("productId");
final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentTypeId");
final Long chargeId = rs.getLong("chargeId");
final Integer productType = rs.getInt("productType");
final String paymentTypeValue = rs.getString("paymentTypeValue");
final Integer financialAccountType = rs.getInt("financialAccountType");
final String glAccountName = rs.getString("name");
final String glCode = rs.getString("code");
final String chargeName = rs.getString("chargeName");
final Boolean penalty = rs.getBoolean("penalty");
final Map<String, Object> loanProductToGLAccountMap = new LinkedHashMap<>(5);
loanProductToGLAccountMap.put("id", id);
loanProductToGLAccountMap.put("glAccountId", glAccountId);
loanProductToGLAccountMap.put("productId", productId);
loanProductToGLAccountMap.put("productType", productType);
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId);
loanProductToGLAccountMap.put("paymentTypeValue", paymentTypeValue);
loanProductToGLAccountMap.put("chargeId", chargeId);
loanProductToGLAccountMap.put("chargeName", chargeName);
loanProductToGLAccountMap.put("penalty", penalty);
loanProductToGLAccountMap.put("glAccountName", glAccountName);
loanProductToGLAccountMap.put("glCode", glCode);
return loanProductToGLAccountMap;
}
}
@Override @Override
public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) { public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8); final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); final List<ProductToGLAccountMapping> mappings = productToGLAccountMappingRepository.findAllRegularMappings(loanProductId,
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null"; PortfolioProductType.LOAN.getValue());
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { PortfolioProductType.LOAN.getValue(), loanProductId });
if (AccountingValidations.isCashBasedAccounting(accountingType)) { if (AccountingValidations.isCashBasedAccounting(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) { for (final ProductToGLAccountMapping mapping : mappings) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); final CashAccountsForLoan glAccountForLoan = CashAccountsForLoan.fromInt(mapping.getFinancialAccountType());
final CashAccountsForLoan glAccountForLoan = CashAccountsForLoan.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForLoan.equals(CashAccountsForLoan.FUND_SOURCE)) { if (glAccountForLoan.equals(CashAccountsForLoan.FUND_SOURCE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData); accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData);
@@ -166,14 +117,11 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
} else if (AccountingValidations.isAccrualBasedAccounting(accountingType) } else if (AccountingValidations.isAccrualBasedAccounting(accountingType)
|| AccountingValidations.isUpfrontAccrualAccounting(accountingType)) { || AccountingValidations.isUpfrontAccrualAccounting(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) { for (ProductToGLAccountMapping mapping : mappings) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); final AccrualAccountsForLoan glAccountForLoan = AccrualAccountsForLoan.fromInt(mapping.getFinancialAccountType());
final AccrualAccountsForLoan glAccountForLoan = AccrualAccountsForLoan.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForLoan.equals(AccrualAccountsForLoan.FUND_SOURCE)) { if (glAccountForLoan.equals(AccrualAccountsForLoan.FUND_SOURCE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData); accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData);
@@ -229,18 +177,16 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
@Override @Override
public Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType) { public Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType) {
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null ";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR final List<ProductToGLAccountMapping> mappings = productToGLAccountMappingRepository.findAllRegularMappings(savingsProductId,
new Object[] { PortfolioProductType.SAVING.getValue(), savingsProductId }); PortfolioProductType.SAVING.getValue());
Map<String, Object> accountMappingDetails = null; Map<String, Object> accountMappingDetails = null;
if (AccountingValidations.isCashBasedAccounting(accountingType)) { if (AccountingValidations.isCashBasedAccounting(accountingType)) {
accountMappingDetails = setCashSavingsProductToGLAccountMaps(listOfProductToGLAccountMaps); accountMappingDetails = setCashSavingsProductToGLAccountMaps(mappings);
} else if (AccountingValidations.isAccrualPeriodicBasedAccounting(accountingType)) { } else if (AccountingValidations.isAccrualPeriodicBasedAccounting(accountingType)) {
accountMappingDetails = setAccrualPeriodicSavingsProductToGLAccountMaps(listOfProductToGLAccountMaps); accountMappingDetails = setAccrualPeriodicSavingsProductToGLAccountMaps(mappings);
} }
@@ -263,25 +209,15 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
*/ */
private List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappings(final PortfolioProductType portfolioProductType, private List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappings(final PortfolioProductType portfolioProductType,
final Long loanProductId) { final Long loanProductId) {
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); final List<ProductToGLAccountMapping> mappings = productToGLAccountMappingRepository.findAllPaymentTypeMappings(loanProductId,
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null"; portfolioProductType.getValue());
final List<Map<String, Object>> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = mappings.isEmpty() ? null : new ArrayList<>();
new Object[] { portfolioProductType.getValue(), loanProductId }); for (final ProductToGLAccountMapping mapping : mappings) {
final PaymentTypeData paymentTypeData = PaymentTypeData.instance(mapping.getPaymentType().getId(),
List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = null; mapping.getPaymentType().getName());
for (final Map<String, Object> productToGLAccountMap : paymentTypeToFundSourceMappingsList) { final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
if (paymentTypeToGLAccountMappers == null) { .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
paymentTypeToGLAccountMappers = new ArrayList<>();
}
final Long paymentTypeId = (Long) productToGLAccountMap.get("paymentTypeId");
final String paymentTypeValue = (String) productToGLAccountMap.get("paymentTypeValue");
final PaymentTypeData paymentTypeData = PaymentTypeData.instance(paymentTypeId, paymentTypeValue);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
final PaymentTypeToGLAccountMapper paymentTypeToGLAccountMapper = new PaymentTypeToGLAccountMapper() final PaymentTypeToGLAccountMapper paymentTypeToGLAccountMapper = new PaymentTypeToGLAccountMapper()
.setPaymentType(paymentTypeData).setFundSourceAccount(gLAccountData); .setPaymentType(paymentTypeData).setFundSourceAccount(gLAccountData);
@@ -312,29 +248,16 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
private List<ChargeToGLAccountMapper> fetchChargeToIncomeAccountMappings(final PortfolioProductType portfolioProductType, private List<ChargeToGLAccountMapper> fetchChargeToIncomeAccountMappings(final PortfolioProductType portfolioProductType,
final Long loanProductId, final boolean penalty) { final Long loanProductId, final boolean penalty) {
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); final List<ProductToGLAccountMapping> mappings = penalty
String sql = "select " + rm.schema() + " and product_id = ? and mapping.charge_id is not null and charge.is_penalty="; ? productToGLAccountMappingRepository.findAllPenaltyMappings(loanProductId, portfolioProductType.getValue())
if (penalty) { : productToGLAccountMappingRepository.findAllFeeMappings(loanProductId, portfolioProductType.getValue());
sql = sql + " true";
} else {
sql = sql + " false";
}
final List<Map<String, Object>> chargeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR List<ChargeToGLAccountMapper> chargeToGLAccountMappers = mappings.isEmpty() ? null : new ArrayList<>();
new Object[] { portfolioProductType.getValue(), loanProductId }); for (final ProductToGLAccountMapping mapping : mappings) {
List<ChargeToGLAccountMapper> chargeToGLAccountMappers = null; final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
for (final Map<String, Object> chargeToIncomeAccountMap : chargeToFundSourceMappingsList) { .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
if (chargeToGLAccountMappers == null) { final ChargeData chargeData = ChargeData.builder().id(mapping.getCharge().getId()).name(mapping.getCharge().getName())
chargeToGLAccountMappers = new ArrayList<>(); .penalty(mapping.getCharge().isPenalty()).build();
}
final Long glAccountId = (Long) chargeToIncomeAccountMap.get("glAccountId");
final String glAccountName = (String) chargeToIncomeAccountMap.get("glAccountName");
final String glCode = (String) chargeToIncomeAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
final Long chargeId = (Long) chargeToIncomeAccountMap.get("chargeId");
final String chargeName = (String) chargeToIncomeAccountMap.get("chargeName");
final Boolean penalty1 = (Boolean) chargeToIncomeAccountMap.get("penalty");
final ChargeData chargeData = ChargeData.builder().id(chargeId).name(chargeName).penalty(penalty1).build();
final ChargeToGLAccountMapper chargeToGLAccountMapper = new ChargeToGLAccountMapper().setCharge(chargeData) final ChargeToGLAccountMapper chargeToGLAccountMapper = new ChargeToGLAccountMapper().setCharge(chargeData)
.setIncomeAccount(gLAccountData); .setIncomeAccount(gLAccountData);
chargeToGLAccountMappers.add(chargeToGLAccountMapper); chargeToGLAccountMappers.add(chargeToGLAccountMapper);
@@ -342,26 +265,46 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
return chargeToGLAccountMappers; return chargeToGLAccountMappers;
} }
private List<ChargeOffReasonToGLAccountMapper> fetchChargeOffReasonMappings(final PortfolioProductType portfolioProductType,
final Long loanProductId) {
final List<ProductToGLAccountMapping> mappings = productToGLAccountMappingRepository.findAllChargeOffReasonsMappings(loanProductId,
portfolioProductType.getValue());
List<ChargeOffReasonToGLAccountMapper> chargeOffReasonToGLAccountMappers = mappings.isEmpty() ? null : new ArrayList<>();
for (final ProductToGLAccountMapping mapping : mappings) {
final Long glAccountId = mapping.getGlAccount().getId();
final String glAccountName = mapping.getGlAccount().getName();
final String glCode = mapping.getGlAccount().getGlCode();
final GLAccountData chargeOffExpenseAccount = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
final Long chargeOffReasonId = mapping.getChargeOffReason().getId();
final String codeValue = mapping.getChargeOffReason().getLabel();
final String codeDescription = mapping.getChargeOffReason().getDescription();
final Integer orderPosition = mapping.getChargeOffReason().getPosition();
final boolean isActive = mapping.getChargeOffReason().isActive();
final boolean isMandatory = mapping.getChargeOffReason().isMandatory();
final CodeValueData chargeOffReasonsCodeValue = CodeValueData.builder().id(chargeOffReasonId).name(codeValue)
.description(codeDescription).position(orderPosition).active(isActive).mandatory(isMandatory).build();
final ChargeOffReasonToGLAccountMapper chargeOffReasonToGLAccountMapper = new ChargeOffReasonToGLAccountMapper()
.setChargeOffReasonCodeValue(chargeOffReasonsCodeValue).setExpenseAccount(chargeOffExpenseAccount);
chargeOffReasonToGLAccountMappers.add(chargeOffReasonToGLAccountMapper);
}
return chargeOffReasonToGLAccountMappers;
}
@Override @Override
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) { public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8); final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); final List<ProductToGLAccountMapping> mappings = productToGLAccountMappingRepository.findAllRegularMappings(productId,
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null "; PortfolioProductType.SHARES.getValue());
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { PortfolioProductType.SHARES.getValue(), productId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) { if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) { for (final ProductToGLAccountMapping mapping : mappings) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); final CashAccountsForShares glAccountForShares = CashAccountsForShares.fromInt(mapping.getFinancialAccountType());
final CashAccountsForShares glAccountForShares = CashAccountsForShares.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForShares.equals(CashAccountsForShares.SHARES_REFERENCE)) { if (glAccountForShares.equals(CashAccountsForShares.SHARES_REFERENCE)) {
accountMappingDetails.put(SharesProductAccountingParams.SHARES_REFERENCE.getValue(), gLAccountData); accountMappingDetails.put(SharesProductAccountingParams.SHARES_REFERENCE.getValue(), gLAccountData);
@@ -388,20 +331,21 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
return fetchChargeToIncomeAccountMappings(PortfolioProductType.SHARES, productId, false); return fetchChargeToIncomeAccountMappings(PortfolioProductType.SHARES, productId, false);
} }
private Map<String, Object> setAccrualPeriodicSavingsProductToGLAccountMaps( @Override
final List<Map<String, Object>> listOfProductToGLAccountMaps) { public List<ChargeOffReasonToGLAccountMapper> fetchChargeOffReasonMappingsForLoanProduct(Long loanProductId) {
return fetchChargeOffReasonMappings(PortfolioProductType.LOAN, loanProductId);
}
private Map<String, Object> setAccrualPeriodicSavingsProductToGLAccountMaps(final List<ProductToGLAccountMapping> mappings) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8); final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) { for (final ProductToGLAccountMapping mapping : mappings) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); AccrualAccountsForSavings glAccountForSavings = AccrualAccountsForSavings.fromInt(mapping.getFinancialAccountType());
AccrualAccountsForSavings glAccountForSavings = AccrualAccountsForSavings.fromInt(financialAccountType);
if (glAccountForSavings != null) { if (glAccountForSavings != null) {
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); final GLAccountData glAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData glAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
// Assets // Assets
if (glAccountForSavings.equals(AccrualAccountsForSavings.SAVINGS_REFERENCE)) { if (glAccountForSavings.equals(AccrualAccountsForSavings.SAVINGS_REFERENCE)) {
@@ -435,26 +379,23 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), glAccountData); accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), glAccountData);
} }
} else { } else {
log.error("Accounting mapping null {}", financialAccountType); log.error("Accounting mapping null {}", mapping.getFinancialAccountType());
} }
} }
return accountMappingDetails; return accountMappingDetails;
} }
private Map<String, Object> setCashSavingsProductToGLAccountMaps(final List<Map<String, Object>> listOfProductToGLAccountMaps) { private Map<String, Object> setCashSavingsProductToGLAccountMaps(final List<ProductToGLAccountMapping> mappings) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8); final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) { for (final ProductToGLAccountMapping mapping : mappings) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); CashAccountsForSavings glAccountForSavings = CashAccountsForSavings.fromInt(mapping.getFinancialAccountType());
CashAccountsForSavings glAccountForSavings = CashAccountsForSavings.fromInt(financialAccountType);
if (glAccountForSavings != null) { if (glAccountForSavings != null) {
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); final GLAccountData glAccountData = new GLAccountData().setId(mapping.getGlAccount().getId())
final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData glAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
// Assets // Assets
if (glAccountForSavings.equals(CashAccountsForSavings.SAVINGS_REFERENCE)) { if (glAccountForSavings.equals(CashAccountsForSavings.SAVINGS_REFERENCE)) {
@@ -482,7 +423,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), glAccountData); accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), glAccountData);
} }
} else { } else {
log.error("Accounting mapping null {}", financialAccountType); log.error("Accounting mapping null {}", mapping.getFinancialAccountType());
} }
} }
@@ -19,6 +19,7 @@
package org.apache.fineract.accounting.producttoaccountmapping.service; package org.apache.fineract.accounting.producttoaccountmapping.service;
import java.util.Map; import java.util.Map;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -29,7 +30,7 @@ public interface ProductToGLAccountMappingWritePlatformService {
void createSavingProductToGLAccountMapping(Long savingProductId, JsonCommand command, DepositAccountType accountType); void createSavingProductToGLAccountMapping(Long savingProductId, JsonCommand command, DepositAccountType accountType);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged, Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId); AccountingRuleType accountingRuleTypeId);
Map<String, Object> updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged, Map<String, Object> updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId, DepositAccountType accountType); int accountingRuleTypeId, DepositAccountType accountType);
@@ -29,6 +29,7 @@ import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper; import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.PortfolioProductType;
@@ -42,9 +43,9 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
public SavingsProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository, public SavingsProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper, final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper, final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) { final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper, final CodeValueRepository codeValueRepository) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper, super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper); paymentTypeRepositoryWrapper, codeValueRepository);
} }
/*** /***
@@ -28,6 +28,7 @@ import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper; import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.PortfolioProductType;
@@ -41,9 +42,9 @@ public class ShareProductToGLAccountMappingHelper extends ProductToGLAccountMapp
public ShareProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository, public ShareProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper, final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper, final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) { final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper, final CodeValueRepository codeValueRepository) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper, super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper); paymentTypeRepositoryWrapper, codeValueRepository);
} }
/*** /***
@@ -18,14 +18,15 @@
*/ */
package org.apache.fineract.accounting.provisioning.api; package org.apache.fineract.accounting.provisioning.api;
import static org.apache.fineract.accounting.provisioning.constant.ProvisioningEntriesApiConstants.CREATE_JOURNAL_ENTRY;
import static org.apache.fineract.accounting.provisioning.constant.ProvisioningEntriesApiConstants.RECREATE_PROVISION_IN_ENTRY;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
@@ -34,24 +35,17 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData; import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData; import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.request.ProvisionEntryRequest;
import org.apache.fineract.accounting.provisioning.service.ProvisioningEntriesReadPlatformService; import org.apache.fineract.accounting.provisioning.service.ProvisioningEntriesReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
@@ -60,38 +54,42 @@ import org.springframework.stereotype.Component;
@Path("/v1/provisioningentries") @Path("/v1/provisioningentries")
@Component @Component
@Tag(name = "Provisioning Entries", description = "This defines the Provisioning Entries for all active loan products\n" + "\n" @Tag(name = "Provisioning Entries", description = """
+ "Field Descriptions\n" + "date\n" + "Date on which day provisioning entries should be created\n" + "createjournalentries\n" This defines the Provisioning Entries for all active loan products
+ "Boolean variable whether to add journal entries for generated provisioning entries\n")
Field Descriptions
date
Date on which day provisioning entries should be created
createjournalentries
Boolean variable whether to add journal entries for generated provisioning entries
""")
@RequiredArgsConstructor @RequiredArgsConstructor
public class ProvisioningEntriesApiResource { public class ProvisioningEntriesApiResource {
private static final Set<String> PROVISIONING_ENTRY_PARAMETERS = new HashSet<>(
Arrays.asList(ProvisioningEntriesApiConstants.PROVISIONINGENTRY_PARAM, ProvisioningEntriesApiConstants.ENTRIES_PARAM));
private static final Set<String> ALL_PROVISIONING_ENTRIES = new HashSet<>(
Arrays.asList(ProvisioningEntriesApiConstants.PROVISIONINGENTRY_PARAM));
private final PlatformSecurityContext platformSecurityContext; private final PlatformSecurityContext platformSecurityContext;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<ProvisioningEntryData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<ProvisioningEntryData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<Object> entriesApiJsonSerializer;
private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService; private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create new Provisioning Entries", description = "Creates a new Provisioning Entries\n" + "\n" @Operation(summary = "Create new Provisioning Entries", description = """
+ "Mandatory Fields\n" + "date\n" + "dateFormat\n" + "locale\n" + "Optional Fields\n" + "createjournalentries") Creates a new Provisioning Entries
@RequestBody(content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PostProvisioningEntriesRequest.class)))
@ApiResponses({ Mandatory Fields
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PostProvisioningEntriesResponse.class))) }) date
public String createProvisioningEntries(@Parameter(hidden = true) final String apiRequestBodyAsJson) { dateFormat
CommandWrapper commandWrapper = null; locale
this.platformSecurityContext.authenticatedUser(); Optional Fields
commandWrapper = new CommandWrapperBuilder().createProvisioningEntries().withJson(apiRequestBodyAsJson).build(); createjournalentries""")
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); @RequestBody(content = @Content(schema = @Schema(implementation = ProvisionEntryRequest.class)))
return this.toApiJsonSerializer.serialize(commandProcessingResult); @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PostProvisioningEntriesResponse.class)))
public CommandProcessingResult createProvisioningEntries(@Parameter(hidden = true) ProvisionEntryRequest provisionEntryRequest) {
platformSecurityContext.authenticatedUser();
CommandWrapper commandWrapper = new CommandWrapperBuilder().createProvisioningEntries()
.withJson(toApiJsonSerializer.serialize(provisionEntryRequest)).build();
return commandsSourceWritePlatformService.logCommandSource(commandWrapper);
} }
@POST @POST
@@ -100,25 +98,12 @@ public class ProvisioningEntriesApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Recreates Provisioning Entry", description = "Recreates Provisioning Entry | createjournalentry.") @Operation(summary = "Recreates Provisioning Entry", description = "Recreates Provisioning Entry | createjournalentry.")
@RequestBody(content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesResponse.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesResponse.class))) }) public CommandProcessingResult modifyProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId,
public String modifyProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId,
@QueryParam("command") @Parameter(description = "command=createjournalentry\ncommand=recreateprovisioningentry") final String commandParam, @QueryParam("command") @Parameter(description = "command=createjournalentry\ncommand=recreateprovisioningentry") final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) { @Parameter(hidden = true) String provisionCommandRequest) {
CommandWrapper commandWrapper = null; platformSecurityContext.authenticatedUser();
this.platformSecurityContext.authenticatedUser(); return getResultByCommandParam(commandParam, entryId, provisionCommandRequest);
if ("createjournalentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().createProvisioningJournalEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService
.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
} else if ("recreateprovisioningentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().reCreateProvisioningEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService
.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
throw new UnrecognizedQueryParamException("command", commandParam);
} }
@GET @GET
@@ -126,45 +111,49 @@ public class ProvisioningEntriesApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieves a Provisioning Entry", description = "Returns the details of a generated Provisioning Entry.") @Operation(summary = "Retrieves a Provisioning Entry", description = "Returns the details of a generated Provisioning Entry.")
@ApiResponses({ public ProvisioningEntryData retrieveProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId) {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntryData.class))) })
public String retrieveProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId,
@Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser(); platformSecurityContext.authenticatedUser();
ProvisioningEntryData data = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntryData(entryId); return provisioningEntriesReadPlatformService.retrieveProvisioningEntryData(entryId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, PROVISIONING_ENTRY_PARAMETERS);
} }
@GET @GET
@Path("entries") @Path("entries")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@ApiResponses({ public Page<LoanProductProvisioningEntryData> retrieveProviioningEntries(@QueryParam("entryId") final Long entryId,
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanProductProvisioningEntryData.class))) }) @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
public String retrieveProviioningEntries(@QueryParam("entryId") final Long entryId, @QueryParam("offset") final Integer offset, @QueryParam("officeId") final Long officeId, @QueryParam("productId") final Long productId,
@QueryParam("limit") final Integer limit, @QueryParam("officeId") final Long officeId, @QueryParam("categoryId") final Long categoryId) {
@QueryParam("productId") final Long productId, @QueryParam("categoryId") final Long categoryId, platformSecurityContext.authenticatedUser();
@Context final UriInfo uriInfo) { final SearchParameters params = SearchParameters.builder().limit(limit).offset(offset).provisioningEntryId(entryId)
this.platformSecurityContext.authenticatedUser(); .officeId(officeId).productId(productId).categoryId(categoryId).build();
SearchParameters params = SearchParameters.builder().limit(limit).offset(offset).provisioningEntryId(entryId).officeId(officeId)
.productId(productId).categoryId(categoryId).build(); return provisioningEntriesReadPlatformService.retrieveProvisioningEntries(params);
Page<LoanProductProvisioningEntryData> entries = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntries(params);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.entriesApiJsonSerializer.serialize(settings, entries, PROVISIONING_ENTRY_PARAMETERS);
} }
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List all Provisioning Entries") @Operation(summary = "List all Provisioning Entries")
@ApiResponses({ public Page<ProvisioningEntryData> retrieveAllProvisioningEntries(
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProvisioningEntryData.class)))) }) @QueryParam("offset") @Parameter(description = "offset") final Integer offset,
public String retrieveAllProvisioningEntries(@QueryParam("offset") @Parameter(description = "offset") final Integer offset, @QueryParam("limit") @Parameter(description = "limit") final Integer limit) {
@QueryParam("limit") @Parameter(description = "limit") final Integer limit, @Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser(); platformSecurityContext.authenticatedUser();
Page<ProvisioningEntryData> data = this.provisioningEntriesReadPlatformService.retrieveAllProvisioningEntries(offset, limit); return provisioningEntriesReadPlatformService.retrieveAllProvisioningEntries(offset, limit);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); }
return this.entriesApiJsonSerializer.serialize(settings, data, ALL_PROVISIONING_ENTRIES);
private CommandProcessingResult getResultByCommandParam(String commandParam, Long entryId, String apiRequestBodyAsJson) {
final CommandWrapperBuilder commandWrapperBuilder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
switch (commandParam) {
case CREATE_JOURNAL_ENTRY -> {
return commandsSourceWritePlatformService
.logCommandSource(commandWrapperBuilder.createProvisioningJournalEntries(entryId).build());
}
case RECREATE_PROVISION_IN_ENTRY -> {
return commandsSourceWritePlatformService
.logCommandSource(commandWrapperBuilder.reCreateProvisioningEntries(entryId).build());
}
default -> throw new UnrecognizedQueryParamException("command", commandParam);
}
} }
} }
@@ -32,4 +32,7 @@ public interface ProvisioningEntriesApiConstants {
String ENTRIES_PARAM = "entries"; String ENTRIES_PARAM = "entries";
String CREATE_JOURNAL_ENTRY = "createjournalentry";
String RECREATE_PROVISION_IN_ENTRY = "recreateprovisioningentry";
} }
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.accounting.provisioning.data; package org.apache.fineract.accounting.provisioning.data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collection; import java.util.Collection;
@@ -29,7 +31,10 @@ import lombok.experimental.Accessors;
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ProvisioningEntryData { public class ProvisioningEntryData implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long id; private Long id;
@@ -39,9 +44,9 @@ public class ProvisioningEntryData {
private String createdUser; private String createdUser;
LocalDate createdDate; private LocalDate createdDate;
Long modifiedById; private Long modifiedById;
private String modifiedUser; private String modifiedUser;
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.data.request;
import java.io.Serial;
import java.io.Serializable;
public record ProvisionEntryRequest(String date, Boolean createjournalentries, String locale, String dateFormat) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
@@ -20,7 +20,6 @@ package org.apache.fineract.accounting.rule.api;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
@@ -39,10 +38,8 @@ import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -52,6 +49,7 @@ import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService; import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
import org.apache.fineract.accounting.rule.data.AccountingRuleData; import org.apache.fineract.accounting.rule.data.AccountingRuleData;
import org.apache.fineract.accounting.rule.data.AccountingTagRuleData; import org.apache.fineract.accounting.rule.data.AccountingTagRuleData;
import org.apache.fineract.accounting.rule.data.request.AccountRuleRequest;
import org.apache.fineract.accounting.rule.service.AccountingRuleReadPlatformService; import org.apache.fineract.accounting.rule.service.AccountingRuleReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
@@ -81,11 +79,6 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AccountingRuleApiResource { public class AccountingRuleApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("id", "officeId", "officeName", "accountToDebitId", "accountToCreditId", "name", "description", "systemDefined",
"allowedCreditTagOptions", "allowedDebitTagOptions", "debitTags", "creditTags", "creditAccounts", "debitAccounts",
"allowMultipleCreditEntries", "allowMultipleDebitEntries", "tag"));
private static final String RESOURCE_NAME_FOR_PERMISSION = "ACCOUNTINGRULE"; private static final String RESOURCE_NAME_FOR_PERMISSION = "ACCOUNTINGRULE";
private final AccountingRuleReadPlatformService accountingRuleReadPlatformService; private final AccountingRuleReadPlatformService accountingRuleReadPlatformService;
@@ -103,17 +96,9 @@ public class AccountingRuleApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Accounting Rule Details Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n" @Operation(summary = "Retrieve Accounting Rule Details Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n"
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n" + "Example Request:\n" + "\n" + "accountingrules/template") + "\n" + "Field Defaults\n" + "Allowed Value Lists\n" + "Example Request:\n" + "\n" + "accountingrules/template")
@ApiResponses({ public AccountingRuleData retrieveTemplate() {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.GetAccountRulesTemplateResponse.class))) }) context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
public String retrieveTemplate(@Context final UriInfo uriInfo) { return handleTemplate(null);
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
AccountingRuleData accountingRuleData = null;
accountingRuleData = handleTemplate(accountingRuleData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@@ -121,30 +106,16 @@ public class AccountingRuleApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Accounting Rules", description = "Returns the list of defined accounting rules.\n" + "\n" @Operation(summary = "Retrieve Accounting Rules", description = "Returns the list of defined accounting rules.\n" + "\n"
+ "Example Requests:\n" + "\n" + "accountingrules") + "Example Requests:\n" + "\n" + "accountingrules")
@ApiResponses({ public List<AccountingRuleData> retrieveAllAccountingRules(@Context final UriInfo uriInfo) {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.GetAccountRulesResponse.class)))) }) final AppUser currentUser = context.authenticatedUser();
public String retrieveAllAccountingRules(@Context final UriInfo uriInfo) {
final AppUser currentUser = this.context.authenticatedUser();
currentUser.validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); currentUser.validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%"; final String hierarchySearchString = hierarchy + "%";
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
boolean isAssociationParametersExists = false; boolean isAssociationParametersExists = !associationParameters.isEmpty() && associationParameters.contains("all");
if (!associationParameters.isEmpty()) { return accountingRuleReadPlatformService.retrieveAllAccountingRules(hierarchySearchString, isAssociationParametersExists);
if (associationParameters.contains("all")) {
isAssociationParametersExists = true; // If true, retrieve
// additional fields for
// journal entry form.
}
}
final List<AccountingRuleData> accountingRuleDatas = this.accountingRuleReadPlatformService
.retrieveAllAccountingRules(hierarchySearchString, isAssociationParametersExists);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, accountingRuleDatas, RESPONSE_DATA_PARAMETERS);
} }
@GET @GET
@@ -153,21 +124,15 @@ public class AccountingRuleApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Accounting rule", description = "Returns the details of a defined Accounting rule.\n" + "\n" @Operation(summary = "Retrieve a Accounting rule", description = "Returns the details of a defined Accounting rule.\n" + "\n"
+ "Example Requests:\n" + "\n" + "accountingrules/1") + "Example Requests:\n" + "\n" + "accountingrules/1")
@ApiResponses({ public AccountingRuleData retreiveAccountingRule(
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleData.class))) })
public String retreiveAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId, @PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION); context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
AccountingRuleData accountingRuleData = this.accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId); final AccountingRuleData accountingRuleData = accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId);
if (settings.isTemplate()) { return settings.isTemplate() ? handleTemplate(accountingRuleData) : accountingRuleData;
accountingRuleData = handleTemplate(accountingRuleData);
}
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
} }
@POST @POST
@@ -176,16 +141,14 @@ public class AccountingRuleApiResource {
@Operation(summary = "Create/Define a Accounting rule", description = "Define a new Accounting rule.\n" + "\n" + "Mandatory Fields\n" @Operation(summary = "Create/Define a Accounting rule", description = "Define a new Accounting rule.\n" + "\n" + "Mandatory Fields\n"
+ "name, officeId,\n" + "accountToDebit OR debitTags,\n" + "accountToCredit OR creditTags.\n" + "\n" + "Optional Fields\n" + "name, officeId,\n" + "accountToDebit OR debitTags,\n" + "accountToCredit OR creditTags.\n" + "\n" + "Optional Fields\n"
+ "description") + "description")
@RequestBody(content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PostAccountingRulesRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = AccountRuleRequest.class)))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PostAccountingRulesResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PostAccountingRulesResponse.class))) })
public String createAccountingRule(@Parameter(hidden = true) final String jsonRequestBody) { public CommandProcessingResult createAccountingRule(@Parameter(hidden = true) AccountRuleRequest accountRuleRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountingRule()
.withJson(apiJsonSerializerService.serialize(accountRuleRequest)).build();
final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountingRule().withJson(jsonRequestBody).build(); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@PUT @PUT
@@ -193,18 +156,16 @@ public class AccountingRuleApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Accounting Rule", description = "Updates the details of a Accounting rule.") @Operation(summary = "Update a Accounting Rule", description = "Updates the details of a Accounting rule.")
@RequestBody(content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PutAccountingRulesRequest.class))) @RequestBody(content = @Content(schema = @Schema(implementation = AccountRuleRequest.class)))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PutAccountingRulesResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PutAccountingRulesResponse.class))) })
public String updateAccountingRule( public CommandProcessingResult updateAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId, @PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId,
@Parameter(hidden = true) final String jsonRequestBody) { @Parameter(hidden = true) AccountRuleRequest accountRuleRequest) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateAccountingRule(accountingRuleId).withJson(jsonRequestBody) final CommandWrapper commandRequest = new CommandWrapperBuilder().updateAccountingRule(accountingRuleId)
.build(); .withJson(apiJsonSerializerService.serialize(accountRuleRequest)).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
@DELETE @DELETE
@@ -214,55 +175,38 @@ public class AccountingRuleApiResource {
@Operation(summary = "Delete a Accounting Rule", description = "Deletes a Accounting rule.") @Operation(summary = "Delete a Accounting Rule", description = "Deletes a Accounting rule.")
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.DeleteAccountingRulesResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.DeleteAccountingRulesResponse.class))) })
public String deleteAccountingRule( public CommandProcessingResult deleteAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId) { @PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteAccountingRule(accountingRuleId).build(); final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteAccountingRule(accountingRuleId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
} }
private AccountingRuleData handleTemplate(AccountingRuleData accountingRuleData) { private AccountingRuleData handleTemplate(AccountingRuleData accountingRuleData) {
final List<GLAccountData> allowedAccounts = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(); final List<GLAccountData> allowedAccounts = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
final List<OfficeData> allowedOffices = (List<OfficeData>) this.officeReadPlatformService.retrieveAllOfficesForDropdown(); final List<OfficeData> allowedOffices = (List<OfficeData>) officeReadPlatformService.retrieveAllOfficesForDropdown();
final Collection<CodeValueData> allowedTagOptions = this.codeValueReadPlatformService final Collection<CodeValueData> allowedTagOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME); .retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
allowedTagOptions allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME)); .addAll(codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME));
allowedTagOptions allowedTagOptions.addAll(codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME));
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME)); allowedTagOptions.addAll(codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME));
allowedTagOptions allowedTagOptions.addAll(codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME));
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME));
allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME));
if (accountingRuleData == null) { if (accountingRuleData == null) {
final Collection<CodeValueData> allowedCreditTagOptions = allowedTagOptions;
final Collection<CodeValueData> allowedDebitTagOptions = allowedTagOptions;
accountingRuleData = new AccountingRuleData().setAllowedOffices(allowedOffices).setAllowedAccounts(allowedAccounts) accountingRuleData = new AccountingRuleData().setAllowedOffices(allowedOffices).setAllowedAccounts(allowedAccounts)
.setAllowedCreditTagOptions(allowedCreditTagOptions).setAllowedDebitTagOptions(allowedDebitTagOptions); .setAllowedCreditTagOptions(allowedTagOptions).setAllowedDebitTagOptions(allowedTagOptions);
} else { } else {
final Collection<CodeValueData> allowedCreditTagOptions; final Collection<CodeValueData> allowedCreditTagOptions = accountingRuleData.getCreditTags() != null
final Collection<CodeValueData> allowedDebitTagOptions; ? retrieveSelectedTags(allowedTagOptions, accountingRuleData.getCreditTags())
: allowedTagOptions;
if (accountingRuleData.getCreditTags() != null) { final Collection<CodeValueData> allowedDebitTagOptions = accountingRuleData.getDebitTags() != null
allowedCreditTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getCreditTags()); ? retrieveSelectedTags(allowedTagOptions, accountingRuleData.getDebitTags())
} else { : allowedTagOptions;
allowedCreditTagOptions = allowedTagOptions;
}
if (accountingRuleData.getDebitTags() != null) {
allowedDebitTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getDebitTags());
} else {
allowedDebitTagOptions = allowedTagOptions;
}
accountingRuleData = new AccountingRuleData().setId(accountingRuleData.getId()).setOfficeId(accountingRuleData.getOfficeId()) accountingRuleData = new AccountingRuleData().setId(accountingRuleData.getId()).setOfficeId(accountingRuleData.getOfficeId())
.setOfficeName(accountingRuleData.getOfficeName()).setName(accountingRuleData.getName()) .setOfficeName(accountingRuleData.getOfficeName()).setName(accountingRuleData.getName())
@@ -19,11 +19,6 @@
package org.apache.fineract.accounting.rule.api; package org.apache.fineract.accounting.rule.api;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.rule.data.AccountingTagRuleData;
import org.apache.fineract.organisation.office.data.OfficeData;
/** /**
* Created by sanyam on 26/7/17. * Created by sanyam on 26/7/17.
@@ -34,57 +29,6 @@ final class AccountingRuleApiResourceSwagger {
// For Swagger Documentation // For Swagger Documentation
} }
@Schema(description = "GetAccountRulesResponse")
public static final class GetAccountRulesResponse {
private GetAccountRulesResponse() {
}
public Long id;
public Long officeId;
public String officeName;
public String name;
public String description;
public boolean systemDefined;
public boolean allowMultipleDebitEntries;
public boolean allowMultipleCreditEntries;
public List<AccountingTagRuleData> creditTags;
public List<AccountingTagRuleData> debitTags;
}
@Schema(description = "GetAccountRulesTemplateResponse")
public static final class GetAccountRulesTemplateResponse {
private GetAccountRulesTemplateResponse() {
}
@Schema(example = "false")
public boolean systemDefined;
public List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
public List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
}
@Schema(description = "PostAccountingRulesRequest")
public static final class PostAccountingRulesRequest {
private PostAccountingRulesRequest() {
}
@Schema(example = "test")
public String name;
@Schema(example = "1")
public Long officeId;
@Schema(example = "21")
public Long accountToDebit;
@Schema(example = "9")
public Long accountToCredit;
@Schema(example = "Employee salary")
public String description;
}
@Schema(description = "PostAccountingRulesResponse") @Schema(description = "PostAccountingRulesResponse")
public static final class PostAccountingRulesResponse { public static final class PostAccountingRulesResponse {
@@ -98,17 +42,6 @@ final class AccountingRuleApiResourceSwagger {
public Long resourceId; public Long resourceId;
} }
@Schema(description = "PutAccountingRulesRequest")
public static final class PutAccountingRulesRequest {
private PutAccountingRulesRequest() {
}
@Schema(example = "Employee Salary posting rule")
public String name;
}
@Schema(description = "PutAccountingRulesResponse") @Schema(description = "PutAccountingRulesResponse")
public static final class PutAccountingRulesResponse { public static final class PutAccountingRulesResponse {
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.data.request;
import java.io.Serial;
import java.io.Serializable;
public record AccountRuleRequest(String name, Long officeId, Long accountToDebit, Long accountToCredit,
String description) implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}
+64 -11
View File
@@ -26,18 +26,39 @@ apply plugin: 'com.github.davidmc24.gradle.plugin.avro-base'
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
task preprocessAvroSchemas() { abstract class PreprocessAvroSchemasTask extends DefaultTask {
doLast { @InputDirectory
copy { abstract DirectoryProperty getInputDir()
from "$projectDir/src/main/avro"
into "$buildDir/generated/avro/src/main/avro" @InputFile
filter { line -> abstract RegularFileProperty getBigDecimalTemplate()
line.replaceAll("\"bigdecimal\"", new File("$projectDir/src/main/resources/avro-templates/bigdecimal.avsc").getText("UTF-8"))
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@TaskAction
def preprocess() {
def template = getBigDecimalTemplate().get().asFile.getText("UTF-8")
def input = getInputDir().get().asFile
def output = getOutputDir().get().asFile
input.eachFileRecurse { file ->
if (file.isFile()) {
def relativePath = input.toPath().relativize(file.toPath())
def targetFile = output.toPath().resolve(relativePath).toFile()
targetFile.parentFile.mkdirs()
targetFile.text = file.text.replaceAll("\"bigdecimal\"", template)
} }
} }
} }
} }
tasks.register('preprocessAvroSchemas', PreprocessAvroSchemasTask) {
inputDir = file("$projectDir/src/main/avro")
bigDecimalTemplate = file("$projectDir/src/main/resources/avro-templates/bigdecimal.avsc")
outputDir = file("$buildDir/generated/avro/src/main/avro")
}
task buildJavaSdk(type: GenerateAvroJavaTask) { task buildJavaSdk(type: GenerateAvroJavaTask) {
source("$buildDir/generated/avro/src/main/avro") source("$buildDir/generated/avro/src/main/avro")
outputDir = file("$buildDir/generated/java/src/main/java") outputDir = file("$buildDir/generated/java/src/main/java")
@@ -53,7 +74,33 @@ spotless {
} }
buildJavaSdk.dependsOn(preprocessAvroSchemas, spotlessJsonApply) buildJavaSdk.dependsOn(preprocessAvroSchemas, spotlessJsonApply)
compileJava.dependsOn(buildJavaSdk, licenseFormatMain)
sourceSets {
main {
java {
srcDir file("$buildDir/generated/java/src/main/java")
}
resources {
srcDir file("$projectDir/src/main/resources")
srcDir file("$projectDir/src/main/avro")
}
output.dir(file("$buildDir/generated/java/src/main/java"), builtBy: 'buildJavaSdk')
}
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from sourceSets.main.output
from("$buildDir/generated/java/src/main/java") {
include "**/*.java"
}
}
compileJava {
dependsOn buildJavaSdk
source = sourceSets.main.java.srcDirs
options.compilerArgs += ['-parameters']
}
java { java {
// keep this at Java 8, not 17 // keep this at Java 8, not 17
@@ -61,7 +108,10 @@ java {
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile).configureEach {
options.errorprone {
enabled = false // Disable ErrorProne for this module since it contains generated code
}
options.compilerArgs -= ["-Werror"] options.compilerArgs -= ["-Werror"]
} }
@@ -74,7 +124,10 @@ test {
useJUnitPlatform() useJUnitPlatform()
} }
sourceSets.main.java.srcDir new File(buildDir, "generated/java/src/main/java")
licenseFormatMain.dependsOn buildJavaSdk licenseFormatMain.dependsOn buildJavaSdk
licenseMain.dependsOn licenseFormatMain licenseMain.dependsOn licenseFormatMain
tasks.named('sourcesJar') {
dependsOn tasks.named('buildJavaSdk')
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
@@ -6,7 +6,7 @@
{ {
"name": "id", "name": "id",
"doc": "The ID of the message to be sent", "doc": "The ID of the message to be sent",
"type": "int" "type": "long"
}, },
{ {
"name": "type", "name": "type",
@@ -6,7 +6,7 @@
{ {
"name": "id", "name": "id",
"doc": "The ID of the message to be sent", "doc": "The ID of the message to be sent",
"type": "int" "type": "long"
}, },
{ {
"name": "source", "name": "source",
@@ -0,0 +1,31 @@
{
"name": "StringEnumOptionDataV1",
"namespace": "org.apache.fineract.avro.generic.v1",
"type": "record",
"fields": [
{
"default": null,
"name": "id",
"type": [
"null",
"string"
]
},
{
"default": null,
"name": "code",
"type": [
"null",
"string"
]
},
{
"default": null,
"name": "value",
"type": [
"null",
"string"
]
}
]
}
@@ -607,6 +607,17 @@
} }
] ]
}, },
{
"default": null,
"name": "loanTermVariations",
"type": [
"null",
{
"type": "array",
"items": "org.apache.fineract.avro.loan.v1.LoanTermVariationsDataV1"
}
]
},
{ {
"default": null, "default": null,
"name": "clientActiveLoanOptions", "name": "clientActiveLoanOptions",
@@ -709,6 +720,14 @@
"org.apache.fineract.avro.generic.v1.EnumOptionDataV1" "org.apache.fineract.avro.generic.v1.EnumOptionDataV1"
] ]
}, },
{
"default": null,
"name": "daysInYearCustomStrategy",
"type": [
"null",
"org.apache.fineract.avro.generic.v1.StringEnumOptionDataV1"
]
},
{ {
"default": null, "default": null,
"name": "isInterestRecalculationEnabled", "name": "isInterestRecalculationEnabled",
@@ -772,6 +791,57 @@
"null", "null",
"boolean" "boolean"
] ]
},
{
"default": null,
"name": "enableInstallmentLevelDelinquency",
"type": [
"null",
"boolean"
]
},
{
"default": null,
"name": "enableDownPayment",
"type": [
"null",
"boolean"
]
},
{
"default": null,
"name": "disbursedAmountPercentageForDownPayment",
"type": [
"null",
"bigdecimal"
]
},
{
"default": null,
"name": "enableAutoRepaymentForDownPayment",
"type": [
"null",
"boolean"
]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
},
{
"default": null,
"name": "customData",
"type": [
"null",
{
"values": "bytes",
"type": "map"
}
]
} }
] ]
} }
@@ -256,6 +256,17 @@
"null", "null",
"string" "string"
] ]
},
{
"default": null,
"name": "customData",
"type": [
"null",
{
"values": "bytes",
"type": "map"
}
]
} }
] ]
} }
@@ -146,6 +146,14 @@
"null", "null",
"boolean" "boolean"
] ]
},
{
"default": null,
"name": "disallowInterestCalculationOnPastDue",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -31,6 +31,14 @@
"string" "string"
] ]
}, },
{
"default": null,
"name": "transferExternalGroupId",
"type": [
"null",
"string"
]
},
{ {
"default": null, "default": null,
"name": "submittedDate", "name": "submittedDate",
@@ -419,6 +419,14 @@
"org.apache.fineract.avro.generic.v1.EnumOptionDataV1" "org.apache.fineract.avro.generic.v1.EnumOptionDataV1"
] ]
}, },
{
"default": null,
"name": "daysInYearCustomStrategy",
"type": [
"null",
"org.apache.fineract.avro.generic.v1.StringEnumOptionDataV1"
]
},
{ {
"default": null, "default": null,
"name": "isInterestRecalculationEnabled", "name": "isInterestRecalculationEnabled",
@@ -641,6 +649,14 @@
"null", "null",
"org.apache.fineract.avro.loan.v1.DelinquencyBucketDataV1" "org.apache.fineract.avro.loan.v1.DelinquencyBucketDataV1"
] ]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -146,6 +146,14 @@
"null", "null",
"boolean" "boolean"
] ]
},
{
"default": null,
"name": "disallowInterestCalculationOnPastDue",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -437,7 +437,7 @@
}, },
{ {
"default": null, "default": null,
"name": "totalUnpaidAccruedDueInterest", "name": "totalUnpaidPayableDueInterest",
"type": [ "type": [
"null", "null",
"bigdecimal" "bigdecimal"
@@ -445,7 +445,7 @@
}, },
{ {
"default": null, "default": null,
"name": "totalUnpaidAccruedNotDueInterest", "name": "totalUnpaidPayableNotDueInterest",
"type": [ "type": [
"null", "null",
"bigdecimal" "bigdecimal"
@@ -244,6 +244,25 @@
"null", "null",
"string" "string"
] ]
},
{
"default": null,
"name": "customData",
"type": [
"null",
{
"values": "bytes",
"type": "map"
}
]
},
{
"default": null,
"name": "reversed",
"type": [
"null",
"boolean"
]
} }
] ]
} }
@@ -1,6 +1,6 @@
{ {
"logicalType": "decimal", "logicalType": "decimal",
"precision": 20, "precision": 27,
"scale": 8, "scale": 8,
"type": "bytes" "type": "bytes"
} }
@@ -29,7 +29,6 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collection; import java.util.Collection;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.teller.data.CashierData; import org.apache.fineract.organisation.teller.data.CashierData;
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformService; import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformService;
@@ -41,20 +40,15 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class CashierApiResource { public class CashierApiResource {
private final DefaultToApiJsonSerializer<CashierData> jsonSerializer;
private final TellerManagementReadPlatformService readPlatformService; private final TellerManagementReadPlatformService readPlatformService;
@GET @GET
@Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public String getCashierData(@QueryParam("officeId") final Long officeId, @QueryParam("tellerId") final Long tellerId, public Collection<CashierData> getCashierData(@QueryParam("officeId") final Long officeId, @QueryParam("tellerId") final Long tellerId,
@QueryParam("staffId") final Long staffId, @QueryParam("date") final String date) { @QueryParam("staffId") final Long staffId, @QueryParam("date") final String date) {
final DateTimeFormatter dateFormatter = DateTimeFormatter.BASIC_ISO_DATE; final LocalDate dateRestriction = date != null ? LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE)
: DateUtils.getBusinessLocalDate();
final LocalDate dateRestriction = (date != null ? LocalDate.parse(date, dateFormatter) : DateUtils.getBusinessLocalDate()); return readPlatformService.getCashierData(officeId, tellerId, staffId, dateRestriction);
final Collection<CashierData> allCashiers = this.readPlatformService.getCashierData(officeId, tellerId, staffId, dateRestriction);
return this.jsonSerializer.serialize(allCashiers);
} }
} }

Some files were not shown because too many files have changed in this diff Show More